Merge branch 'develop' into travis/dispatcher-types

This commit is contained in:
Travis Ralston 2020-05-15 09:56:02 -06:00
commit 798e7a1f86
38 changed files with 517 additions and 191 deletions

View file

@ -87,7 +87,6 @@
"project-name-generator": "^2.1.7",
"prop-types": "^15.5.8",
"qrcode": "^1.4.4",
"qrcode-react": "^0.1.16",
"qs": "^6.6.0",
"react": "^16.9.0",
"react-beautiful-dnd": "^4.0.1",
@ -120,6 +119,7 @@
"@types/classnames": "^2.2.10",
"@types/flux": "^3.1.9",
"@types/modernizr": "^3.5.3",
"@types/qrcode": "^1.3.4",
"@types/react": "16.9",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",

View file

@ -108,6 +108,7 @@
@import "./views/elements/_ManageIntegsButton.scss";
@import "./views/elements/_PowerSelector.scss";
@import "./views/elements/_ProgressBar.scss";
@import "./views/elements/_QRCode.scss";
@import "./views/elements/_ReplyThread.scss";
@import "./views/elements/_ResizeHandle.scss";
@import "./views/elements/_RichText.scss";

View file

@ -64,9 +64,6 @@ limitations under the License.
.mx_ShareDialog_qrcode_container {
float: left;
background-color: #ffffff;
padding: 5px; // makes qr code more readable in dark theme
border-radius: 5px;
height: 256px;
width: 256px;
margin-right: 64px;

View file

@ -37,7 +37,7 @@ limitations under the License.
order: 2;
/* min-width hack needed for FF */
min-width: 0px;
max-height: 90%;
height: 90%;
flex: 15 15 0;
display: flex;
align-items: center;

View file

@ -0,0 +1,21 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_QRCode {
img {
border-radius: 8px;
}
}

View file

@ -167,13 +167,9 @@ export default class BasePlatform {
setLanguage(preferredLangs: string[]) {}
getSSOCallbackUrl(hsUrl: string, isUrl: string): URL {
getSSOCallbackUrl(hsUrl: string, isUrl: string, fragmentAfterLogin: string): URL {
const url = new URL(window.location.href);
// XXX: at this point, the fragment will always be #/login, which is no
// use to anyone. Ideally, we would get the intended fragment from
// MatrixChat.screenAfterLogin so that you could follow #/room links etc
// through an SSO login.
url.hash = "";
url.hash = fragmentAfterLogin || "";
url.searchParams.set("homeserver", hsUrl);
url.searchParams.set("identityServer", isUrl);
return url;
@ -183,9 +179,11 @@ export default class BasePlatform {
* Begin Single Sign On flows.
* @param {MatrixClient} mxClient the matrix client using which we should start the flow
* @param {"sso"|"cas"} loginType the type of SSO it is, CAS/SSO.
* @param {string} fragmentAfterLogin the hash to pass to the app during sso callback.
*/
startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") {
const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl());
startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) {
const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl(),
fragmentAfterLogin);
window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO
}

View file

@ -225,14 +225,6 @@ export default class DeviceListener {
});
}
}
} else if (await cli.secretStorageKeyNeedsUpgrade()) {
ToastStore.sharedInstance().addOrReplaceToast({
key: THIS_DEVICE_TOAST_KEY,
title: _t("Encryption upgrade available"),
icon: "verification_warning",
props: {kind: 'upgrade_ssss'},
component: sdk.getComponent("toasts.SetupEncryptionToast"),
});
} else {
// cross-signing is ready, and we don't need to upgrade encryption
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);

View file

@ -34,6 +34,7 @@ export enum Categories {
CALLS = "Calls",
COMPOSER = "Composer",
ROOM_LIST = "Room List",
ROOM = "Room",
AUTOCOMPLETE = "Autocomplete",
}
@ -142,6 +143,34 @@ const shortcuts: Record<Categories, IShortcut[]> = {
},
],
[Categories.ROOM]: [
{
keybinds: [{
key: Key.PAGE_UP,
}, {
key: Key.PAGE_DOWN,
}],
description: _td("Scroll up/down in the timeline"),
}, {
keybinds: [{
key: Key.ESCAPE,
}],
description: _td("Dismiss read marker and jump to bottom"),
}, {
keybinds: [{
modifiers: [Modifiers.SHIFT],
key: Key.PAGE_UP,
}],
description: _td("Jump to oldest unread message"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL, Modifiers.SHIFT],
key: Key.U,
}],
description: _td("Upload a file"),
}
],
[Categories.ROOM_LIST]: [
{
keybinds: [{
@ -181,13 +210,6 @@ const shortcuts: Record<Categories, IShortcut[]> = {
[Categories.NAVIGATION]: [
{
keybinds: [{
key: Key.PAGE_UP,
}, {
key: Key.PAGE_DOWN,
}],
description: _td("Scroll up/down in the timeline"),
}, {
keybinds: [{
modifiers: [Modifiers.ALT, Modifiers.SHIFT],
key: Key.ARROW_UP,
@ -257,10 +279,11 @@ const shortcuts: Record<Categories, IShortcut[]> = {
const categoryOrder = [
Categories.COMPOSER,
Categories.CALLS,
Categories.ROOM_LIST,
Categories.AUTOCOMPLETE,
Categories.ROOM,
Categories.ROOM_LIST,
Categories.NAVIGATION,
Categories.CALLS,
];
interface IModal {

View file

@ -1971,6 +1971,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
render() {
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
let fragmentAfterLogin = "";
if (this.props.initialScreenAfterLogin) {
fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`;
}
let view;
if (this.state.view === Views.LOADING) {
@ -2050,7 +2055,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
} else if (this.state.view === Views.WELCOME) {
const Welcome = sdk.getComponent('auth.Welcome');
view = <Welcome {...this.getServerProperties()} />;
view = <Welcome {...this.getServerProperties()} fragmentAfterLogin={fragmentAfterLogin} />;
} else if (this.state.view === Views.REGISTER) {
const Registration = sdk.getComponent('structures.auth.Registration');
view = (
@ -2089,6 +2094,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
onForgotPasswordClick={this.onForgotPasswordClick}
onServerConfigChange={this.onServerConfigChange}
fragmentAfterLogin={fragmentAfterLogin}
{...this.getServerProperties()}
/>
);
@ -2098,6 +2104,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<SoftLogout
realQueryParams={this.props.realQueryParams}
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
fragmentAfterLogin={fragmentAfterLogin}
/>
);
} else {

View file

@ -41,7 +41,7 @@ import * as ObjectUtils from '../../ObjectUtils';
import * as Rooms from '../../Rooms';
import eventSearch from '../../Searching';
import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard';
import {isOnlyCtrlOrCmdIgnoreShiftKeyEvent, isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard';
import MainSplit from './MainSplit';
import RightPanel from './RightPanel';
@ -588,6 +588,18 @@ export default createReactClass({
handled = true;
}
break;
case Key.PAGE_UP:
if (!ev.altKey && !ev.ctrlKey && ev.shiftKey && !ev.metaKey) {
this.jumpToReadMarker();
handled = true;
}
break;
case Key.U.toUpperCase():
if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) {
dis.dispatch({ action: "upload_file" })
handled = true;
}
break;
}
if (handled) {

View file

@ -355,7 +355,8 @@ export default createReactClass({
ev.preventDefault();
ev.stopPropagation();
const ssoKind = step === 'm.login.sso' ? 'sso' : 'cas';
PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind);
PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind,
this.props.fragmentAfterLogin);
} else {
// Don't intercept - just go through to the register page
this.onRegisterClick(ev);
@ -628,7 +629,9 @@ export default createReactClass({
<SSOButton
className="mx_Login_sso_link mx_Login_submit"
matrixClient={this._loginLogic.createTemporaryClient()}
loginType={loginType} />
loginType={loginType}
fragmentAfterLogin={this.props.fragmentAfterLogin}
/>
</div>
);
},

View file

@ -243,10 +243,15 @@ export default createReactClass({
});
};
try {
await this._makeRegisterRequest({});
// This should never succeed since we specified an empty
// auth object.
console.log("Expecting 401 from register request but got success!");
// We do the first registration request ourselves to discover whether we need to
// do SSO instead. If we've already started the UI Auth process though, we don't
// need to.
if (!this.state.doingUIAuth) {
await this._makeRegisterRequest({});
// This should never succeed since we specified an empty
// auth object.
console.log("Expecting 401 from register request but got success!");
}
} catch (e) {
if (e.httpStatus === 401) {
this.setState({

View file

@ -244,7 +244,9 @@ export default class SoftLogout extends React.Component {
<p>{introText}</p>
<SSOButton
matrixClient={MatrixClientPeg.get()}
loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"} />
loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"}
fragmentAfterLogin={this.props.fragmentAfterLogin}
/>
</div>
);
}

View file

@ -412,14 +412,14 @@ export const EmailIdentityAuthEntry = createReactClass({
this.props.onPhaseChange(DEFAULT_PHASE);
},
getInitialState: function() {
return {
requestingToken: false,
};
},
render: function() {
if (this.state.requestingToken) {
// This component is now only displayed once the token has been requested,
// so we know the email has been sent. It can also get loaded after the user
// has clicked the validation link if the server takes a while to propagate
// the validation internally. If we're in the session spawned from clicking
// the validation link, we won't know the email address, so if we don't have it,
// assume that the link has been clicked and the server will realise when we poll.
if (this.props.inputs.emailAddress === undefined) {
const Loader = sdk.getComponent("elements.Spinner");
return <Loader />;
} else {

View file

@ -45,7 +45,8 @@ export default class Welcome extends React.PureComponent {
idBaseUrl: isUrl,
});
const plaf = PlatformPeg.get();
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl());
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl(),
this.props.fragmentAfterLogin);
return (
<AuthPage>

View file

@ -34,6 +34,7 @@ export default class DeactivateAccountDialog extends React.Component {
shouldErase: false,
errStr: null,
authData: null, // for UIA
authEnabled: true, // see usages for information
// A few strings that are passed to InteractiveAuth for design or are displayed
// next to the InteractiveAuth component.
@ -42,21 +43,7 @@ export default class DeactivateAccountDialog extends React.Component {
continueKind: null,
};
MatrixClientPeg.get().deactivateAccount(null, false).then(r => {
// If we got here, oops. The server didn't require any auth.
// Our application lifecycle will catch the error and do the logout bits.
// We'll try to log something in an vain attempt to record what happened (storage
// is also obliterated on logout).
console.warn("User's account got deactivated without confirmation: Server had no auth");
this.setState({errStr: _t("Server did not require any authentication")});
}).catch(e => {
if (e && e.httpStatus === 401 && e.data) {
// Valid UIA response
this.setState({authData: e.data});
} else {
this.setState({errStr: _t("Server did not return valid authentication information.")});
}
});
this._initAuth(/* shouldErase= */false);
}
_onStagePhaseChange = (stage, phase) => {
@ -124,13 +111,40 @@ export default class DeactivateAccountDialog extends React.Component {
_onEraseFieldChange = (ev) => {
this.setState({
shouldErase: ev.target.checked,
// Disable the auth form because we're going to have to reinitialize the auth
// information. We do this because we can't modify the parameters in the UIA
// session, and the user will have selected something which changes the request.
// Therefore, we throw away the last auth session and try a new one.
authEnabled: false,
});
// As mentioned above, set up for auth again to get updated UIA session info
this._initAuth(/* shouldErase= */ev.target.checked);
};
_onCancel() {
this.props.onFinished(false);
}
_initAuth(shouldErase) {
MatrixClientPeg.get().deactivateAccount(null, shouldErase).then(r => {
// If we got here, oops. The server didn't require any auth.
// Our application lifecycle will catch the error and do the logout bits.
// We'll try to log something in an vain attempt to record what happened (storage
// is also obliterated on logout).
console.warn("User's account got deactivated without confirmation: Server had no auth");
this.setState({errStr: _t("Server did not require any authentication")});
}).catch(e => {
if (e && e.httpStatus === 401 && e.data) {
// Valid UIA response
this.setState({authData: e.data, authEnabled: true});
} else {
this.setState({errStr: _t("Server did not return valid authentication information.")});
}
});
}
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
@ -142,7 +156,7 @@ export default class DeactivateAccountDialog extends React.Component {
}
let auth = <div>{_t("Loading...")}</div>;
if (this.state.authData) {
if (this.state.authData && this.state.authEnabled) {
auth = (
<div>
{this.state.bodyText}

View file

@ -24,7 +24,7 @@ import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import QRCode from 'qrcode-react';
import QRCode from "../elements/QRCode";
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
import * as ContextMenu from "../../structures/ContextMenu";
import {toRightOf} from "../../structures/ContextMenu";
@ -221,7 +221,7 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
<div className="mx_ShareDialog_split">
<div className="mx_ShareDialog_qrcode_container">
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} />
<QRCode data={matrixToUrl} width={256} />
</div>
<div className="mx_ShareDialog_social_container">
{ socials.map((social) => (

View file

@ -0,0 +1,51 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from "react";
import {toDataURL, QRCodeSegment, QRCodeToDataURLOptions} from "qrcode";
import classNames from "classnames";
import {_t} from "../../../languageHandler";
import Spinner from "./Spinner";
interface IProps extends QRCodeToDataURLOptions {
data: string | QRCodeSegment[];
className?: string;
}
const defaultOptions: QRCodeToDataURLOptions = {
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
};
const QRCode: React.FC<IProps> = ({data, className, ...options}) => {
const [dataUri, setUri] = React.useState<string>(null);
React.useEffect(() => {
let cancelled = false;
toDataURL(data, {...defaultOptions, ...options}).then(uri => {
if (cancelled) return;
setUri(uri);
});
return () => {
cancelled = true;
};
}, [JSON.stringify(data), options]);
return <div className={classNames("mx_QRCode", className)}>
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }
</div>;
};
export default QRCode;

View file

@ -21,9 +21,9 @@ import PlatformPeg from "../../../PlatformPeg";
import AccessibleButton from "./AccessibleButton";
import {_t} from "../../../languageHandler";
const SSOButton = ({matrixClient, loginType, ...props}) => {
const SSOButton = ({matrixClient, loginType, fragmentAfterLogin, ...props}) => {
const onClick = () => {
PlatformPeg.get().startSingleSignOn(matrixClient, loginType);
PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin);
};
return (
@ -36,6 +36,7 @@ const SSOButton = ({matrixClient, loginType, ...props}) => {
SSOButton.propTypes = {
matrixClient: PropTypes.object.isRequired, // does not use context as may use a temporary client
loginType: PropTypes.oneOf(["sso", "cas"]), // defaults to "sso" in base-apis
fragmentAfterLogin: PropTypes.string,
};
export default SSOButton;

View file

@ -17,8 +17,7 @@ limitations under the License.
import React from "react";
import PropTypes from "prop-types";
import {replaceableComponent} from "../../../../utils/replaceableComponent";
import Spinner from "../Spinner";
import * as QRCode from "qrcode";
import QRCode from "../QRCode";
@replaceableComponent("views.elements.crypto.VerificationQRCode")
export default class VerificationQRCode extends React.PureComponent {
@ -26,33 +25,12 @@ export default class VerificationQRCode extends React.PureComponent {
qrCodeData: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
dataUri: null,
};
this.generateQrCode();
}
componentDidUpdate(prevProps): void {
if (JSON.stringify(this.props) === JSON.stringify(prevProps)) return; // No prop change
this.generateQRCode();
}
async generateQrCode() {
// Now actually assemble the QR code's data URI
const uri = await QRCode.toDataURL([{data: this.props.qrCodeData.buffer, mode: 'byte'}], {
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
});
this.setState({dataUri: uri});
}
render() {
if (!this.state.dataUri) {
return <div className='mx_VerificationQRCode'><Spinner /></div>;
}
return <img src={this.state.dataUri} className='mx_VerificationQRCode' />;
return (
<QRCode
data={[{data: this.props.qrCodeData.buffer, mode: 'byte'}]}
className="mx_VerificationQRCode"
width={196} />
);
}
}

View file

@ -114,8 +114,19 @@ class UploadButton extends React.Component {
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
this._uploadInput = createRef();
this._dispatcherRef = dis.register(this.onAction);
}
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
}
onAction = payload => {
if (payload.action === "upload_file") {
this.onUploadClick();
}
};
onUploadClick(ev) {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({action: 'require_registration'});
@ -128,7 +139,7 @@ class UploadButton extends React.Component {
if (ev.target.files.length === 0) return;
// take a copy so we can safely reset the value of the form control
// (Note it is a FileList: we can't use slice or sesnible iteration).
// (Note it is a FileList: we can't use slice or sensible iteration).
const tfiles = [];
for (let i = 0; i < ev.target.files.length; ++i) {
tfiles.push(ev.target.files[i]);

View file

@ -36,7 +36,6 @@ export default class CrossSigningPanel extends React.PureComponent {
userSigningPrivateKeyCached: false,
sessionBackupKeyCached: false,
secretStorageKeyInAccount: false,
secretStorageKeyNeedsUpgrade: null,
};
}
@ -88,7 +87,6 @@ export default class CrossSigningPanel extends React.PureComponent {
const homeserverSupportsCrossSigning =
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
const crossSigningReady = await cli.isCrossSigningReady();
const secretStorageKeyNeedsUpgrade = await cli.secretStorageKeyNeedsUpgrade();
this.setState({
crossSigningPublicKeysOnDevice,
@ -100,7 +98,6 @@ export default class CrossSigningPanel extends React.PureComponent {
secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
crossSigningReady,
secretStorageKeyNeedsUpgrade,
});
}
@ -150,7 +147,6 @@ export default class CrossSigningPanel extends React.PureComponent {
secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
crossSigningReady,
secretStorageKeyNeedsUpgrade,
} = this.state;
let errorSection;
@ -259,10 +255,6 @@ export default class CrossSigningPanel extends React.PureComponent {
<td>{_t("Homeserver feature support:")}</td>
<td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td>
</tr>
<tr>
<td>{_t("Secret Storage key format:")}</td>
<td>{secretStorageKeyNeedsUpgrade ? _t("outdated") : _t("up to date")}</td>
</tr>
</tbody></table>
</details>
{errorSection}

View file

@ -348,7 +348,7 @@ export default class GeneralUserSettingsTab extends React.Component {
// For newer homeservers with separate 3PID add and bind methods (MSC2290),
// there is no such concern, so we can always show the HS account 3PIDs.
if (this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true) {
const emails = this.state.loading3pids || true
const emails = this.state.loading3pids
? <Spinner />
: <EmailAddresses
emails={this.state.emails}

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import Modal from '../../../Modal';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
import DeviceListener from '../../../DeviceListener';
@ -31,7 +30,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
'set_up_encryption',
'verify_this_session',
'upgrade_encryption',
'upgrade_ssss',
]).isRequired,
};
@ -39,24 +37,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
DeviceListener.sharedInstance().dismissEncryptionSetup();
};
async _waitForCompletion() {
if (this.props.kind === 'upgrade_ssss') {
return new Promise(resolve => {
const recheck = async () => {
const needsUpgrade = await MatrixClientPeg.get().secretStorageKeyNeedsUpgrade();
if (!needsUpgrade) {
MatrixClientPeg.get().removeListener('accountData', recheck);
resolve();
}
};
MatrixClientPeg.get().on('accountData', recheck);
recheck();
});
} else {
return;
}
}
_onSetupClick = async () => {
if (this.props.kind === "verify_this_session") {
Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog,
@ -68,7 +48,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
);
try {
await accessSecretStorage();
await this._waitForCompletion();
} finally {
modal.close();
}
@ -82,8 +61,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
return _t('Verify yourself & others to keep your chats safe');
case 'verify_this_session':
return _t('Other users may not trust it');
case 'upgrade_ssss':
return _t('Update your secure storage');
}
}
@ -92,7 +69,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
case 'set_up_encryption':
return _t('Set up');
case 'upgrade_encryption':
case 'upgrade_ssss':
return _t('Upgrade');
case 'verify_this_session':
return _t('Verify');

View file

@ -2237,5 +2237,62 @@
"Waiting for you to accept on your other session…": "Čekáme na vaše přijetí v druhé relaci…",
"Almost there! Is your other session showing the same shield?": "Téměř hotovo! Je vaše druhá relace také ověřená?",
"Almost there! Is %(displayName)s showing the same shield?": "Téměř hotovo! Je relace %(displayName)s také ověřená?",
"You've successfully verified %(deviceName)s (%(deviceId)s)!": "Ověřili jste %(deviceName)s (%(deviceId)s)!"
"You've successfully verified %(deviceName)s (%(deviceId)s)!": "Ověřili jste %(deviceName)s (%(deviceId)s)!",
"If you cancel now, you won't complete your operation.": "Pokud teď akci stornujete, nebudete jí moci dokončit.",
"Review where youre logged in": "Zobrazit kde jste přihlášení",
"New login. Was this you?": "Nové přihlášní. Jste to vy?",
"%(name)s is requesting verification": "%(name)s žádá o ověření",
"Failed to set topic": "Nepovedlo se nastavit téma",
"Command failed": "Příkaz selhal",
"Could not find user in room": "Nepovedlo se najít uživatele v místnosti",
"Please supply a widget URL or embed code": "Zadejte prosím URL widgetu nebo jeho kód",
"Send a bug report with logs": "Zaslat hlášení o chybě",
"You signed in to a new session without verifying it:": "Přihlásili jste se do nové relace, ale neoveřili jste ji:",
"Verify your other session using one of the options below.": "Ověřte ostatní relací jedním z následujících způsobů.",
"Enable cross-signing to verify per-user instead of per-session": "Povolit cross-signing - ověřování uživatelů místo oveřování jednotlivých relací",
"Keep recovery passphrase in memory for this session": "V této relaci si heslo pro obnovení zálohy zapamatovat",
"Click the button below to confirm deleting these sessions.|other": "Zmáčknutím tlačítka potvrdíte smazání těchto relací.",
"Click the button below to confirm deleting these sessions.|one": "Zmáčknutím tlačítka potvrdíte smazání této relace.",
"Delete sessions|other": "Smazat relace",
"Delete sessions|one": "Smazat relaci",
"Where youre logged in": "Kde jste přihlášení",
"You've successfully verified your device!": "Úspěšně jste ověřili vaše zařízení!",
"Start verification again from the notification.": "Začít proces ověření znovu pomocí notifikace.",
"Start verification again from their profile.": "Začít proces ověření znovu z jejich profilu.",
"Verification timed out.": "Ověření vypršelo.",
"You cancelled verification on your other session.": "Na druhé relace jste proces ověření zrušili.",
"%(displayName)s cancelled verification.": "%(displayName)s zrušil/a proces ověření.",
"You cancelled verification.": "Zrušili jste proces ověření.",
"Message deleted": "Zpráva smazána",
"Message deleted by %(name)s": "Zpráva smazána uživatelem %(name)s",
"Can't load this message": "Tuto zprávu nelze načíst",
"Submit logs": "Odeslat záznamy o chybě",
"Looks good": "To vypadá dobře",
"Can't find this server or its room list": "Server nebo jeho seznam místností se nepovedlo nalézt",
"All rooms": "Všechny místnosti",
"Your server": "Váš server",
"Are you sure you want to remove <b>%(serverName)s</b>": "Opravdu chcete odstranit <b>%(serverName)s</b>",
"Remove server": "Odstranit server",
"Matrix": "Matrix",
"Add a new server": "Přidat nový server",
"Enter the name of a new server you want to explore.": "Zadejte jméno serveru, který si chcete prohlédnout.",
"Server name": "Jméno serveru",
"Add a new server...": "Přidat nový server...",
"%(networkName)s rooms": "místnosti v %(networkName)s",
"Matrix rooms": "místnosti na Matrixu",
"Reminder: Your browser is unsupported, so your experience may be unpredictable.": "Připomínka: Váš prohlížeč není oficiálně podporován, tak se může Riot chovat nepředvídatelně.",
"Enable end-to-end encryption": "Povolit E2E šifrování",
"You cant disable this later. Bridges & most bots wont work yet.": "Už to v budoucnu nepůjde vypnout. Většina botů a propojení zatím nefunguje.",
"Server did not require any authentication": "Server nevyžadoval žádné ověření",
"Server did not return valid authentication information.": "Server neposkytl platné informace o ověření.",
"Confirm your account deactivation by using Single Sign On to prove your identity.": "Potvrďte deaktivaci účtu použtím Jednotného přihlášení.",
"Are you sure you want to deactivate your account? This is irreversible.": "Opravdu chcete deaktivovat účet? Je to nevratné.",
"Confirm account deactivation": "Potvrďte deaktivaci účtu",
"There was a problem communicating with the server. Please try again.": "Došlo k potížím při komunikaci se serverem. Zkuste to prosím znovu.",
"Start a conversation with someone using their name, username (like <userId/>) or email address.": "Začněte s někým konverzovat za pomocí jména, přihlašovacího jména (jako <userId/>) nebo emailu.",
"Opens chat with the given user": "Otevře konverzaci s tímto uživatelem",
"Sends a message to the given user": "Pošle zprávu danému uživateli",
"Waiting for your other session to verify…": "Čekáme na ověření od vaší druhé relace…",
"Verify all your sessions to ensure your account & messages are safe": "Ověřte všechny své relace, abyste zaručili, že jsou vaše zprávy a účet bezpečné",
"Verify the new login accessing your account: %(name)s": "Ověřte nové přihlášení na váš účet: %(name)s"
}

View file

@ -569,7 +569,6 @@
"Review": "Review",
"Verify yourself & others to keep your chats safe": "Verify yourself & others to keep your chats safe",
"Other users may not trust it": "Other users may not trust it",
"Update your secure storage": "Update your secure storage",
"Set up": "Set up",
"Upgrade": "Upgrade",
"Verify": "Verify",
@ -621,9 +620,6 @@
"in account data": "in account data",
"Homeserver feature support:": "Homeserver feature support:",
"exists": "exists",
"Secret Storage key format:": "Secret Storage key format:",
"outdated": "outdated",
"up to date": "up to date",
"Your homeserver does not support session management.": "Your homeserver does not support session management.",
"Unable to load session list": "Unable to load session list",
"Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Confirm deleting these sessions by using Single Sign On to prove your identity.",
@ -1498,6 +1494,7 @@
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes",
"Power level": "Power level",
"Custom level": "Custom level",
"QR Code": "QR Code",
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.",
"<a>In reply to</a> <pill>": "<a>In reply to</a> <pill>",
"Room alias": "Room alias",
@ -1589,13 +1586,13 @@
"You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ",
"Incompatible Database": "Incompatible Database",
"Continue With Encryption Disabled": "Continue With Encryption Disabled",
"Server did not require any authentication": "Server did not require any authentication",
"Server did not return valid authentication information.": "Server did not return valid authentication information.",
"Confirm your account deactivation by using Single Sign On to prove your identity.": "Confirm your account deactivation by using Single Sign On to prove your identity.",
"Are you sure you want to deactivate your account? This is irreversible.": "Are you sure you want to deactivate your account? This is irreversible.",
"Confirm account deactivation": "Confirm account deactivation",
"To continue, please enter your password:": "To continue, please enter your password:",
"There was a problem communicating with the server. Please try again.": "There was a problem communicating with the server. Please try again.",
"Server did not require any authentication": "Server did not require any authentication",
"Server did not return valid authentication information.": "Server did not return valid authentication information.",
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>",
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.",
"Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.": "Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.",
@ -2289,13 +2286,16 @@
"Cancel replying to a message": "Cancel replying to a message",
"Toggle microphone mute": "Toggle microphone mute",
"Toggle video on/off": "Toggle video on/off",
"Scroll up/down in the timeline": "Scroll up/down in the timeline",
"Dismiss read marker and jump to bottom": "Dismiss read marker and jump to bottom",
"Jump to oldest unread message": "Jump to oldest unread message",
"Upload a file": "Upload a file",
"Jump to room search": "Jump to room search",
"Navigate up/down in the room list": "Navigate up/down in the room list",
"Select room from the room list": "Select room from the room list",
"Collapse room list section": "Collapse room list section",
"Expand room list section": "Expand room list section",
"Clear room list filter field": "Clear room list filter field",
"Scroll up/down in the timeline": "Scroll up/down in the timeline",
"Previous/next unread room or DM": "Previous/next unread room or DM",
"Previous/next room or DM": "Previous/next room or DM",
"Toggle the top left menu": "Toggle the top left menu",

View file

@ -70,7 +70,7 @@
"Unable to create widget.": "Ne povas krei fenestraĵon.",
"Failed to send request.": "Malsukcesis sendi peton.",
"This room is not recognised.": "Ĉi tiu ĉambro ne estas rekonita.",
"Power level must be positive integer.": "Nivelo de potenco devas esti entjero pozitiva.",
"Power level must be positive integer.": "Povnivelo devas esti entjero pozitiva.",
"You are not in this room.": "Vi ne estas en tiu ĉi ĉambro.",
"You do not have permission to do that in this room.": "Vi ne havas permeson fari tion en tiu ĉambro.",
"Missing room_id in request": "En peto mankas room_id",
@ -122,7 +122,7 @@
"%(senderName)s made future room history visible to anyone.": "%(senderName)s videbligis estontan historion de la ĉambro al ĉiuj.",
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s videbligis estontan historion de la ĉambro al nekonata (%(visibility)s).",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s al %(toPowerLevel)s",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ŝanĝis la potencan nivelon de %(powerLevelDiffText)s.",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ŝanĝis la povnivelon de %(powerLevelDiffText)s.",
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s ŝanĝis la fiksitajn mesaĝojn de la ĉambro.",
"%(widgetName)s widget modified by %(senderName)s": "Fenestraĵon %(widgetName)s ŝanĝis %(senderName)s",
"%(widgetName)s widget added by %(senderName)s": "Fenestraĵon %(widgetName)s aldonis %(senderName)s",
@ -212,8 +212,8 @@
"Failed to ban user": "Malsukcesis forbari uzanton",
"Failed to mute user": "Malsukcesis silentigi uzanton",
"Failed to toggle moderator status": "Malsukcesis baskuligi estrecon",
"Failed to change power level": "Malsukcesis ŝanĝi nivelon de potenco",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Tiun ĉi ŝanĝon vi ne povos fareblos, ĉar vi donas al la uzanto la saman nivelon de potenco, kiun havas vi mem.",
"Failed to change power level": "Malsukcesis ŝanĝi povnivelon",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Tiun ĉi ŝanĝon vi ne povos malfari, ĉar vi donas al la uzanto la saman povnivelon, kiun havas vi mem.",
"Are you sure?": "Ĉu vi certas?",
"Unignore": "Reatenti",
"Ignore": "Malatenti",
@ -233,7 +233,7 @@
"and %(count)s others...|one": "kaj unu alia…",
"Invited": "Invititaj",
"Filter room members": "Filtri ĉambranojn",
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (potenco je %(powerLevelNumber)s)",
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (povnivelo je %(powerLevelNumber)s)",
"Attachment": "Aldonaĵo",
"Hangup": "Fini vokon",
"Voice call": "Voĉvoko",
@ -275,7 +275,7 @@
"Search": "Serĉi",
"Community Invites": "Komunumaj invitoj",
"Invites": "Invitoj",
"Favourites": "Ŝatataj",
"Favourites": "Elstarigitaj",
"Rooms": "Ĉambroj",
"Low priority": "Malpli gravaj",
"Historical": "Estintaj",
@ -291,7 +291,7 @@
"Banned users": "Forbaritaj uzantoj",
"This room is not accessible by remote Matrix servers": "Ĉi tiu ĉambro ne atingeblas por foraj serviloj de Matrix",
"Leave room": "Eliri ĉambron",
"Favourite": "Ŝatata",
"Favourite": "Elstarigi",
"Guests cannot join this room even if explicitly invited.": "Gastoj ne povas aliĝi ĉi tiun ĉambron eĉ kun malimplica invito.",
"Click here to fix": "Klaku ĉi tie por riparo",
"Who can access this room?": "Kiu povas aliri ĉi tiun ĉambron?",
@ -489,7 +489,7 @@
"Skip": "Preterpasi",
"An error occurred: %(error_string)s": "Okazis eraro: %(error_string)s",
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "Tio ĉi estos la nomo de via konto sur la hejmservilo <span></span>, aŭ vi povas elekti <a>alian servilon</a>.",
"Blacklist": "Malpermesi legadon de ĉifritaj mesaĝoj",
"Blacklist": "Malpermesi malĉifradon",
"Unverify": "Malkontroli",
"If you already have a Matrix account you can <a>log in</a> instead.": "Se vi jam havas Matrix-konton, vi povas <a>saluti</a> anstataŭe.",
"Private Chat": "Privata babilo",
@ -509,7 +509,7 @@
"Failed to remove the room from the summary of %(groupId)s": "Malsukcesis forigi la ĉambron de la superrigardo de %(groupId)s",
"The room '%(roomName)s' could not be removed from the summary.": "Ĉambro ;%(roomName)s' ne forigeblas de la superrigardo.",
"Add users to the community summary": "Aldoni uzantojn al la komunuma superrigardo",
"Who would you like to add to this summary?": "Kiun vi ŝatus aldoni al tiu ĉi superrigardo?",
"Who would you like to add to this summary?": "Kiun vi volas aldoni al tiu ĉi superrigardo?",
"Failed to add the following users to the summary of %(groupId)s:": "Malsukcesis aldoni la jenajn uzantojn al la superrigardo de %(groupId)s:",
"Add a User": "Aldoni uzanton",
"Failed to remove a user from the summary of %(groupId)s": "Malsukcesis forigi uzanton de la superrigardo de %(groupId)s",
@ -550,7 +550,7 @@
"Connectivity to the server has been lost.": "Konekto al la servilo perdiĝis.",
"Sent messages will be stored until your connection has returned.": "Senditaj mesaĝoj konserviĝos ĝis via konekto refunkcios.",
"Active call": "Aktiva voko",
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Neniu alia ĉeestas! Ĉu vi ŝatus <inviteText>inviti aliajn</inviteText> aŭ <nowarnText>ĉesigi avertadon pri la malplena ĉambro</nowarnText>?",
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Neniu alia ĉeestas! Ĉu vi volas <inviteText>inviti aliajn</inviteText> aŭ <nowarnText>ĉesigi avertadon pri la malplena ĉambro</nowarnText>?",
"You seem to be uploading files, are you sure you want to quit?": "Ŝajne vi alŝutas dosierojn nun; ĉu vi tamen volas foriri?",
"You seem to be in a call, are you sure you want to quit?": "Ŝajne vi vokas nun; ĉu vi tamen volas foriri?",
"Search failed": "Serĉo malsukcesis",
@ -621,7 +621,7 @@
"This server does not support authentication with a phone number.": "Ĉi tiu servilo ne subtenas aŭtentikigon per telefona numero.",
"Displays action": "Montras agon",
"Bans user with given id": "Forbaras uzanton kun la donita identigilo",
"Define the power level of a user": "Difini la potencan nivelon de uzanto",
"Define the power level of a user": "Difini la povnivelon de uzanto",
"Deops user with given id": "Senestrigas uzanton kun donita identigilo",
"Invites user with given id to current room": "Invitas uzanton per identigilo al la nuna ĉambro",
"Joins room with given alias": "Aliĝas al ĉambro per kromnomo",
@ -664,7 +664,7 @@
"File to import": "Enportota dosiero",
"Import": "Enporti",
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Forigo de fenestraĵo efektiviĝos por ĉiuj uzantoj en ĉi tiu ĉambro. Ĉu vi certe volas ĝin forigi?",
"Unblacklist": "Repermesi legadon de ĉifritaj mesaĝoj",
"Unblacklist": "Repermesi malĉifradon",
"none": "neniu",
"The version of Riot.im": "Tiu ĉi versio de Riot.im",
"Your language of choice": "Via preferata lingvo",
@ -883,7 +883,7 @@
"Language and region": "Lingvo kaj regiono",
"Theme": "Haŭto",
"General": "Ĝenerala",
"<a>In reply to</a> <pill>": "<a>Respondante al</a> <pill>",
"<a>In reply to</a> <pill>": "<a>Responde al</a> <pill>",
"Share Message": "Diskonigi",
"Whether or not you're logged in (we don't record your username)": "Ĉu vi salutis aŭ ne (ni ne registras vian uzantonomon)",
"You do not have permission to start a conference call in this room": "Vi ne havas permeson komenci grupvokon en ĉi tiu ĉambro",
@ -1477,7 +1477,7 @@
"Demote yourself?": "Ĉu malrangaltigi vin mem?",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Vi ne povos malfari tiun ŝanĝon, ĉar vi malrangaltigas vin mem; se vi estas la lasta povohava uzanto en la ĉambro, estos neeble vian povon rehavi.",
"Demote": "Malrangaltigi",
"Power level": "Povonivelo",
"Power level": "Povnivelo",
"Use two-way text verification": "Uzi duflankan tekstan kontrolon",
"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:": "Gradaltigo de ĉi tiu ĉambro bezonas fermi ĝin, kaj krei novan por anstataŭi ĝin. Por plejbonigi sperton de la ĉambranoj, ni:",
"Invalid homeserver discovery response": "Nevalida eltrova respondo de hejmservilo",
@ -1571,10 +1571,10 @@
"Always show the window menu bar": "Ĉiam montri la fenestran menubreton",
"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.",
"Error changing power level requirement": "Eraris ŝanĝo de postulo de povnivelo",
"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 povnivelo. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.",
"Error changing power level": "Eraris ŝanĝo de povnivelo",
"An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de povnivelo 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",
@ -1965,7 +1965,7 @@
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Uzu kunigilon <b>(%(serverName)s)</b> por administrado de robotoj, fenestraĵoj, kaj glumarkaroj.",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Uzu kunigilon por administrado de robotoj, fenestraĵoj, kaj glumarkaroj.",
"Manage integrations": "Administri kunigojn",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Kunigiloj ricevas agordajn datumojn, kaj povas modifi fenestraĵojn, sendi invitojn al ĉambroj, kaj vianome agordi nivelojn de potenco.",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Kunigiloj ricevas agordajn datumojn, kaj povas modifi fenestraĵojn, sendi invitojn al ĉambroj, kaj vianome agordi povnivelojn.",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Via pasvorto sukcese ŝanĝiĝis. Vi ne ricevados pasivajn sciigojn en aliaj salutaĵoj, ĝis vi ilin resalutos",
"Error downloading theme information.": "Eraris elŝuto de informoj pri haŭto.",
"Theme added!": "Haŭto aldoniĝis!",
@ -2396,5 +2396,9 @@
"Cancel replying to a message": "Nuligi respondon al mesaĝo",
"Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Invitu iun per ĝia nomo, uzantonomo (kiel <userId/>), retpoŝtadreso, aŭ <a>kunhavigu la ĉambron</a>.",
"Message deleted": "Mesaĝo foriĝis",
"Message deleted by %(name)s": "Mesaĝon forigis %(name)s"
"Message deleted by %(name)s": "Mesaĝon forigis %(name)s",
"Opens chat with the given user": "Malfermas babilon kun la uzanto",
"Sends a message to the given user": "Sendas mesaĝon al la uzanto",
"Waiting for your other session to verify…": "Atendante kontrolon de via alia salutaĵo…",
"You've successfully verified your device!": "Vi sukcese kontrolis vian aparaton!"
}

View file

@ -6,7 +6,7 @@
"Failed to verify email address: make sure you clicked the link in the email": "E-posti aadressi kontrollimine ei õnnestunud: palun vaata, et sa kindlasti klõpsisid saabunud kirjas olnud viidet",
"The platform you're on": "Sinu kasutatav arvutisüsteem",
"The version of Riot": "Riot'i versioon",
"Whether or not you're logged in (we don't record your username)": "Kas sa oled sisseloginud või mitte (me ei salvesta sinu kasutajanime)",
"Whether or not you're logged in (we don't record your username)": "Kas sa oled sisseloginud või mitte (me ei salvesta sinu kasutajanime)",
"Your language of choice": "Sinu keelevalik",
"Your homeserver's URL": "Sinu koduserveri aadress",
"e.g. <CurrentPageURL>": "näiteks <CurrentPageURL>",
@ -238,7 +238,7 @@
"Remove %(name)s from the directory?": "Eemalda %(name)s kataloogist?",
"Remove from Directory": "Eemalda kataloogist",
"remove %(name)s from the directory.": "eemalda %(name)s kataloogist.",
"You seem to be uploading files, are you sure you want to quit?": "Tundub, et sa parasjagu laed faile üles, kas sa kindlasti soovid väljuda?",
"You seem to be uploading files, are you sure you want to quit?": "Tundub, et sa parasjagu laed faile üles. Kas sa kindlasti soovid väljuda?",
"Failed to set direct chat tag": "Otsevestluse sildi lisamine ei õnnestunud",
"Failed to remove tag %(tagName)s from room": "Sildi %(tagName)s eemaldamine jututoast ebaõnnestus",
"Calls": "Kõned",
@ -543,7 +543,7 @@
"Start a conversation with someone using their name, username (like <userId/>) or email address.": "Alusta vestlust kasutades teise osapoole nime, kasutajanime (näiteks <userId/>) või e-posti aadressi.",
"Go": "Mine",
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "Sinu sõnumit ei saadetud, kuna see koduserver on saavutanud igakuise aktiivsete kasutajate piiri. Teenuse kasutamiseks palun <a>võta ühendust serveri haldajaga</a>.",
"Add room": "LIsa jututuba",
"Add room": "Lisa jututuba",
"%(senderName)s invited %(targetName)s.": "%(senderName)s kutsus vestlema kasutajat %(targetName)s.",
"%(targetName)s joined the room.": "%(targetName)s liitus jututoaga.",
"%(senderName)s answered the call.": "%(senderName)s vastas kõnele.",
@ -961,5 +961,126 @@
"Previous/next room or DM": "Eelmine/järgmine otsevestlus või jututuba",
"Toggle the top left menu": "Lülita ülemine vasak menüü sisse/välja",
"Activate selected button": "Aktiveeri valitud nupp",
"Toggle right panel": "Lülita parem paan sisse/välja"
"Toggle right panel": "Lülita parem paan sisse/välja",
"%(count)s of your messages have not been sent.|other": "Mõned sinu sõnumid on saatmata.",
"%(count)s of your messages have not been sent.|one": "Sinu sõnum on saatmata.",
"You seem to be in a call, are you sure you want to quit?": "Tundub, et sul parasjagu on kõne pooleli. Kas sa kindlasti soovid väljuda?",
"Unknown room %(roomId)s": "Tundmatu jututuba %(roomId)s",
"Room": "Jututuba",
"Failed to reject invite": "Kutse tagasilükkamine ei õnnestunud",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Üritasin laadida teatud hetke selle jututoa ajajoonelt, kuid sul ei ole õigusi selle sõnumi nägemiseks.",
"Failed to load timeline position": "Asukoha laadimine ajajoonel ei õnnestunud",
" (1/%(totalCount)s)": " (1/%(totalCount)s)",
"Guest": "Külaline",
"Uploading %(filename)s and %(count)s others|other": "Laen üles %(filename)s ning %(count)s muud faili",
"Uploading %(filename)s and %(count)s others|zero": "Laen üles %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Laen üles %(filename)s ning veel %(count)s faili",
"Verify this login": "Verifitseeri see sisselogimissessioon",
"Session verified": "Sessioon on verifitseeritud",
"Failed to send email": "E-kirja saatmine ebaõnnestus",
"The email address linked to your account must be entered.": "Sa pead sisestama oma kontoga seotud e-posti aadressi.",
"A new password must be entered.": "Palun sisesta uus salasõna.",
"New passwords must match each other.": "Uued salasõnad peavad omavahel klappima.",
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Salasõna muutmine tühistab kõik läbiva krüptimise võtmed sinu kõikides sessioonides ning seega muutub kogu sinu vestluste ajalugu loetamatuks. Palun kindlasti kas sea üles võtmete varundamine või ekspordi mõnest muust sessioonist jututubade võtmed enne senise salasõna tühistamist.",
"Your Matrix account on %(serverName)s": "Sinu Matrix'i konto serveris %(serverName)s",
"Your Matrix account on <underlinedServerName />": "Sinu Matrix'i kasutajakonto serveris <underlinedServerName />",
"This homeserver does not support login using email address.": "See koduserver ei võimalda e-posti aadressi kasutamist sisselogimisel.",
"Please <a>contact your service administrator</a> to continue using this service.": "Jätkamaks selle teenuse kasutamist palun <a>võta ühendust oma teenuse haldajaga</a>.",
"This account has been deactivated.": "See kasutajakonto on deaktiveeritud.",
"Incorrect username and/or password.": "Vigane kasutajanimi ja/või salasõna.",
"Please note you are logging into the %(hs)s server, not matrix.org.": "Sa kasutad sisselogimiseks serverit %(hs)s, mitte aga matrix.org'i.",
"Failed to perform homeserver discovery": "Koduserveri leidmine ebaõnnestus",
"The phone number entered looks invalid": "Sisestatud telefoninumber tundub vigane",
"This homeserver doesn't offer any login flows which are supported by this client.": "See koduserver ei paku ühtegi sisselogimislahendust, mida see klient toetab.",
"Error: Problem communicating with the given homeserver.": "Viga: Suhtlusel koduserveriga tekkis probleem.",
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Kui aadressiribal on HTTPS-aadress, siis HTTP-protokolli kasutades ei saa ühendust koduserveriga. Palun pruugi HTTPS-protokolli või <a>luba brauseris ebaturvaliste skriptide kasutamine</a>.",
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Ei sa ühendust koduserveriga. Palun kontrolli, et sinu <a>koduserveri SSL sertifikaat</a> oleks usaldusväärne ning mõni brauseri lisamoodul ei blokeeri päringuid.",
"Syncing...": "Sünkroniseerin...",
"Signing In...": "Login sisse...",
"Create account": "Loo kasutajakonto",
"Failed to fetch avatar URL": "Ei õnnestunud laadida profiilipildi ehk avatari aadressi",
"Upload an avatar:": "Lae üles profiilipilt ehk avatar:",
"Unable to query for supported registration methods.": "Ei õnnestunud pärida toetatud registreerimismeetodite loendit.",
"Registration has been disabled on this homeserver.": "Väline registreerimine ei ole selles koduserveris kasutusel.",
"This server does not support authentication with a phone number.": "See server ei toeta autentimist telefoninumbri alusel.",
"Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Sinu uus kasutajakonto (%(newAccountId)s) on registreeritud, kuid sa jube oled sisse loginud teise kasutajakontoga (%(loggedInUserId)s).",
"Continue with previous account": "Jätka senise konto kasutamist",
"<a>Log in</a> to your new account.": "<a>Logi sisse</a> oma uuele kasutajakontole.",
"Registration Successful": "Registreerimine õnnestus",
"Create your account": "Loo endale konto",
"Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Kinnita oma isikusamasust verifitseerides seda sisselogimissessiooni mõnest oma muust sessioonist. Sellega tagad ka ligipääsu krüptitud sõnumitele.",
"This requires the latest Riot on your other devices:": "Selleks on sul vaja muudes seadmetes kõige uuemat Riot'i versiooni:",
"You're signed out": "Sa oled loginud välja",
"Clear personal data": "Kustuta privaatsed andmed",
"Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Hoiatus: Sinu privaatsed andmed (sealhulgas krüptimisvõtmed) on jätkuvalt salvestatud selles sessioonis. Eemalda nad, kui oled lõpetanud selle sessiooni kasutamise või soovid sisse logida muu kasutajakontoga.",
"Commands": "Käsud",
"Results from DuckDuckGo": "Otsingutulemused DuckDuckGo saidist",
"DuckDuckGo Results": "DuckDuckGo otsingutulemused",
"Emoji": "Emoji",
"Notify the whole room": "Teavita kogu jututuba",
"Users": "Kasutajad",
"unknown device": "tundmatu seade",
"NOT verified": "EI OLE verifitseeritud",
"verified": "verifitseeritud",
"Verification": "Verifitseerimine",
"Ed25519 fingerprint": "Ed25519 sõrmejälg",
"User ID": "Kasutajatunnus",
"Algorithm": "Algoritm",
"unencrypted": "krüptimata",
"Terms and Conditions": "Kasutustingimused",
"Logout": "Logi välja",
"Your Communities": "Sinu kogukonnad",
"Error whilst fetching joined communities": "Viga nende kogukondade laadimisel, millega sa oled liitunud",
"You have no visible notifications": "Sul ei ole nähtavaid teavitusi",
"Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot'il ei õnnestunud koduserverist laadida toetatud protokollide loendit. Toetamaks kolmandate osapoolte võrke võib koduserver olla liiga vana.",
"Riot failed to get the public room list.": "Riot'il ei õnnestunud laadida avalike jututubade loendit.",
"The homeserver may be unavailable or overloaded.": "Koduserver pole kas saadaval või on üle koormatud.",
"Room not found": "Jututuba ei leidunud",
"Preview": "Eelvaade",
"View": "Näita",
"Message not sent due to unknown sessions being present": "Sõnum on saatmata, sest jutuoas on tundmatud sessioonid",
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "Sa ei saa saata ühtego sõnumit enne, kui oled läbi lugenud ja nõustunud <consentLink>meie kasutustingimustega</consentLink>.",
"Couldn't load page": "Lehe laadimine ei õnnestunud",
"You must <a>register</a> to use this functionality": "Selle funktsionaalsuse kasutamiseks pead sa <a>registreeruma</a>",
"<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n": "<h1>Sinu kogukonna lehe HTML'i näidis - see on pealkiri</h1>\n<p>\n Tutvustamaks uutele liikmetele kogukonda, kasuta seda pikka kirjeldust\n või jaga olulist teavet <a href=\"foo\">viidetena</a>\n</p>\n<p>\n Pildite lisaminseks võid sa isegi kasutada img-märgendit\n</p>\n",
"Add to summary": "Lisa kokkuvõtte lehele",
"Add a Room": "Lisa jututuba",
"The room '%(roomName)s' could not be removed from the summary.": "Valitud %(roomName)s jututoa eemaldamine koondinfost ei õnnestunud.",
"Add users to the community summary": "Lisa kasutajad kogukonna kokkuvõtte lehele",
"Who would you like to add to this summary?": "Keda sa sooviksid lisada siia kokkuvõtte lehele?",
"Failed to add the following users to the summary of %(groupId)s:": "Järgnevate kasutajate lisamine %(groupId)s kogukonna koondinfo lehele ei õnnestunud:",
"Add a User": "Lisa kasutaja",
"The user '%(displayName)s' could not be removed from the summary.": "Kasutaja %(displayName)s eemaldamine koondinfo lehelt ei õnnestunud.",
"Failed to upload image": "Pildi üleslaadimine ei õnnestunud",
"Failed to update community": "Kogukonna uuendamine ei õnnestunud",
"Unable to accept invite": "Ei saanud kutset vastu võtta",
"Unable to join community": "Ei õnnetunud liituda kogukonnaga",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Sa oled selle kogukonna haldur. Sa ei saa uuesti liituda ilma kutseta mõnelt teiselt administraatori õigustes liikmelt.",
"Leave Community": "Lahku kogukonnast",
"Leave %(groupName)s?": "Lahku %(groupName)s kogukonnast?",
"Unable to leave community": "Kogukonnast lahkumine ei õnnestunud",
"Community Settings": "Kogukonna seadistused",
"Want more than a community? <a>Get your own server</a>": "Soovid enamat kui kogukond? <a>Pane üles oma server</a>",
"Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Oma kogukonnas tehtud <bold1>nime</bold1> ja <bold2>avatari</bold2> muudatused ei pruugi teistele näha olla kuni 30 minuti jooksul.",
"Featured Users:": "Esiletõstetud kasutajad:",
"%(inviter)s has invited you to join this community": "%(inviter)s on kutsunud sind kogukonna liikmeks",
"Join this community": "Liitu selle kogukonnaga",
"Leave this community": "Lahku sellest kogukonnast",
"You are an administrator of this community": "Sa oled selle kogukonna haldur",
"You are a member of this community": "Sa oled kogukonna liige",
"Who can join this community?": "Kes võib liituda selle kogukonnaga?",
"Everyone": "Kes iganes soovib",
"Your community hasn't got a Long Description, a HTML page to show to community members.<br />Click here to open settings and give it one!": "Sinu kogukonnal on puudu pikk kirjeldus, mis pole muud kui lihtne HTML-leht, mida kuvatakse liikmetele.<br />Klõpsi siia ja loo selline leht!",
"Long Description (HTML)": "Pikk kirjeldus (HTML)",
"Upload avatar": "Lae üles profiilipilt ehk avatar",
"Description": "Kirjeldus",
"Community %(groupId)s not found": "%(groupId)s kogukonda ei leidunud",
"This homeserver does not support communities": "See koduserver ei toeta kogukondade funktsionaalsust",
"Failed to load %(groupId)s": "%(groupId)s kogukonna laadimine ei õnnestunud",
"Welcome to %(appName)s": "Tere tulemast %(appName)s kasutajaks",
"Liberate your communication": "Vabasta oma suhtlus",
"Send a Direct Message": "Saada otsesõnum",
"Are you sure you want to leave the room '%(roomName)s'?": "Kas oled kindel, et soovid lahkuda jututoast '%(roomName)s'?",
"Unknown error": "Teadmata viga",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleka et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutamistingimustega."
}

View file

@ -2157,5 +2157,15 @@
"Syncing...": "Synkronoidaan...",
"Signing In...": "Kirjaudutaan sisään...",
"If you've joined lots of rooms, this might take a while": "Jos olet liittynyt moniin huoneisiin, tässä voi kestää hetken",
"Use your other device to continue…": "Jatka toisella laitteellasi…"
"Use your other device to continue…": "Jatka toisella laitteellasi…",
"Click the button below to confirm adding this email address.": "Klikkaa alapuolella olevaa painiketta lisätäksesi tämän sähköpostiosoitteen.",
"Click the button below to confirm adding this phone number.": "Klikkaa alapuolella olevaa painiketta lisätäksesi tämän puhelinnumeron.",
"If you cancel now, you won't complete your operation.": "Jos peruutat, toimintoa ei suoriteta loppuun.",
"Review where youre logged in": "Tarkasta missä olet sisäänkirjautuneena",
"New login. Was this you?": "Uusi sisäänkirjautuminen. Olitko se sinä?",
"%(name)s is requesting verification": "%(name)s pyytää varmennusta",
"Sends a message as html, without interpreting it as markdown": "Lähettää viestin HTML-muodossa, tulkitsematta sitä Markdowniksi",
"Please supply a widget URL or embed code": "Anna sovelman osoite tai upotettava koodinpätkä",
"You signed in to a new session without verifying it:": "Olet kirjautunut uuteen istuntoon varmentamatta sitä:",
"Verify your other session using one of the options below.": "Varmenna toinen istuntosi käyttämällä yhtä seuraavista tavoista."
}

View file

@ -2422,5 +2422,14 @@
"Verify your other session using one of the options below.": "Vérifiez votre autre session en utilisant une des options ci-dessous.",
"Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Invitez quelquun en utilisant leur nom, leur nom dutilisateur (comme <userId/>), leur adresse e-mail ou <a>partagez ce salon</a>.",
"Message deleted": "Message supprimé",
"Message deleted by %(name)s": "Message supprimé par %(name)s"
"Message deleted by %(name)s": "Message supprimé par %(name)s",
"Opens chat with the given user": "Ouvre une discussion avec lutilisateur fourni",
"Sends a message to the given user": "Envoie un message à lutilisateur fourni",
"Waiting for your other session to verify…": "En attente de la vérification depuis votre autre session…",
"You've successfully verified your device!": "Vous avez bien vérifié votre appareil !",
"To continue, use Single Sign On to prove your identity.": "Pour continuer, utilisez lauthentification unique pour prouver votre identité.",
"Confirm to continue": "Confirmer pour continuer",
"Click the button below to confirm your identity.": "Cliquez sur le bouton ci-dessous pour confirmer votre identité.",
"Confirm encryption setup": "Confirmer la configuration du chiffrement",
"Click the button below to confirm setting up encryption.": "Cliquez sur le bouton ci-dessous pour confirmer la configuration du chiffrement."
}

View file

@ -2417,5 +2417,14 @@
"Verify your other session using one of the options below.": "Verifica la tua altra sessione usando una delle opzioni sotto.",
"Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Invita qualcuno usando il suo nome, nome utente (come <userId/>), indirizzo email o <a>condividi questa stanza</a>.",
"Message deleted": "Messaggio eliminato",
"Message deleted by %(name)s": "Messaggio eliminato da %(name)s"
"Message deleted by %(name)s": "Messaggio eliminato da %(name)s",
"Opens chat with the given user": "Apre una chat con l'utente specificato",
"Sends a message to the given user": "Invia un messaggio all'utente specificato",
"Waiting for your other session to verify…": "In attesa che la tua altra sessione verifichi…",
"You've successfully verified your device!": "Hai verificato correttamente il tuo dispositivo!",
"To continue, use Single Sign On to prove your identity.": "Per continuare, usa Single Sign On per provare la tua identità.",
"Confirm to continue": "Conferma per continuare",
"Click the button below to confirm your identity.": "Clicca il pulsante sotto per confermare la tua identità.",
"Confirm encryption setup": "Conferma impostazione cifratura",
"Click the button below to confirm setting up encryption.": "Clicca il pulsante sotto per confermare l'impostazione della cifratura."
}

View file

@ -523,7 +523,7 @@
"Invite to this room": "이 방에 초대",
"You cannot delete this message. (%(code)s)": "이 메시지를 삭제할 수 없습니다. (%(code)s)",
"Thursday": "목요일",
"I understand the risks and wish to continue": "위험을 인지하고 계속하고 싶습니다",
"I understand the risks and wish to continue": "위험하다는 것을 이해했으며 계속하고 싶습니다",
"Back": "돌아가기",
"Failed to change settings": "설정 변경 실패",
"Show message in desktop notification": "컴퓨터 알림에서 내용 보이기",
@ -1799,5 +1799,7 @@
"Session already verified!": "이미 검증된 세션입니다!",
"WARNING: Session already verified, but keys do NOT MATCH!": "경고: 이미 검증된 세션이지만 키가 일치하지 않습니다!",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "경고: 키 검증 실패! 제공된 키인 \"%(fingerprint)s\"가 사용자 %(userId)s와 %(deviceId)s 세션의 서명 키인 \"%(fprint)s\"와 일치하지 않습니다. 이는 통신이 탈취되고 있는 중일 수도 있다는 뜻입니다!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "사용자 %(userId)s의 세션 %(deviceId)s에서 받은 서명 키와 당신이 제공한 서명 키가 일치합니다. 세션이 검증되었습니다."
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "사용자 %(userId)s의 세션 %(deviceId)s에서 받은 서명 키와 당신이 제공한 서명 키가 일치합니다. 세션이 검증되었습니다.",
"Show more": "더 보기",
"Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "비밀번호를 변경한다면 방의 암호화 키를 내보낸 후 다시 가져오지 않는 이상 모든 종단간 암호화 키는 초기화 될 것이고, 암호화된 대화 내역은 읽을 수 없게 될 것입니다. 이 문제는 추후에 개선될 것입니다."
}

View file

@ -1341,5 +1341,6 @@
"You're done!": "Du har gjort alt klart!",
"Enter a recovery passphrase...": "Skriv inn en gjenopprettingspassfrase …",
"Please enter your recovery passphrase a second time to confirm.": "Skriv inn gjenopprettingspassfrasen din en andre gang for å bekrefte.",
"Repeat your recovery passphrase...": "Gjenta gjenopprettingspassfrasen din …"
"Repeat your recovery passphrase...": "Gjenta gjenopprettingspassfrasen din …",
"Other users may not trust it": "Andre brukere kan kanskje mistro den"
}

View file

@ -1443,5 +1443,30 @@
"Verifies a user, session, and pubkey tuple": "验证用户、会话和公钥元组",
"Unknown (user, session) pair:": "未知(用户、会话)对:",
"Session already verified!": "会话已验证!",
"WARNING: Session already verified, but keys do NOT MATCH!": "警告:会话已验证,但密钥不匹配!"
"WARNING: Session already verified, but keys do NOT MATCH!": "警告:会话已验证,但密钥不匹配!",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "警告:密钥验证失败!%(userId)s 的会话 %(deviceId)s 的签名密钥为 %(fprint)s与提供的密钥 %(fingerprint)s 不符。这可能表示您的通讯已被截获!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "您提供的签名密钥与您从 %(userId)s 的会话 %(deviceId)s 获取的一致。该会话被标为已验证。",
"Sends the given message coloured as a rainbow": "以彩虹色发送给定消息",
"Sends the given emote coloured as a rainbow": "以彩虹色发送给定表情符号",
"Displays list of commands with usages and descriptions": "显示指令清单与其描述和用法",
"Displays information about a user": "显示关于用户的信息",
"Send a bug report with logs": "发送带日志的错误报告",
"Opens chat with the given user": "与指定用户发起聊天",
"Sends a message to the given user": "向指定用户发消息",
"%(senderName)s made no change.": "%(senderName)s 未做出变更。",
"%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s 将聊天室名称从 %(oldRoomName)s 改为 %(newRoomName)s。",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s 为此聊天室添加备用地址 %(addresses)s。",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s 为此聊天室添加了备用地址 %(addresses)s。",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s 为此聊天室移除了备用地址 %(addresses)s。",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s 为此聊天室移除了备用地址 %(addresses)s。",
"%(senderName)s changed the alternative addresses for this room.": "%(senderName)s 更改了此聊天室的备用地址。",
"%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s 更改了此聊天室的主地址与备用地址。",
"%(senderName)s changed the addresses for this room.": "%(senderName)s 更改了此聊天室的地址。",
"%(senderName)s placed a voice call.": "%(senderName)s 发起了语音通话。",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s 发起了语音通话。(此浏览器不支持)",
"%(senderName)s placed a video call.": "%(senderName)s 发起了视频通话。",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s 发起了视频通话。(此浏览器不支持)",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的用户的规则",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的服务器的规则",
"%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的规则"
}

View file

@ -2421,5 +2421,14 @@
"Verify your other session using one of the options below.": "使用下方的其中一個選項來驗證您其他的工作階段。",
"Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "使用某人的名字、使用者名稱(如 <userId/>)、電子郵件地址或<a>分享此聊天室</a>來邀請他們。",
"Message deleted": "訊息已刪除",
"Message deleted by %(name)s": "訊息已被 %(name)s 刪除"
"Message deleted by %(name)s": "訊息已被 %(name)s 刪除",
"Opens chat with the given user": "開啟與指定使用者的聊天",
"Sends a message to the given user": "傳送訊息給指定的使用者",
"Waiting for your other session to verify…": "正在等待您的其他工作階段進行驗證……",
"You've successfully verified your device!": "您已成功驗證您的裝置!",
"To continue, use Single Sign On to prove your identity.": "要繼續,使用單一登入系統來證明您的身份。",
"Confirm to continue": "確認以繼續",
"Click the button below to confirm your identity.": "點擊下方按鈕以確認您的身份。",
"Confirm encryption setup": "確認加密設定",
"Click the button below to confirm setting up encryption.": "點擊下方按鈕以確認設定加密。"
}

View file

@ -44,7 +44,7 @@ function matrixLinkify(linkify) {
const S_HASH = S_START.jump(TT.POUND);
const S_HASH_NAME = new linkify.parser.State();
const S_HASH_NAME_COLON = new linkify.parser.State();
const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State();
const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State(ROOMALIAS);
const S_HASH_NAME_COLON_DOMAIN_DOT = new linkify.parser.State();
const S_ROOMALIAS = new linkify.parser.State(ROOMALIAS);
const S_ROOMALIAS_COLON = new linkify.parser.State();
@ -92,7 +92,7 @@ function matrixLinkify(linkify) {
const S_AT = S_START.jump(TT.AT);
const S_AT_NAME = new linkify.parser.State();
const S_AT_NAME_COLON = new linkify.parser.State();
const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State();
const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State(USERID);
const S_AT_NAME_COLON_DOMAIN_DOT = new linkify.parser.State();
const S_USERID = new linkify.parser.State(USERID);
const S_USERID_COLON = new linkify.parser.State();
@ -138,7 +138,7 @@ function matrixLinkify(linkify) {
const S_PLUS = S_START.jump(TT.PLUS);
const S_PLUS_NAME = new linkify.parser.State();
const S_PLUS_NAME_COLON = new linkify.parser.State();
const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State();
const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(GROUPID);
const S_PLUS_NAME_COLON_DOMAIN_DOT = new linkify.parser.State();
const S_GROUPID = new linkify.parser.State(GROUPID);
const S_GROUPID_COLON = new linkify.parser.State();

View file

@ -77,8 +77,6 @@ function findRefNodes(root, route, isAddition) {
const end = isAddition ? route.length - 1 : route.length;
for (let i = 0; i < end; ++i) {
refParentNode = refNode;
// Lists don't have appropriate child nodes we can use.
if (!refNode.childNodes[route[i]]) continue;
refNode = refNode.childNodes[route[i]];
}
return {refNode, refParentNode};
@ -190,10 +188,11 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
break;
}
case "addTextElement": {
if (diff.value !== "\n") {
const insNode = wrapInsertion(stringAsTextNode(diff.value));
insertBefore(refParentNode, refNode, insNode);
}
// XXX: sometimes diffDOM says insert a newline when there shouldn't be one
// but we must insert the node anyway so that we don't break the route child IDs.
// See https://github.com/fiduswriter/diffDOM/issues/100
const insNode = wrapInsertion(stringAsTextNode(diff.value !== "\n" ? diff.value : ""));
insertBefore(refParentNode, refNode, insNode);
break;
}
// e.g. when changing a the href of a link,

View file

@ -1293,6 +1293,13 @@
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/qrcode@^1.3.4":
version "1.3.4"
resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b"
integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg==
dependencies:
"@types/node" "*"
"@types/react@16.9":
version "16.9.32"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383"
@ -6872,18 +6879,6 @@ pvutils@latest:
resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.0.17.tgz#ade3c74dfe7178944fe44806626bd2e249d996bf"
integrity sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==
qr.js@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
qrcode-react@^0.1.16:
version "0.1.16"
resolved "https://registry.yarnpkg.com/qrcode-react/-/qrcode-react-0.1.16.tgz#d064999d510ffc3e55a9ca3ffcf6c203c69f1517"
integrity sha512-FK+QCfFqCQMSxUE1byzglERJQkwKqXYvYMCS+/Ad2zACJOfoHkHHtRqsQQPji7lfb1y1qCXLvL+3eP1hAfg8Ng==
dependencies:
qr.js "0.0.0"
qrcode@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"