Merge branch 'develop' into feature/padLockInviteOnly

This commit is contained in:
Travis Ralston 2020-02-24 09:50:06 -07:00
commit d2e7cc7f19
138 changed files with 4634 additions and 1211 deletions

View file

@ -1,18 +1,21 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
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
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
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.
*/
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { getCurrentLanguage, _t, _td } from './languageHandler';
import PlatformPeg from './PlatformPeg';
@ -54,6 +57,8 @@ function getRedactedUrl() {
}
const customVariables = {
// The Matomo installation at https://matomo.riot.im is currently configured
// with a limit of 10 custom variables.
'App Platform': {
id: 1,
expl: _td('The platform you\'re on'),
@ -61,7 +66,7 @@ const customVariables = {
},
'App Version': {
id: 2,
expl: _td('The version of Riot.im'),
expl: _td('The version of Riot'),
example: '15.0.0',
},
'User Type': {
@ -84,20 +89,25 @@ const customVariables = {
expl: _td('Whether or not you\'re using the Richtext mode of the Rich Text Editor'),
example: 'off',
},
'Breadcrumbs': {
id: 9,
expl: _td("Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)"),
example: 'disabled',
},
'Homeserver URL': {
id: 7,
expl: _td('Your homeserver\'s URL'),
example: 'https://matrix.org',
},
'Identity Server URL': {
'Touch Input': {
id: 8,
expl: _td('Your identity server\'s URL'),
example: 'https://vector.im',
expl: _td("Whether you're using Riot on a device where touch is the primary input mechanism"),
example: 'false',
},
'Breadcrumbs': {
id: 9,
expl: _td("Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)"),
example: 'disabled',
},
'Installed PWA': {
id: 10,
expl: _td("Whether you're using Riot as an installed Progressive Web App"),
example: 'false',
},
};
@ -106,61 +116,80 @@ function whitelistRedact(whitelist, str) {
return '<redacted>';
}
const UID_KEY = "mx_Riot_Analytics_uid";
const CREATION_TS_KEY = "mx_Riot_Analytics_cts";
const VISIT_COUNT_KEY = "mx_Riot_Analytics_vc";
const LAST_VISIT_TS_KEY = "mx_Riot_Analytics_lvts";
function getUid() {
try {
let data = localStorage.getItem(UID_KEY);
if (!data) {
localStorage.setItem(UID_KEY, data = [...Array(16)].map(() => Math.random().toString(16)[2]).join(''));
}
return data;
} catch (e) {
console.error("Analytics error: ", e);
return "";
}
}
const HEARTBEAT_INTERVAL = 30 * 1000; // seconds
class Analytics {
constructor() {
this._paq = null;
this.disabled = true;
this.baseUrl = null;
this.siteId = null;
this.visitVariables = {};
this.firstPage = true;
this._heartbeatIntervalID = null;
this.creationTs = localStorage.getItem(CREATION_TS_KEY);
if (!this.creationTs) {
localStorage.setItem(CREATION_TS_KEY, this.creationTs = new Date().getTime());
}
this.lastVisitTs = localStorage.getItem(LAST_VISIT_TS_KEY);
this.visitCount = localStorage.getItem(VISIT_COUNT_KEY) || 0;
localStorage.setItem(VISIT_COUNT_KEY, parseInt(this.visitCount, 10) + 1);
}
get disabled() {
return !this.baseUrl;
}
/**
* Enable Analytics if initialized but disabled
* otherwise try and initalize, no-op if piwik config missing
*/
enable() {
if (this._paq || this._init()) {
this.disabled = false;
}
}
async enable() {
if (!this.disabled) return;
/**
* Disable Analytics calls, will not fully unload Piwik until a refresh,
* but this is second best, Piwik should not pull anything implicitly.
*/
disable() {
this.trackEvent('Analytics', 'opt-out');
// disableHeartBeatTimer is undocumented but exists in the piwik code
// the _paq.push method will result in an error being printed in the console
// if an unknown method signature is passed
this._paq.push(['disableHeartBeatTimer']);
this.disabled = true;
}
_init() {
const config = SdkConfig.get();
if (!config || !config.piwik || !config.piwik.url || !config.piwik.siteId) return;
const url = config.piwik.url;
const siteId = config.piwik.siteId;
const self = this;
window._paq = this._paq = window._paq || [];
this._paq.push(['setTrackerUrl', url+'piwik.php']);
this._paq.push(['setSiteId', siteId]);
this._paq.push(['trackAllContentImpressions']);
this._paq.push(['discardHashTag', false]);
this._paq.push(['enableHeartBeatTimer']);
// this._paq.push(['enableLinkTracking', true]);
this.baseUrl = new URL("piwik.php", config.piwik.url);
// set constants
this.baseUrl.searchParams.set("rec", 1); // rec is required for tracking
this.baseUrl.searchParams.set("idsite", config.piwik.siteId); // rec is required for tracking
this.baseUrl.searchParams.set("apiv", 1); // API version to use
this.baseUrl.searchParams.set("send_image", 0); // we want a 204, not a tiny GIF
// set user parameters
this.baseUrl.searchParams.set("_id", getUid()); // uuid
this.baseUrl.searchParams.set("_idts", this.creationTs); // first ts
this.baseUrl.searchParams.set("_idvc", parseInt(this.visitCount, 10)+ 1); // visit count
if (this.lastVisitTs) {
this.baseUrl.searchParams.set("_viewts", this.lastVisitTs); // last visit ts
}
const platform = PlatformPeg.get();
this._setVisitVariable('App Platform', platform.getHumanReadableName());
platform.getAppVersion().then((version) => {
this._setVisitVariable('App Version', version);
}).catch(() => {
try {
this._setVisitVariable('App Version', await platform.getAppVersion());
} catch (e) {
this._setVisitVariable('App Version', 'unknown');
});
}
this._setVisitVariable('Chosen Language', getCurrentLanguage());
@ -168,20 +197,78 @@ class Analytics {
this._setVisitVariable('Instance', window.location.pathname);
}
(function() {
const g = document.createElement('script');
const s = document.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=url+'piwik.js';
let installedPWA = "unknown";
try {
// Known to work at least for desktop Chrome
installedPWA = window.matchMedia('(display-mode: standalone)').matches;
} catch (e) { }
this._setVisitVariable('Installed PWA', installedPWA);
g.onload = function() {
console.log('Initialised anonymous analytics');
self._paq = window._paq;
};
let touchInput = "unknown";
try {
// MDN claims broad support across browsers
touchInput = window.matchMedia('(pointer: coarse)').matches;
} catch (e) { }
this._setVisitVariable('Touch Input', touchInput);
s.parentNode.insertBefore(g, s);
})();
// start heartbeat
this._heartbeatIntervalID = window.setInterval(this.ping.bind(this), HEARTBEAT_INTERVAL);
}
return true;
/**
* Disable Analytics, stop the heartbeat and clear identifiers from localStorage
*/
disable() {
if (this.disabled) return;
this.trackEvent('Analytics', 'opt-out');
window.clearInterval(this._heartbeatIntervalID);
this.baseUrl = null;
this.visitVariables = {};
localStorage.removeItem(UID_KEY);
localStorage.removeItem(CREATION_TS_KEY);
localStorage.removeItem(VISIT_COUNT_KEY);
localStorage.removeItem(LAST_VISIT_TS_KEY);
}
async _track(data) {
if (this.disabled) return;
const now = new Date();
const params = {
...data,
url: getRedactedUrl(),
_cvar: JSON.stringify(this.visitVariables), // user custom vars
res: `${window.screen.width}x${window.screen.height}`, // resolution as WWWWxHHHH
rand: String(Math.random()).slice(2, 8), // random nonce to cache-bust
h: now.getHours(),
m: now.getMinutes(),
s: now.getSeconds(),
};
const url = new URL(this.baseUrl);
for (const key in params) {
url.searchParams.set(key, params[key]);
}
try {
await window.fetch(url, {
method: "GET",
mode: "no-cors",
cache: "no-cache",
redirect: "follow",
});
} catch (e) {
console.error("Analytics error: ", e);
window.err = e;
}
}
ping() {
this._track({
ping: 1,
});
localStorage.setItem(LAST_VISIT_TS_KEY, new Date().getTime()); // update last visit ts
}
trackPageChange(generationTimeMs) {
@ -193,31 +280,29 @@ class Analytics {
return;
}
if (typeof generationTimeMs === 'number') {
this._paq.push(['setGenerationTimeMs', generationTimeMs]);
} else {
if (typeof generationTimeMs !== 'number') {
console.warn('Analytics.trackPageChange: expected generationTimeMs to be a number');
// But continue anyway because we still want to track the change
}
this._paq.push(['setCustomUrl', getRedactedUrl()]);
this._paq.push(['trackPageView']);
this._track({
gt_ms: generationTimeMs,
});
}
trackEvent(category, action, name, value) {
if (this.disabled) return;
this._paq.push(['setCustomUrl', getRedactedUrl()]);
this._paq.push(['trackEvent', category, action, name, value]);
}
logout() {
if (this.disabled) return;
this._paq.push(['deleteCookies']);
this._track({
e_c: category,
e_a: action,
e_n: name,
e_v: value,
});
}
_setVisitVariable(key, value) {
if (this.disabled) return;
this._paq.push(['setCustomVariable', customVariables[key].id, key, value, 'visit']);
this.visitVariables[customVariables[key].id] = [key, value];
}
setLoggedIn(isGuest, homeserverUrl, identityServerUrl) {
@ -227,16 +312,9 @@ class Analytics {
if (!config.piwik) return;
const whitelistedHSUrls = config.piwik.whitelistedHSUrls || [];
const whitelistedISUrls = config.piwik.whitelistedISUrls || [];
this._setVisitVariable('User Type', isGuest ? 'Guest' : 'Logged In');
this._setVisitVariable('Homeserver URL', whitelistRedact(whitelistedHSUrls, homeserverUrl));
this._setVisitVariable('Identity Server URL', whitelistRedact(whitelistedISUrls, identityServerUrl));
}
setRichtextMode(state) {
if (this.disabled) return;
this._setVisitVariable('RTE: Uses Richtext Mode', state ? 'on' : 'off');
}
setBreadcrumbs(state) {
@ -244,13 +322,11 @@ class Analytics {
this._setVisitVariable('Breadcrumbs', state ? 'enabled' : 'disabled');
}
showDetailsModal() {
showDetailsModal = () => {
let rows = [];
if (window.Piwik) {
const Tracker = window.Piwik.getAsyncTracker();
rows = Object.values(customVariables).map((v) => Tracker.getCustomVariable(v.id)).filter(Boolean);
if (!this.disabled) {
rows = Object.values(this.visitVariables);
} else {
// Piwik may not have been enabled, so show example values
rows = Object.keys(customVariables).map(
(k) => [
k,
@ -271,7 +347,7 @@ class Analytics {
},
),
},
{ expl: _td('Your User Agent'), value: navigator.userAgent },
{ expl: _td('Your user agent'), value: navigator.userAgent },
{ expl: _td('Your device resolution'), value: resolution },
];
@ -280,7 +356,7 @@ class Analytics {
title: _t('Analytics'),
description: <div className="mx_AnalyticsModal">
<div>
{ _t('The information being sent to us to help make Riot.im better includes:') }
{ _t('The information being sent to us to help make Riot better includes:') }
</div>
<table>
{ rows.map((row) => <tr key={row[0]}>
@ -300,7 +376,7 @@ class Analytics {
</div>
</div>,
});
}
};
}
if (!global.mxAnalytics) {

View file

@ -102,6 +102,8 @@ export function getInitialLetter(name) {
}
export function avatarUrlForRoom(room, width, height, resizeMethod) {
if (!room) return null; // null-guard
const explicitRoomAvatar = room.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
width,

View file

@ -43,7 +43,28 @@ export class AccessCancelledError extends Error {
}
}
async function getSecretStorageKey({ keys: keyInfos }) {
async function confirmToDismiss(name) {
let description;
if (name === "m.cross_signing.user_signing") {
description = _t("If you cancel now, you won't complete verifying the other user.");
} else if (name === "m.cross_signing.self_signing") {
description = _t("If you cancel now, you won't complete verifying your other session.");
} else {
description = _t("If you cancel now, you won't complete your secret storage operation.");
}
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const [sure] = await Modal.createDialog(QuestionDialog, {
title: _t("Cancel entering passphrase?"),
description,
danger: true,
cancelButton: _t("Enter passphrase"),
button: _t("Cancel"),
}).finished;
return sure;
}
async function getSecretStorageKey({ keys: keyInfos }, ssssItemName) {
const keyInfoEntries = Object.entries(keyInfos);
if (keyInfoEntries.length > 1) {
throw new Error("Multiple storage key requests not implemented");
@ -70,6 +91,7 @@ async function getSecretStorageKey({ keys: keyInfos }) {
sdk.getComponent("dialogs.secretstorage.AccessSecretStorageDialog");
const { finished } = Modal.createTrackedDialog("Access Secret Storage dialog", "",
AccessSecretStorageDialog,
/* props= */
{
keyInfo: info,
checkPrivateKey: async (input) => {
@ -77,6 +99,17 @@ async function getSecretStorageKey({ keys: keyInfos }) {
return MatrixClientPeg.get().checkSecretStoragePrivateKey(key, info.pubkey);
},
},
/* className= */ null,
/* isPriorityModal= */ false,
/* isStaticModal= */ false,
/* options= */ {
onBeforeClose: async (reason) => {
if (reason === "backgroundClick") {
return confirmToDismiss(ssssItemName);
}
return true;
},
},
);
const [input] = await finished;
if (!input) {
@ -115,18 +148,21 @@ export const crossSigningCallbacks = {
*
* @param {Function} [func] An operation to perform once secret storage has been
* bootstrapped. Optional.
* @param {bool} [force] Reset secret storage even if it's already set up
*/
export async function accessSecretStorage(func = async () => { }) {
export async function accessSecretStorage(func = async () => { }, force = false) {
const cli = MatrixClientPeg.get();
secretStorageBeingAccessed = true;
try {
if (!await cli.hasSecretStorageKey()) {
if (!await cli.hasSecretStorageKey() || force) {
// This dialog calls bootstrap itself after guiding the user through
// passphrase creation.
const { finished } = Modal.createTrackedDialogAsync('Create Secret Storage dialog', '',
import("./async-components/views/dialogs/secretstorage/CreateSecretStorageDialog"),
null, null, /* priority = */ false, /* static = */ true,
{
force,
},
null, /* priority = */ false, /* static = */ true,
);
const [confirmed] = await finished;
if (!confirmed) {

View file

@ -160,7 +160,7 @@ const transformTags = { // custom to matrix
delete attribs.target;
}
}
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
attribs.rel = 'noreferrer noopener'; // https://mathiasbynens.github.io/rel-noopener/
return { tagName, attribs };
},
'img': function(tagName, attribs) {

View file

@ -435,7 +435,7 @@ async function _doSetLoggedIn(credentials, clearStorage) {
}
}
Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl);
Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl);
if (localStorage) {
try {
@ -632,7 +632,7 @@ export async function onLoggedOut() {
* @returns {Promise} promise which resolves once the stores have been cleared
*/
async function _clearStorage() {
Analytics.logout();
Analytics.disable();
if (window.localStorage) {
window.localStorage.clear();

View file

@ -136,7 +136,7 @@ export default class Markdown {
// thus opening in a new tab.
if (externalLinks) {
attrs.push(['target', '_blank']);
attrs.push(['rel', 'noopener']);
attrs.push(['rel', 'noreferrer noopener']);
}
this.tag('a', attrs);
} else {

View file

@ -32,7 +32,7 @@ import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientB
import * as StorageManager from './utils/StorageManager';
import IdentityAuthClient from './IdentityAuthClient';
import { crossSigningCallbacks } from './CrossSigningManager';
import {SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
interface MatrixClientCreds {
homeserverUrl: string,
@ -221,7 +221,6 @@ class _MatrixClientPeg {
verificationMethods: [
verificationMethods.SAS,
SHOW_QR_CODE_METHOD,
SCAN_QR_CODE_METHOD, // XXX: We don't actually support scanning yet!
verificationMethods.RECIPROCATE_QR_CODE,
],
unstableClientRelationAggregation: true,

View file

@ -47,7 +47,7 @@ class ModalManager {
} */
];
this.closeAll = this.closeAll.bind(this);
this.onBackgroundClick = this.onBackgroundClick.bind(this);
}
hasDialogs() {
@ -106,7 +106,7 @@ class ModalManager {
return this.appendDialogAsync(...rest);
}
_buildModal(prom, props, className) {
_buildModal(prom, props, className, options) {
const modal = {};
// never call this from onFinished() otherwise it will loop
@ -124,13 +124,27 @@ class ModalManager {
);
modal.onFinished = props ? props.onFinished : null;
modal.className = className;
modal.onBeforeClose = options.onBeforeClose;
modal.beforeClosePromise = null;
modal.close = closeDialog;
modal.closeReason = null;
return {modal, closeDialog, onFinishedProm};
}
_getCloseFn(modal, props) {
const deferred = defer();
return [(...args) => {
return [async (...args) => {
if (modal.beforeClosePromise) {
await modal.beforeClosePromise;
} else if (modal.onBeforeClose) {
modal.beforeClosePromise = modal.onBeforeClose(modal.closeReason);
const shouldClose = await modal.beforeClosePromise;
modal.beforeClosePromise = null;
if (!shouldClose) {
return;
}
}
deferred.resolve(args);
if (props && props.onFinished) props.onFinished.apply(null, args);
const i = this._modals.indexOf(modal);
@ -156,6 +170,12 @@ class ModalManager {
}, deferred.promise];
}
/**
* @callback onBeforeClose
* @param {string?} reason either "backgroundClick" or null
* @return {Promise<bool>} whether the dialog should close
*/
/**
* Open a modal view.
*
@ -183,11 +203,12 @@ class ModalManager {
* also be removed from the stack. This is not compatible
* with being a priority modal. Only one modal can be
* static at a time.
* @param {Object} options? extra options for the dialog
* @param {onBeforeClose} options.onBeforeClose a callback to decide whether to close the dialog
* @returns {object} Object with 'close' parameter being a function that will close the dialog
*/
createDialogAsync(prom, props, className, isPriorityModal, isStaticModal) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className);
createDialogAsync(prom, props, className, isPriorityModal, isStaticModal, options = {}) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, options);
if (isPriorityModal) {
// XXX: This is destructive
this._priorityModal = modal;
@ -206,7 +227,7 @@ class ModalManager {
}
appendDialogAsync(prom, props, className) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className);
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, {});
this._modals.push(modal);
this._reRender();
@ -216,24 +237,22 @@ class ModalManager {
};
}
closeAll() {
const modalsToClose = [...this._modals, this._priorityModal];
this._modals = [];
this._priorityModal = null;
if (this._staticModal && modalsToClose.length === 0) {
modalsToClose.push(this._staticModal);
this._staticModal = null;
onBackgroundClick() {
const modal = this._getCurrentModal();
if (!modal) {
return;
}
// we want to pass a reason to the onBeforeClose
// callback, but close is currently defined to
// pass all number of arguments to the onFinished callback
// so, pass the reason to close through a member variable
modal.closeReason = "backgroundClick";
modal.close();
modal.closeReason = null;
}
for (let i = 0; i < modalsToClose.length; i++) {
const m = modalsToClose[i];
if (m && m.onFinished) {
m.onFinished(false);
}
}
this._reRender();
_getCurrentModal() {
return this._priorityModal ? this._priorityModal : (this._modals[0] || this._staticModal);
}
_reRender() {
@ -264,7 +283,7 @@ class ModalManager {
<div className="mx_Dialog">
{ this._staticModal.elem }
</div>
<div className="mx_Dialog_background mx_Dialog_staticBackground" onClick={this.closeAll}></div>
<div className="mx_Dialog_background mx_Dialog_staticBackground" onClick={this.onBackgroundClick}></div>
</div>
);
@ -274,8 +293,8 @@ class ModalManager {
ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer());
}
const modal = this._priorityModal ? this._priorityModal : this._modals[0];
if (modal) {
const modal = this._getCurrentModal();
if (modal !== this._staticModal) {
const classes = "mx_Dialog_wrapper "
+ (this._staticModal ? "mx_Dialog_wrapperWithStaticUnder " : '')
+ (modal.className ? modal.className : '');
@ -285,7 +304,7 @@ class ModalManager {
<div className="mx_Dialog">
{modal.elem}
</div>
<div className="mx_Dialog_background" onClick={this.closeAll}></div>
<div className="mx_Dialog_background" onClick={this.onBackgroundClick}></div>
</div>
);

View file

@ -153,10 +153,12 @@ const Notifier = {
},
start: function() {
this.boundOnEvent = this.onEvent.bind(this);
this.boundOnSyncStateChange = this.onSyncStateChange.bind(this);
this.boundOnRoomReceipt = this.onRoomReceipt.bind(this);
this.boundOnEventDecrypted = this.onEventDecrypted.bind(this);
// do not re-bind in the case of repeated call
this.boundOnEvent = this.boundOnEvent || this.onEvent.bind(this);
this.boundOnSyncStateChange = this.boundOnSyncStateChange || this.onSyncStateChange.bind(this);
this.boundOnRoomReceipt = this.boundOnRoomReceipt || this.onRoomReceipt.bind(this);
this.boundOnEventDecrypted = this.boundOnEventDecrypted || this.onEventDecrypted.bind(this);
MatrixClientPeg.get().on('event', this.boundOnEvent);
MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt);
MatrixClientPeg.get().on('Event.decrypted', this.boundOnEventDecrypted);
@ -166,7 +168,7 @@ const Notifier = {
},
stop: function() {
if (MatrixClientPeg.get() && this.boundOnRoomTimeline) {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('Event', this.boundOnEvent);
MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt);
MatrixClientPeg.get().removeListener('Event.decrypted', this.boundOnEventDecrypted);

View file

@ -23,7 +23,7 @@ import {MatrixClientPeg} from './MatrixClientPeg';
* of aliases. Otherwise return null;
*/
export function getDisplayAliasForRoom(room) {
return room.getCanonicalAlias() || room.getAliases()[0];
return room.getCanonicalAlias() || room.getAltAliases()[0];
}
/**

View file

@ -46,9 +46,18 @@ export default class ManageEventIndexDialog extends React.Component {
};
}
async updateCurrentRoom(room) {
updateCurrentRoom = async (room) => {
const eventIndex = EventIndexPeg.get();
const stats = await eventIndex.getStats();
let stats;
try {
stats = await eventIndex.getStats();
} catch {
// This call may fail if sporadically, not a huge issue as we will
// try later again and probably succeed.
return;
}
let currentRoom = null;
if (room) currentRoom = room.name;
@ -63,13 +72,13 @@ export default class ManageEventIndexDialog extends React.Component {
roomCount: roomCount,
currentRoom: currentRoom,
});
}
};
componentWillUnmount(): void {
const eventIndex = EventIndexPeg.get();
if (eventIndex !== null) {
eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom.bind(this));
eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom);
}
}
@ -83,14 +92,21 @@ export default class ManageEventIndexDialog extends React.Component {
const eventIndex = EventIndexPeg.get();
if (eventIndex !== null) {
eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this));
eventIndex.on("changedCheckpoint", this.updateCurrentRoom);
try {
const stats = await eventIndex.getStats();
eventIndexSize = stats.size;
eventCount = stats.eventCount;
} catch {
// This call may fail if sporadically, not a huge issue as we
// will try later again in the updateCurrentRoom call and
// probably succeed.
}
const stats = await eventIndex.getStats();
const roomStats = eventIndex.crawlingRooms();
eventIndexSize = stats.size;
crawlingRoomsCount = roomStats.crawlingRooms.size;
roomCount = roomStats.totalRooms.size;
eventCount = stats.eventCount;
const room = eventIndex.currentRoom();
if (room) currentRoom = room.name;
@ -144,8 +160,10 @@ export default class ManageEventIndexDialog extends React.Component {
<div className='mx_SettingsTab_subsectionText'>
{_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}<br />
{_t("Indexed messages:")} {formatCountLong(this.state.eventCount)}<br />
{_t("Number of rooms:")} {formatCountLong(this.state.crawlingRoomsCount)} {_t("of ")}
{formatCountLong(this.state.roomCount)}<br />
{_t("Indexed rooms:")} {_t("%(crawlingRooms)s out of %(totalRooms)s", {
crawlingRooms: formatCountLong(this.state.crawlingRoomsCount),
totalRooms: formatCountLong(this.state.roomCount),
})} <br />
{crawlerState}<br />
<Field
id={"crawlerSleepTimeMs"}

View file

@ -24,6 +24,7 @@ import { scorePassword } from '../../../../utils/PasswordScorer';
import { _t } from '../../../../languageHandler';
import { accessSecretStorage } from '../../../../CrossSigningManager';
import SettingsStore from '../../../../settings/SettingsStore';
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
const PHASE_PASSPHRASE = 0;
const PHASE_PASSPHRASE_CONFIRM = 1;
@ -191,44 +192,38 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
});
}
_onPassPhraseNextClick = () => {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
}
_onPassPhraseNextClick = async (e) => {
e.preventDefault();
_onPassPhraseKeyPress = async (e) => {
if (e.key === 'Enter') {
// If we're waiting for the timeout before updating the result at this point,
// skip ahead and do it now, otherwise we'll deny the attempt to proceed
// even if the user entered a valid passphrase
if (this._setZxcvbnResultTimeout !== null) {
clearTimeout(this._setZxcvbnResultTimeout);
this._setZxcvbnResultTimeout = null;
await new Promise((resolve) => {
this.setState({
zxcvbnResult: scorePassword(this.state.passPhrase),
}, resolve);
});
}
if (this._passPhraseIsValid()) {
this._onPassPhraseNextClick();
}
// If we're waiting for the timeout before updating the result at this point,
// skip ahead and do it now, otherwise we'll deny the attempt to proceed
// even if the user entered a valid passphrase
if (this._setZxcvbnResultTimeout !== null) {
clearTimeout(this._setZxcvbnResultTimeout);
this._setZxcvbnResultTimeout = null;
await new Promise((resolve) => {
this.setState({
zxcvbnResult: scorePassword(this.state.passPhrase),
}, resolve);
});
}
}
if (this._passPhraseIsValid()) {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
}
};
_onPassPhraseConfirmNextClick = async (e) => {
e.preventDefault();
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
_onPassPhraseConfirmNextClick = async () => {
this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(this.state.passPhrase);
this.setState({
copied: false,
downloaded: false,
phase: PHASE_SHOWKEY,
});
}
_onPassPhraseConfirmKeyPress = (e) => {
if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) {
this._onPassPhraseConfirmNextClick();
}
}
};
_onSetAgainClick = () => {
this.setState({
@ -299,7 +294,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
</div>;
}
return <div>
return <form onSubmit={this._onPassPhraseNextClick}>
<p>{_t(
"<b>Warning</b>: You should only set up key backup from a trusted computer.", {},
{ b: sub => <b>{sub}</b> },
@ -314,7 +309,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
<input type="password"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Enter a passphrase...")}
@ -327,7 +321,8 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
</div>
</div>
<DialogButtons primaryButton={_t('Next')}
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNextClick}
hasCancel={false}
disabled={!this._passPhraseIsValid()}
@ -335,11 +330,11 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
<details>
<summary>{_t("Advanced")}</summary>
<p><button onClick={this._onSkipPassPhraseClick} >
<AccessibleButton kind='primary' onClick={this._onSkipPassPhraseClick} >
{_t("Set up with a recovery key")}
</button></p>
</AccessibleButton>
</details>
</div>;
</form>;
}
_renderPhasePassPhraseConfirm() {
@ -371,7 +366,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
</div>;
}
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
<p>{_t(
"Please enter your passphrase a second time to confirm.",
)}</p>
@ -380,7 +375,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
<div>
<input type="password"
onChange={this._onPassPhraseConfirmChange}
onKeyPress={this._onPassPhraseConfirmKeyPress}
value={this.state.passPhraseConfirm}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Repeat your passphrase...")}
@ -390,12 +384,13 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
{passPhraseMatch}
</div>
</div>
<DialogButtons primaryButton={_t('Next')}
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
/>
</div>;
</form>;
}
_renderPhaseShowKey() {

View file

@ -55,10 +55,12 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
static propTypes = {
hasCancel: PropTypes.bool,
accountPassword: PropTypes.string,
force: PropTypes.bool,
};
static defaultProps = {
hasCancel: true,
force: false,
};
constructor(props) {
@ -107,7 +109,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
MatrixClientPeg.get().isCryptoEnabled() && await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo)
);
const phase = backupInfo ? PHASE_MIGRATE : PHASE_PASSPHRASE;
const { force } = this.props;
const phase = (backupInfo && !force) ? PHASE_MIGRATE : PHASE_PASSPHRASE;
this.setState({
phase,
@ -219,13 +222,24 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
const cli = MatrixClientPeg.get();
const { force } = this.props;
try {
await cli.bootstrapSecretStorage({
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
createSecretStorageKey: async () => this._keyInfo,
keyBackupInfo: this.state.backupInfo,
setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup,
});
if (force) {
await cli.bootstrapSecretStorage({
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
createSecretStorageKey: async () => this._keyInfo,
setupNewKeyBackup: true,
setupNewSecretStorage: true,
});
} else {
await cli.bootstrapSecretStorage({
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
createSecretStorageKey: async () => this._keyInfo,
keyBackupInfo: this.state.backupInfo,
setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup,
});
}
this.setState({
phase: PHASE_DONE,
});
@ -289,31 +303,31 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
});
}
_onPassPhraseNextClick = () => {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
}
_onPassPhraseNextClick = async (e) => {
e.preventDefault();
_onPassPhraseKeyPress = async (e) => {
if (e.key === 'Enter') {
// If we're waiting for the timeout before updating the result at this point,
// skip ahead and do it now, otherwise we'll deny the attempt to proceed
// even if the user entered a valid passphrase
if (this._setZxcvbnResultTimeout !== null) {
clearTimeout(this._setZxcvbnResultTimeout);
this._setZxcvbnResultTimeout = null;
await new Promise((resolve) => {
this.setState({
zxcvbnResult: scorePassword(this.state.passPhrase),
}, resolve);
});
}
if (this._passPhraseIsValid()) {
this._onPassPhraseNextClick();
}
// If we're waiting for the timeout before updating the result at this point,
// skip ahead and do it now, otherwise we'll deny the attempt to proceed
// even if the user entered a valid passphrase
if (this._setZxcvbnResultTimeout !== null) {
clearTimeout(this._setZxcvbnResultTimeout);
this._setZxcvbnResultTimeout = null;
await new Promise((resolve) => {
this.setState({
zxcvbnResult: scorePassword(this.state.passPhrase),
}, resolve);
});
}
}
if (this._passPhraseIsValid()) {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
}
};
_onPassPhraseConfirmNextClick = async (e) => {
e.preventDefault();
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
_onPassPhraseConfirmNextClick = async () => {
const [keyInfo, encodedRecoveryKey] =
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
this._keyInfo = keyInfo;
@ -325,12 +339,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
});
}
_onPassPhraseConfirmKeyPress = (e) => {
if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) {
this._onPassPhraseConfirmNextClick();
}
}
_onSetAgainClick = () => {
this.setState({
passPhrase: '',
@ -400,7 +408,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
} else if (this.state.canUploadKeysWithPasswordOnly) {
authPrompt = <div>
<div>{_t("Enter your account password to confirm the upgrade:")}</div>
<div><Field type="password"
<div><Field
type="password"
id="mx_CreateSecretStorage_accountPassword"
label={_t("Password")}
value={this.state.accountPassword}
@ -422,8 +431,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
"as trusted for other users.",
)}</p>
<div>{authPrompt}</div>
<DialogButtons primaryButton={nextCaption}
primaryIsSubmit={true}
<DialogButtons
primaryButton={nextCaption}
hasCancel={false}
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
>
@ -467,7 +476,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
</div>;
}
return <div>
return <form onSubmit={this._onPassPhraseNextClick}>
<p>{_t(
"Set up encryption on this session to allow it to verify other sessions, " +
"granting them access to encrypted messages and marking them as trusted for other users.",
@ -483,10 +492,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
id="mx_CreateSecretStorageDialog_passPhraseField"
className="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
label={_t("Enter a passphrase")}
autoFocus={true}
autoComplete="new-password"
/>
<div className="mx_CreateSecretStorageDialog_passPhraseHelp">
{strengthMeter}
@ -499,7 +508,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
onChange={this._onUseKeyBackupChange} value={this.state.useKeyBackup}
/>
<DialogButtons primaryButton={_t('Continue')}
<DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this._onPassPhraseNextClick}
hasCancel={false}
disabled={!this._passPhraseIsValid()}
@ -516,7 +526,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
{_t("Set up with a recovery key")}
</AccessibleButton>
</details>
</div>;
</form>;
}
_renderPhasePassPhraseConfirm() {
@ -549,25 +559,27 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
</div>;
}
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
<p>{_t(
"Enter your passphrase a second time to confirm it.",
)}</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field type="password"
<Field
type="password"
id="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this._onPassPhraseConfirmChange}
onKeyPress={this._onPassPhraseConfirmKeyPress}
value={this.state.passPhraseConfirm}
className="mx_CreateSecretStorageDialog_passPhraseField"
label={_t("Confirm your passphrase")}
autoFocus={true}
autoComplete="new-password"
/>
<div className="mx_CreateSecretStorageDialog_passPhraseMatch">
{passPhraseMatch}
</div>
</div>
<DialogButtons primaryButton={_t('Continue')}
<DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this._onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
@ -577,7 +589,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
className="danger"
>{_t("Skip")}</button>
</DialogButtons>
</div>;
</form>;
}
_renderPhaseShowKey() {

View file

@ -23,7 +23,6 @@ import AutocompleteProvider from './AutocompleteProvider';
import {MatrixClientPeg} from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher';
import {PillCompletion} from './Components';
import {getDisplayAliasForRoom} from '../Rooms';
import * as sdk from '../index';
import _sortBy from 'lodash/sortBy';
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
@ -40,11 +39,19 @@ function score(query, space) {
}
}
function matcherObject(room, displayedAlias, matchName = "") {
return {
room,
matchName,
displayedAlias,
};
}
export default class RoomProvider extends AutocompleteProvider {
constructor() {
super(ROOM_REGEX);
this.matcher = new QueryMatcher([], {
keys: ['displayedAlias', 'name'],
keys: ['displayedAlias', 'matchName'],
});
}
@ -56,16 +63,16 @@ export default class RoomProvider extends AutocompleteProvider {
const {command, range} = this.getCurrentCommand(query, selection, force);
if (command) {
// the only reason we need to do this is because Fuse only matches on properties
let matcherObjects = client.getVisibleRooms().filter(
(room) => !!room && !!getDisplayAliasForRoom(room),
).map((room) => {
return {
room: room,
name: room.name,
displayedAlias: getDisplayAliasForRoom(room),
};
});
let matcherObjects = client.getVisibleRooms().reduce((aliases, room) => {
if (room.getCanonicalAlias()) {
aliases = aliases.concat(matcherObject(room, room.getCanonicalAlias(), room.name));
}
if (room.getAltAliases().length) {
const altAliases = room.getAltAliases().map(alias => matcherObject(room, alias));
aliases = aliases.concat(altAliases);
}
return aliases;
}, []);
// Filter out any matches where the user will have also autocompleted new rooms
matcherObjects = matcherObjects.filter((r) => {
const tombstone = r.room.currentState.getStateEvents("m.room.tombstone", "");
@ -84,16 +91,16 @@ export default class RoomProvider extends AutocompleteProvider {
completions = _sortBy(completions, [
(c) => score(matchedString, c.displayedAlias),
(c) => c.displayedAlias.length,
]).map((room) => {
const displayAlias = getDisplayAliasForRoom(room.room) || room.roomId;
]);
completions = completions.map((room) => {
return {
completion: displayAlias,
completionId: displayAlias,
completion: room.displayedAlias,
completionId: room.room.roomId,
type: "room",
suffix: ' ',
href: makeRoomPermalink(displayAlias),
href: makeRoomPermalink(room.displayedAlias),
component: (
<PillCompletion initialComponent={<RoomAvatar width={24} height={24} room={room.room} />} title={room.name} description={displayAlias} />
<PillCompletion initialComponent={<RoomAvatar width={24} height={24} room={room.room} />} title={room.room.name} description={room.displayedAlias} />
),
range,
};

View file

@ -255,7 +255,7 @@ export class ContextMenu extends React.Component {
if (chevronFace === 'top' || chevronFace === 'bottom') {
chevronOffset.left = props.chevronOffset;
} else {
} else if (position.top !== undefined) {
const target = position.top;
// By default, no adjustment is made

View file

@ -95,8 +95,8 @@ const FilePanel = createReactClass({
// this could be made more general in the future or the filter logic
// could be fixed.
if (EventIndexPeg.get() !== null) {
client.on('Room.timeline', this.onRoomTimeline.bind(this));
client.on('Event.decrypted', this.onEventDecrypted.bind(this));
client.on('Room.timeline', this.onRoomTimeline);
client.on('Event.decrypted', this.onEventDecrypted);
}
},
@ -107,8 +107,8 @@ const FilePanel = createReactClass({
if (!MatrixClientPeg.get().isRoomEncrypted(this.props.roomId)) return;
if (EventIndexPeg.get() !== null) {
client.removeListener('Room.timeline', this.onRoomTimeline.bind(this));
client.removeListener('Event.decrypted', this.onEventDecrypted.bind(this));
client.removeListener('Room.timeline', this.onRoomTimeline);
client.removeListener('Event.decrypted', this.onEventDecrypted);
}
},

View file

@ -821,10 +821,10 @@ export default createReactClass({
{_t(
"Want more than a community? <a>Get your own server</a>", {},
{
a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener">{sub}</a>,
a: sub => <a href={hostingSignupLink} target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)}
<a href={hostingSignupLink} target="_blank" rel="noopener">
<a href={hostingSignupLink} target="_blank" rel="noreferrer noopener">
<img src={require("../../../res/img/external-link.svg")} width="11" height="10" alt='' />
</a>
</div>;

View file

@ -65,6 +65,7 @@ import { ThemeWatcher } from "../../theme";
import { storeRoomAliasInCache } from '../../RoomAliasCache';
import { defer } from "../../utils/promise";
import ToastStore from "../../stores/ToastStore";
import * as StorageManager from "../../utils/StorageManager";
/** constants for MatrixChat.state.view */
export const VIEWS = {
@ -1174,6 +1175,7 @@ export default createReactClass({
* Called when a new logged in session has started
*/
_onLoggedIn: async function() {
ThemeController.isLogin = false;
this.setStateForNewView({ view: VIEWS.LOGGED_IN });
if (MatrixClientPeg.currentUserIsJustRegistered()) {
MatrixClientPeg.setJustRegisteredUserId(null);
@ -1193,6 +1195,8 @@ export default createReactClass({
} else {
this._showScreenAfterLogin();
}
StorageManager.tryPersistStorage();
},
_showScreenAfterLogin: function() {
@ -1371,7 +1375,8 @@ export default createReactClass({
cancelButton: _t('Dismiss'),
onFinished: (confirmed) => {
if (confirmed) {
window.open(consentUri, '_blank');
const wnd = window.open(consentUri, '_blank');
wnd.opener = null;
}
},
}, null, true);

View file

@ -115,6 +115,7 @@ export default class MessagePanel extends React.Component {
// previous positions the read marker has been in, so we can
// display 'ghost' read markers that are animating away
ghostReadMarkers: [],
showTypingNotifications: SettingsStore.getValue("showTypingNotifications"),
};
// opaque readreceipt info for each userId; used by ReadReceiptMarker
@ -164,6 +165,9 @@ export default class MessagePanel extends React.Component {
this._readMarkerNode = createRef();
this._whoIsTyping = createRef();
this._scrollPanel = createRef();
this._showTypingNotificationsWatcherRef =
SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange);
}
componentDidMount() {
@ -172,6 +176,7 @@ export default class MessagePanel extends React.Component {
componentWillUnmount() {
this._isMounted = false;
SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef);
}
componentDidUpdate(prevProps, prevState) {
@ -184,6 +189,12 @@ export default class MessagePanel extends React.Component {
}
}
onShowTypingNotificationsChange = () => {
this.setState({
showTypingNotifications: SettingsStore.getValue("showTypingNotifications"),
});
};
/* get the DOM node representing the given event */
getNodeForEventId(eventId) {
if (!this.eventNodes) {
@ -402,10 +413,6 @@ export default class MessagePanel extends React.Component {
};
_getEventTiles() {
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
this.eventNodes = {};
let i;
@ -447,199 +454,48 @@ export default class MessagePanel extends React.Component {
this._readReceiptsByEvent = this._getReadReceiptsByShownEvent();
}
let grouper = null;
for (i = 0; i < this.props.events.length; i++) {
const mxEv = this.props.events[i];
const eventId = mxEv.getId();
const last = (mxEv === lastShownEvent);
// Wrap initial room creation events into an EventListSummary
// Grouping only events sent by the same user that sent the `m.room.create` and only until
// the first non-state event or membership event which is not regarding the sender of the `m.room.create` event
const shouldGroup = (ev) => {
if (ev.getType() === "m.room.member"
&& (ev.getStateKey() !== mxEv.getSender() || ev.getContent()["membership"] !== "join")) {
return false;
}
if (ev.isState() && ev.getSender() === mxEv.getSender()) {
return true;
}
return false;
};
// events that we include in the group but then eject out and place
// above the group.
const shouldEject = (ev) => {
if (ev.getType() === "m.room.encryption") return true;
return false;
};
if (mxEv.getType() === "m.room.create") {
let summaryReadMarker = null;
const ts1 = mxEv.getTs();
if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) {
const dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} /></li>;
ret.push(dateSeparator);
if (grouper) {
if (grouper.shouldGroup(mxEv)) {
grouper.add(mxEv);
continue;
} else {
// not part of group, so get the group tiles, close the
// group, and continue like a normal event
ret.push(...grouper.getTiles());
prevEvent = grouper.getNewPrevEvent();
grouper = null;
}
// If RM event is the first in the summary, append the RM after the summary
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(mxEv.getId());
// If this m.room.create event should be shown (room upgrade) then show it before the summary
if (this._shouldShowEvent(mxEv)) {
// pass in the mxEv as prevEvent as well so no extra DateSeparator is rendered
ret.push(...this._getTilesForEvent(mxEv, mxEv, false));
}
const summarisedEvents = []; // Don't add m.room.create here as we don't want it inside the summary
const ejectedEvents = [];
for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1];
// Ignore redacted/hidden member events
if (!this._shouldShowEvent(collapsedMxEv)) {
// If this hidden event is the RM and in or at end of a summary put RM after the summary.
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(collapsedMxEv.getId());
continue;
}
if (!shouldGroup(collapsedMxEv) || this._wantsDateSeparator(mxEv, collapsedMxEv.getDate())) {
break;
}
// If RM event is in the summary, mark it as such and the RM will be appended after the summary.
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(collapsedMxEv.getId());
if (shouldEject(collapsedMxEv)) {
ejectedEvents.push(collapsedMxEv);
} else {
summarisedEvents.push(collapsedMxEv);
}
}
// At this point, i = the index of the last event in the summary sequence
const eventTiles = summarisedEvents.map((e) => {
// In order to prevent DateSeparators from appearing in the expanded form
// of EventListSummary, render each member event as if the previous
// one was itself. This way, the timestamp of the previous event === the
// timestamp of the current event, and no DateSeparator is inserted.
return this._getTilesForEvent(e, e, e === lastShownEvent);
}).reduce((a, b) => a.concat(b), []);
for (const ejected of ejectedEvents) {
ret.push(...this._getTilesForEvent(mxEv, ejected, last));
}
// Get sender profile from the latest event in the summary as the m.room.create doesn't contain one
const ev = this.props.events[i];
ret.push(<EventListSummary
key="roomcreationsummary"
events={summarisedEvents}
onToggle={this._onHeightChanged} // Update scroll state
summaryMembers={[ev.sender]}
summaryText={_t("%(creator)s created and configured the room.", {
creator: ev.sender ? ev.sender.name : ev.getSender(),
})}
>
{ eventTiles }
</EventListSummary>);
if (summaryReadMarker) {
ret.push(summaryReadMarker);
}
prevEvent = mxEv;
continue;
}
const wantTile = this._shouldShowEvent(mxEv);
// Wrap consecutive member events in a ListSummary, ignore if redacted
if (isMembershipChange(mxEv) && wantTile) {
let summaryReadMarker = null;
const ts1 = mxEv.getTs();
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
// instead will allow new props to be provided. In turn, the shouldComponentUpdate
// method on MELS can be used to prevent unnecessary renderings.
//
// Whilst back-paginating with a MELS at the top of the panel, prevEvent will be null,
// so use the key "membereventlistsummary-initial". Otherwise, use the ID of the first
// membership event, which will not change during forward pagination.
const key = "membereventlistsummary-" + (prevEvent ? mxEv.getId() : "initial");
if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) {
const dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} /></li>;
ret.push(dateSeparator);
for (const Grouper of groupers) {
if (Grouper.canStartGroup(this, mxEv)) {
grouper = new Grouper(this, mxEv, prevEvent, lastShownEvent);
}
// If RM event is the first in the MELS, append the RM after MELS
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(mxEv.getId());
const summarisedEvents = [mxEv];
for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1];
// Ignore redacted/hidden member events
if (!this._shouldShowEvent(collapsedMxEv)) {
// If this hidden event is the RM and in or at end of a MELS put RM after MELS.
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(collapsedMxEv.getId());
continue;
}
if (!isMembershipChange(collapsedMxEv) ||
this._wantsDateSeparator(mxEv, collapsedMxEv.getDate())) {
break;
}
// If RM event is in MELS mark it as such and the RM will be appended after MELS.
summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(collapsedMxEv.getId());
summarisedEvents.push(collapsedMxEv);
}
let highlightInMels = false;
// At this point, i = the index of the last event in the summary sequence
let eventTiles = summarisedEvents.map((e) => {
if (e.getId() === this.props.highlightedEventId) {
highlightInMels = true;
}
// In order to prevent DateSeparators from appearing in the expanded form
// of MemberEventListSummary, render each member event as if the previous
// one was itself. This way, the timestamp of the previous event === the
// timestamp of the current event, and no DateSeparator is inserted.
return this._getTilesForEvent(e, e, e === lastShownEvent);
}).reduce((a, b) => a.concat(b), []);
if (eventTiles.length === 0) {
eventTiles = null;
}
ret.push(<MemberEventListSummary key={key}
events={summarisedEvents}
onToggle={this._onHeightChanged} // Update scroll state
startExpanded={highlightInMels}
>
{ eventTiles }
</MemberEventListSummary>);
if (summaryReadMarker) {
ret.push(summaryReadMarker);
}
prevEvent = mxEv;
continue;
}
if (!grouper) {
const wantTile = this._shouldShowEvent(mxEv);
if (wantTile) {
// make sure we unpack the array returned by _getTilesForEvent,
// otherwise react will auto-generate keys and we will end up
// replacing all of the DOM elements every time we paginate.
ret.push(...this._getTilesForEvent(prevEvent, mxEv, last));
prevEvent = mxEv;
}
if (wantTile) {
// make sure we unpack the array returned by _getTilesForEvent,
// otherwise react will auto-generate keys and we will end up
// replacing all of the DOM elements every time we paginate.
ret.push(...this._getTilesForEvent(prevEvent, mxEv, last));
prevEvent = mxEv;
const readMarker = this._readMarkerForEvent(eventId, i >= lastShownNonLocalEchoIndex);
if (readMarker) ret.push(readMarker);
}
}
const readMarker = this._readMarkerForEvent(eventId, i >= lastShownNonLocalEchoIndex);
if (readMarker) ret.push(readMarker);
if (grouper) {
ret.push(...grouper.getTiles());
}
return ret;
@ -921,7 +777,7 @@ export default class MessagePanel extends React.Component {
);
let whoIsTyping;
if (this.props.room && !this.props.tileShape) {
if (this.props.room && !this.props.tileShape && this.state.showTypingNotifications) {
whoIsTyping = (<WhoIsTypingTile
room={this.props.room}
onShown={this._onTypingShown}
@ -950,3 +806,222 @@ export default class MessagePanel extends React.Component {
);
}
}
/* Grouper classes determine when events can be grouped together in a summary.
* Groupers should have the following methods:
* - canStartGroup (static): determines if a new group should be started with the
* given event
* - shouldGroup: determines if the given event should be added to an existing group
* - add: adds an event to an existing group (should only be called if shouldGroup
* return true)
* - getTiles: returns the tiles that represent the group
* - getNewPrevEvent: returns the event that should be used as the new prevEvent
* when determining things such as whether a date separator is necessary
*/
// Wrap initial room creation events into an EventListSummary
// Grouping only events sent by the same user that sent the `m.room.create` and only until
// the first non-state event or membership event which is not regarding the sender of the `m.room.create` event
class CreationGrouper {
static canStartGroup = function(panel, ev) {
return ev.getType() === "m.room.create";
};
constructor(panel, createEvent, prevEvent, lastShownEvent) {
this.panel = panel;
this.createEvent = createEvent;
this.prevEvent = prevEvent;
this.lastShownEvent = lastShownEvent;
this.events = [];
// events that we include in the group but then eject out and place
// above the group.
this.ejectedEvents = [];
this.readMarker = panel._readMarkerForEvent(createEvent.getId());
}
shouldGroup(ev) {
const panel = this.panel;
const createEvent = this.createEvent;
if (!panel._shouldShowEvent(ev)) {
this.readMarker = this.readMarker || panel._readMarkerForEvent(ev.getId());
return true;
}
if (panel._wantsDateSeparator(this.createEvent, ev.getDate())) {
return false;
}
if (ev.getType() === "m.room.member"
&& (ev.getStateKey() !== createEvent.getSender() || ev.getContent()["membership"] !== "join")) {
return false;
}
if (ev.isState() && ev.getSender() === createEvent.getSender()) {
return true;
}
return false;
}
add(ev) {
const panel = this.panel;
this.readMarker = this.readMarker || panel._readMarkerForEvent(ev.getId());
if (!panel._shouldShowEvent(ev)) {
return;
}
if (ev.getType() === "m.room.encryption") {
this.ejectedEvents.push(ev);
} else {
this.events.push(ev);
}
}
getTiles() {
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
const panel = this.panel;
const ret = [];
const createEvent = this.createEvent;
const lastShownEvent = this.lastShownEvent;
if (panel._wantsDateSeparator(this.prevEvent, createEvent.getDate())) {
const ts = createEvent.getTs();
ret.push(
<li key={ts+'~'}><DateSeparator key={ts+'~'} ts={ts} /></li>,
);
}
// If this m.room.create event should be shown (room upgrade) then show it before the summary
if (panel._shouldShowEvent(createEvent)) {
// pass in the createEvent as prevEvent as well so no extra DateSeparator is rendered
ret.push(...panel._getTilesForEvent(createEvent, createEvent, false));
}
for (const ejected of this.ejectedEvents) {
ret.push(...panel._getTilesForEvent(
createEvent, ejected, createEvent === lastShownEvent,
));
}
const eventTiles = this.events.map((e) => {
// In order to prevent DateSeparators from appearing in the expanded form
// of EventListSummary, render each member event as if the previous
// one was itself. This way, the timestamp of the previous event === the
// timestamp of the current event, and no DateSeparator is inserted.
return panel._getTilesForEvent(e, e, e === lastShownEvent);
}).reduce((a, b) => a.concat(b), []);
// Get sender profile from the latest event in the summary as the m.room.create doesn't contain one
const ev = this.events[this.events.length - 1];
ret.push(
<EventListSummary
key="roomcreationsummary"
events={this.events}
onToggle={panel._onHeightChanged} // Update scroll state
summaryMembers={[ev.sender]}
summaryText={_t("%(creator)s created and configured the room.", {
creator: ev.sender ? ev.sender.name : ev.getSender(),
})}
>
{ eventTiles }
</EventListSummary>,
);
if (this.readMarker) {
ret.push(this.readMarker);
}
return ret;
}
getNewPrevEvent() {
return this.createEvent;
}
}
// Wrap consecutive member events in a ListSummary, ignore if redacted
class MemberGrouper {
static canStartGroup = function(panel, ev) {
return panel._shouldShowEvent(ev) && isMembershipChange(ev);
}
constructor(panel, ev, prevEvent, lastShownEvent) {
this.panel = panel;
this.readMarker = panel._readMarkerForEvent(ev.getId());
this.events = [ev];
this.prevEvent = prevEvent;
this.lastShownEvent = lastShownEvent;
}
shouldGroup(ev) {
return isMembershipChange(ev);
}
add(ev) {
this.readMarker = this.readMarker || this.panel._readMarkerForEvent(ev.getId());
this.events.push(ev);
}
getTiles() {
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
const panel = this.panel;
const lastShownEvent = this.lastShownEvent;
const ret = [];
if (panel._wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
const ts = this.events[0].getTs();
ret.push(
<li key={ts+'~'}><DateSeparator key={ts+'~'} ts={ts} /></li>,
);
}
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
// instead will allow new props to be provided. In turn, the shouldComponentUpdate
// method on MELS can be used to prevent unnecessary renderings.
//
// Whilst back-paginating with a MELS at the top of the panel, prevEvent will be null,
// so use the key "membereventlistsummary-initial". Otherwise, use the ID of the first
// membership event, which will not change during forward pagination.
const key = "membereventlistsummary-" + (
this.prevEvent ? this.events[0].getId() : "initial"
);
let highlightInMels;
let eventTiles = this.events.map((e) => {
if (e.getId() === panel.props.highlightedEventId) {
highlightInMels = true;
}
// In order to prevent DateSeparators from appearing in the expanded form
// of MemberEventListSummary, render each member event as if the previous
// one was itself. This way, the timestamp of the previous event === the
// timestamp of the current event, and no DateSeparator is inserted.
return panel._getTilesForEvent(e, e, e === lastShownEvent);
}).reduce((a, b) => a.concat(b), []);
if (eventTiles.length === 0) {
eventTiles = null;
}
ret.push(
<MemberEventListSummary key={key}
events={this.events}
onToggle={panel._onHeightChanged} // Update scroll state
startExpanded={highlightInMels}
>
{ eventTiles }
</MemberEventListSummary>,
);
if (this.readMarker) {
ret.push(this.readMarker);
}
return ret;
}
getNewPrevEvent() {
return this.events[0];
}
}
// all the grouper classes that we use
const groupers = [CreationGrouper, MemberGrouper];

View file

@ -92,6 +92,7 @@ export default class RightPanel extends React.Component {
// not mounted in time to get the dispatch.
// Until then, let this code serve as a warning from history.
if (
rps.roomPanelPhaseParams.member &&
userForPanel.userId === rps.roomPanelPhaseParams.member.userId &&
rps.roomPanelPhaseParams.verificationRequest
) {
@ -285,7 +286,7 @@ export default class RightPanel extends React.Component {
});
return (
<aside className={classes}>
<aside className={classes} id="mx_RightPanel">
{ panel }
</aside>
);

View file

@ -460,8 +460,6 @@ export default createReactClass({
// (We could use isMounted, but facebook have deprecated that.)
this.unmounted = true;
SettingsStore.unwatchSetting(this._ciderWatcherRef);
// update the scroll map before we get unmounted
if (this.state.roomId) {
RoomScrollStateStore.setScrollState(this.state.roomId, this._getScrollState());
@ -811,7 +809,9 @@ export default createReactClass({
debuglog("e2e verified", verified, "unverified", unverified);
/* Check all verified user devices. */
for (const userId of [...verified, cli.getUserId()]) {
/* Don't alarm if no other users are verified */
const targets = (verified.length > 0) ? [...verified, cli.getUserId()] : verified;
for (const userId of targets) {
const devices = await cli.getStoredDevicesForUser(userId);
const anyDeviceNotVerified = devices.some(({deviceId}) => {
return !cli.checkDeviceTrust(userId, deviceId).isVerified();

View file

@ -160,6 +160,7 @@ export default createReactClass({
onKeyDown={ this._onKeyDown }
onBlur={this._onBlur}
placeholder={ placeholder }
autoComplete="off"
/>
{ clearButton }
</div>

View file

@ -83,12 +83,13 @@ export default class CompleteSecurity extends React.Component {
}
}
onVerificationRequest = (request) => {
onVerificationRequest = async (request) => {
if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;
if (this.state.verificationRequest) {
this.state.verificationRequest.off("change", this.onVerificationRequestChange);
}
await request.accept();
request.on("change", this.onVerificationRequestChange);
this.setState({
verificationRequest: request,
@ -138,9 +139,12 @@ export default class CompleteSecurity extends React.Component {
let body;
if (this.state.verificationRequest) {
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
body = <IncomingSasDialog verifier={this.state.verificationRequest.verifier}
onFinished={this.props.onFinished}
const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel");
body = <EncryptionPanel
layout="dialog"
verificationRequest={this.state.verificationRequest}
onClose={this.props.onFinished}
member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
/>;
} else if (phase === PHASE_INTRO) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;

View file

@ -481,7 +481,7 @@ export default createReactClass({
"Either use HTTPS or <a>enable unsafe scripts</a>.", {},
{
'a': (sub) => {
return <a target="_blank" rel="noopener"
return <a target="_blank" rel="noreferrer noopener"
href="https://www.google.com/search?&q=enable%20unsafe%20scripts"
>
{ sub }
@ -496,11 +496,10 @@ export default createReactClass({
"<a>homeserver's SSL certificate</a> is trusted, and that a browser extension " +
"is not blocking requests.", {},
{
'a': (sub) => {
return <a target="_blank" rel="noopener" href={this.props.serverConfig.hsUrl}>
'a': (sub) =>
<a target="_blank" rel="noreferrer noopener" href={this.props.serverConfig.hsUrl}>
{ sub }
</a>;
},
</a>,
},
) }
</span>;

View file

@ -26,7 +26,7 @@ export default createReactClass({
render: function() {
return (
<div className="mx_AuthFooter">
<a href="https://matrix.org" target="_blank" rel="noopener">{ _t("powered by Matrix") }</a>
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t("powered by Matrix") }</a>
</div>
);
},

View file

@ -61,13 +61,9 @@ export default createReactClass({
} else {
console.log("Loading recaptcha script...");
window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();};
let protocol = global.location.protocol;
if (protocol !== "http:") {
protocol = "https:";
}
const scriptTag = document.createElement('script');
scriptTag.setAttribute(
'src', `${protocol}//www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`,
'src', `https://www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`,
);
this._recaptchaContainer.current.appendChild(scriptTag);
}

View file

@ -331,7 +331,7 @@ export const TermsAuthEntry = createReactClass({
checkboxes.push(
<label key={"policy_checkbox_" + policy.id} className="mx_InteractiveAuthEntryComponents_termsPolicy">
<input type="checkbox" onChange={() => this._togglePolicy(policy.id)} checked={checked} />
<a href={policy.url} target="_blank" rel="noopener">{ policy.name }</a>
<a href={policy.url} target="_blank" rel="noreferrer noopener">{ policy.name }</a>
</label>,
);
}
@ -604,6 +604,7 @@ export const FallbackAuthEntry = createReactClass({
this.props.authSessionId,
);
this._popupWindow = window.open(url);
this._popupWindow.opener = null;
},
_onReceiveMessage: function(event) {

View file

@ -99,7 +99,7 @@ export default class ModularServerConfig extends ServerConfig {
"Enter the location of your Modular homeserver. It may use your own " +
"domain name or be a subdomain of <a>modular.im</a>.",
{}, {
a: sub => <a href={MODULAR_URL} target="_blank" rel="noopener">
a: sub => <a href={MODULAR_URL} target="_blank" rel="noreferrer noopener">
{sub}
</a>,
},

View file

@ -486,6 +486,7 @@ export default createReactClass({
id="mx_RegistrationForm_password"
ref={field => this[FIELD_PASSWORD] = field}
type="password"
autoComplete="new-password"
label={_t("Password")}
value={this.state.password}
onChange={this.onPasswordChange}
@ -499,6 +500,7 @@ export default createReactClass({
id="mx_RegistrationForm_passwordConfirm"
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
type="password"
autoComplete="new-password"
label={_t("Confirm")}
value={this.state.passwordConfirm}
onChange={this.onPasswordConfirmChange}

View file

@ -274,15 +274,13 @@ export default class ServerConfig extends React.PureComponent {
: null;
return (
<div className="mx_ServerConfig">
<form className="mx_ServerConfig" onSubmit={this.onSubmit} autoComplete="off">
<h3>{_t("Other servers")}</h3>
{errorText}
{this._renderHomeserverSection()}
{this._renderIdentityServerSection()}
<form onSubmit={this.onSubmit} autoComplete="off" action={null}>
{submitButton}
</form>
</div>
{submitButton}
</form>
);
}
}

View file

@ -46,7 +46,7 @@ export const TYPES = {
label: () => _t('Premium'),
logo: () => <img src={require('../../../../res/img/modular-bw-logo.svg')} />,
description: () => _t('Premium hosting for organisations <a>Learn more</a>', {}, {
a: sub => <a href={MODULAR_URL} target="_blank" rel="noopener">
a: sub => <a href={MODULAR_URL} target="_blank" rel="noreferrer noopener">
{sub}
</a>,
}),

View file

@ -414,11 +414,16 @@ export default createReactClass({
}
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
const permalinkButton = (
<MenuItem className="mx_MessageContextMenu_field">
<a href={permalink} target="_blank" rel="noopener" onClick={this.onPermalinkClick} tabIndex={-1}>
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
? _t('Share Permalink') : _t('Share Message') }
</a>
<MenuItem
element="a"
className="mx_MessageContextMenu_field"
onClick={this.onPermalinkClick}
href={permalink}
target="_blank"
rel="noreferrer noopener"
>
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
? _t('Share Permalink') : _t('Share Message') }
</MenuItem>
);
@ -436,16 +441,15 @@ export default createReactClass({
isUrlPermitted(mxEvent.event.content.external_url)
) {
externalURLButton = (
<MenuItem className="mx_MessageContextMenu_field">
<a
href={mxEvent.event.content.external_url}
target="_blank"
rel="noopener"
onClick={this.closeMenu}
tabIndex={-1}
>
{ _t('Source URL') }
</a>
<MenuItem
element="a"
className="mx_MessageContextMenu_field"
target="_blank"
rel="noreferrer noopener"
onClick={this.closeMenu}
href={mxEvent.event.content.external_url}
>
{ _t('Source URL') }
</MenuItem>
);
}

View file

@ -68,10 +68,11 @@ export default class TopLeftMenu extends React.Component {
{_t(
"<a>Upgrade</a> to your own domain", {},
{
a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener" tabIndex={-1}>{sub}</a>,
a: sub =>
<a href={hostingSignupLink} target="_blank" rel="noreferrer noopener" tabIndex={-1}>{sub}</a>,
},
)}
<a href={hostingSignupLink} target="_blank" rel="noopener" role="presentation" aria-hidden={true} tabIndex={-1}>
<a href={hostingSignupLink} target="_blank" rel="noreferrer noopener" role="presentation" aria-hidden={true} tabIndex={-1}>
<img src={require("../../../../res/img/external-link.svg")} width="11" height="10" alt='' />
</a>
</div>;

View file

@ -52,7 +52,7 @@ export default class ChangelogDialog extends React.Component {
_elementsForCommit(commit) {
return (
<li key={commit.sha} className="mx_ChangelogDialog_li">
<a href={commit.html_url} target="_blank" rel="noopener">
<a href={commit.html_url} target="_blank" rel="noreferrer noopener">
{commit.commit.message.split('\n')[0]}
</a>
</li>

View file

@ -0,0 +1,65 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import * as sdk from "../../../index";
export default class ConfirmDestroyCrossSigningDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
};
_onConfirm = () => {
this.props.onFinished(true);
};
_onDecline = () => {
this.props.onFinished(false);
};
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (
<BaseDialog
className='mx_ConfirmDestroyCrossSigningDialog'
hasCancel={true}
onFinished={this.props.onFinished}
title={_t("Destroy cross-signing keys?")}>
<div className='mx_ConfirmDestroyCrossSigningDialog_content'>
<p>
{_t(
"Deleting cross-signing keys is permanent. " +
"Anyone you have verified with will see security alerts. " +
"You almost certainly don't want to do this, unless " +
"you've lost every device you can cross-sign from.",
)}
</p>
</div>
<DialogButtons
primaryButton={_t("Clear cross-signing keys")}
onPrimaryButtonClick={this._onConfirm}
primaryButtonClass="danger"
cancelButton={_t("Cancel")}
onCancel={this._onDecline}
/>
</BaseDialog>
);
}
}

View file

@ -118,6 +118,7 @@ export default class DeactivateAccountDialog extends React.Component {
const Field = sdk.getComponent('elements.Field');
// this is on purpose not a <form /> to prevent Enter triggering submission, to further prevent accidents
return (
<BaseDialog className="mx_DeactivateAccountDialog"
onFinished={this.props.onFinished}

View file

@ -27,16 +27,19 @@ import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {ensureDMExists} from "../../../createRoom";
import dis from "../../../dispatcher";
import SettingsStore from '../../../settings/SettingsStore';
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import VerificationQREmojiOptions from "../verification/VerificationQREmojiOptions";
const MODE_LEGACY = 'legacy';
const MODE_SAS = 'sas';
const PHASE_START = 0;
const PHASE_WAIT_FOR_PARTNER_TO_ACCEPT = 1;
const PHASE_SHOW_SAS = 2;
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 3;
const PHASE_VERIFIED = 4;
const PHASE_CANCELLED = 5;
const PHASE_PICK_VERIFICATION_OPTION = 2;
const PHASE_SHOW_SAS = 3;
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 4;
const PHASE_VERIFIED = 5;
const PHASE_CANCELLED = 6;
export default class DeviceVerifyDialog extends React.Component {
static propTypes = {
@ -49,6 +52,7 @@ export default class DeviceVerifyDialog extends React.Component {
super();
this._verifier = null;
this._showSasEvent = null;
this._request = null;
this.state = {
phase: PHASE_START,
mode: MODE_SAS,
@ -80,6 +84,25 @@ export default class DeviceVerifyDialog extends React.Component {
this.props.onFinished(false);
}
_onUseSasClick = async () => {
try {
this._verifier = this._request.beginKeyVerification(verificationMethods.SAS);
this._verifier.on('show_sas', this._onVerifierShowSas);
// throws upon cancellation
await this._verifier.verify();
this.setState({phase: PHASE_VERIFIED});
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
this._verifier = null;
} catch (e) {
console.log("Verification failed", e);
this.setState({
phase: PHASE_CANCELLED,
});
this._verifier = null;
this._request = null;
}
};
_onLegacyFinished = (confirm) => {
if (confirm) {
MatrixClientPeg.get().setDeviceVerified(
@ -100,7 +123,7 @@ export default class DeviceVerifyDialog extends React.Component {
const roomId = await ensureDMExistsAndOpen(this.props.userId);
// throws upon cancellation before having started
const request = await client.requestVerificationDM(
this.props.userId, roomId, [verificationMethods.SAS],
this.props.userId, roomId,
);
await request.waitFor(r => r.ready || r.started);
if (request.ready) {
@ -108,11 +131,21 @@ export default class DeviceVerifyDialog extends React.Component {
} else {
this._verifier = request.verifier;
}
} else if (verifyingOwnDevice && SettingsStore.isFeatureEnabled("feature_cross_signing")) {
this._request = await client.requestVerification(this.props.userId, [
verificationMethods.SAS,
SHOW_QR_CODE_METHOD,
verificationMethods.RECIPROCATE_QR_CODE,
]);
await this._request.waitFor(r => r.ready || r.started);
this.setState({phase: PHASE_PICK_VERIFICATION_OPTION});
} else {
this._verifier = client.beginKeyVerification(
verificationMethods.SAS, this.props.userId, this.props.device.deviceId,
);
}
if (!this._verifier) return;
this._verifier.on('show_sas', this._onVerifierShowSas);
// throws upon cancellation
await this._verifier.verify();
@ -150,10 +183,13 @@ export default class DeviceVerifyDialog extends React.Component {
let body;
switch (this.state.phase) {
case PHASE_START:
body = this._renderSasVerificationPhaseStart();
body = this._renderVerificationPhaseStart();
break;
case PHASE_WAIT_FOR_PARTNER_TO_ACCEPT:
body = this._renderSasVerificationPhaseWaitAccept();
body = this._renderVerificationPhaseWaitAccept();
break;
case PHASE_PICK_VERIFICATION_OPTION:
body = this._renderVerificationPhasePick();
break;
case PHASE_SHOW_SAS:
body = this._renderSasVerificationPhaseShowSas();
@ -162,10 +198,10 @@ export default class DeviceVerifyDialog extends React.Component {
body = this._renderSasVerificationPhaseWaitForPartnerToConfirm();
break;
case PHASE_VERIFIED:
body = this._renderSasVerificationPhaseVerified();
body = this._renderVerificationPhaseVerified();
break;
case PHASE_CANCELLED:
body = this._renderSasVerificationPhaseCancelled();
body = this._renderVerificationPhaseCancelled();
break;
}
@ -180,7 +216,7 @@ export default class DeviceVerifyDialog extends React.Component {
);
}
_renderSasVerificationPhaseStart() {
_renderVerificationPhaseStart() {
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (
@ -206,7 +242,7 @@ export default class DeviceVerifyDialog extends React.Component {
);
}
_renderSasVerificationPhaseWaitAccept() {
_renderVerificationPhaseWaitAccept() {
const Spinner = sdk.getComponent("views.elements.Spinner");
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
@ -227,6 +263,14 @@ export default class DeviceVerifyDialog extends React.Component {
);
}
_renderVerificationPhasePick() {
return <VerificationQREmojiOptions
request={this._request}
onCancel={this._onCancelClick}
onStartEmoji={this._onUseSasClick}
/>;
}
_renderSasVerificationPhaseShowSas() {
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
return <VerificationShowSas
@ -234,6 +278,7 @@ export default class DeviceVerifyDialog extends React.Component {
onCancel={this._onCancelClick}
onDone={this._onSasMatchesClick}
isSelf={MatrixClientPeg.get().getUserId() === this.props.userId}
onStartEmoji={this._onUseSasClick}
/>;
}
@ -247,12 +292,12 @@ export default class DeviceVerifyDialog extends React.Component {
</div>;
}
_renderSasVerificationPhaseVerified() {
_renderVerificationPhaseVerified() {
const VerificationComplete = sdk.getComponent('views.verification.VerificationComplete');
return <VerificationComplete onDone={this._onVerifiedDoneClick} />;
}
_renderSasVerificationPhaseCancelled() {
_renderVerificationPhaseCancelled() {
const VerificationCancelled = sdk.getComponent('views.verification.VerificationCancelled');
return <VerificationCancelled onDone={this._onCancelClick} />;
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, {useState, useEffect} from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import SyntaxHighlight from '../elements/SyntaxHighlight';
@ -22,6 +22,16 @@ import { _t } from '../../../languageHandler';
import { Room } from "matrix-js-sdk";
import Field from "../elements/Field";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import {
PHASE_UNSENT,
PHASE_REQUESTED,
PHASE_READY,
PHASE_DONE,
PHASE_STARTED,
PHASE_CANCELLED,
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
class GenericEditor extends React.PureComponent {
// static propTypes = {onBack: PropTypes.func.isRequired};
@ -605,12 +615,97 @@ class ServersInRoomList extends React.PureComponent {
}
}
const PHASE_MAP = {
[PHASE_UNSENT]: "unsent",
[PHASE_REQUESTED]: "requested",
[PHASE_READY]: "ready",
[PHASE_DONE]: "done",
[PHASE_STARTED]: "started",
[PHASE_CANCELLED]: "cancelled",
};
function VerificationRequest({txnId, request}) {
const [, updateState] = useState();
const [timeout, setRequestTimeout] = useState(request.timeout);
/* Re-render if something changes state */
useEventEmitter(request, "change", updateState);
/* Keep re-rendering if there's a timeout */
useEffect(() => {
if (request.timeout == 0) return;
/* Note that request.timeout is a getter, so its value changes */
const id = setInterval(() => {
setRequestTimeout(request.timeout);
}, 500);
return () => { clearInterval(id); };
}, [request]);
return (<div className="mx_DevTools_VerificationRequest">
<dl>
<dt>Transaction</dt>
<dd>{txnId}</dd>
<dt>Phase</dt>
<dd>{PHASE_MAP[request.phase] || request.phase}</dd>
<dt>Timeout</dt>
<dd>{Math.floor(timeout / 1000)}</dd>
<dt>Methods</dt>
<dd>{request.methods && request.methods.join(", ")}</dd>
<dt>requestingUserId</dt>
<dd>{request.requestingUserId}</dd>
<dt>observeOnly</dt>
<dd>{JSON.stringify(request.observeOnly)}</dd>
</dl>
</div>);
}
class VerificationExplorer extends React.Component {
static getLabel() {
return _t("Verification Requests");
}
/* Ensure this.context is the cli */
static contextType = MatrixClientContext;
onNewRequest = () => {
this.forceUpdate();
}
componentDidMount() {
const cli = this.context;
cli.on("crypto.verification.request", this.onNewRequest);
}
componentWillUnmount() {
const cli = this.context;
cli.off("crypto.verification.request", this.onNewRequest);
}
render() {
const cli = this.context;
const room = this.props.room;
const inRoomChannel = cli._crypto._inRoomVerificationRequests;
const inRoomRequests = (inRoomChannel._requestsByRoomId || new Map()).get(room.roomId) || new Map();
return (<div>
<div className="mx_Dialog_content">
{Array.from(inRoomRequests.entries()).reverse().map(([txnId, request]) =>
<VerificationRequest txnId={txnId} request={request} key={txnId} />,
)}
</div>
</div>);
}
}
const Entries = [
SendCustomEvent,
RoomStateExplorer,
SendAccountData,
AccountDataExplorer,
ServersInRoomList,
VerificationExplorer,
];
export default class DevtoolsDialog extends React.PureComponent {

View file

@ -31,7 +31,7 @@ import dis from "../../../dispatcher";
import IdentityAuthClient from "../../../IdentityAuthClient";
import Modal from "../../../Modal";
import {humanizeTime} from "../../../utils/humanize";
import createRoom from "../../../createRoom";
import createRoom, {canEncryptToAllUsers} from "../../../createRoom";
import {inviteMultipleToRoom} from "../../../RoomInvite";
import SettingsStore from '../../../settings/SettingsStore';
@ -512,9 +512,27 @@ export default class InviteDialog extends React.PureComponent {
return false;
}
_convertFilter(): Member[] {
// Check to see if there's anything to convert first
if (!this.state.filterText || !this.state.filterText.includes('@')) return this.state.targets || [];
let newMember: Member;
if (this.state.filterText.startsWith('@')) {
// Assume mxid
newMember = new DirectoryMember({user_id: this.state.filterText, display_name: null, avatar_url: null});
} else {
// Assume email
newMember = new ThreepidMember(this.state.filterText);
}
const newTargets = [...(this.state.targets || []), newMember];
this.setState({targets: newTargets, filterText: ''});
return newTargets;
}
_startDm = async () => {
this.setState({busy: true});
const targetIds = this.state.targets.map(t => t.userId);
const targets = this._convertFilter();
const targetIds = targets.map(t => t.userId);
// Check if there is already a DM with these people and reuse it if possible.
const existingRoom = DMRoomMap.shared().getDMRoomForIdentifiers(targetIds);
@ -535,11 +553,7 @@ export default class InviteDialog extends React.PureComponent {
// Check whether all users have uploaded device keys before.
// If so, enable encryption in the new room.
const client = MatrixClientPeg.get();
const usersToDevicesMap = await client.downloadKeys(targetIds);
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
return Object.keys(devices).length > 0;
});
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
if (allHaveDeviceKeys) {
createRoomOptions.encryption = true;
}
@ -548,9 +562,12 @@ export default class InviteDialog extends React.PureComponent {
// Check if it's a traditional DM and create the room if required.
// TODO: [Canonical DMs] Remove this check and instead just create the multi-person DM
let createRoomPromise = Promise.resolve();
if (targetIds.length === 1) {
const isSelf = targetIds.length === 1 && targetIds[0] === MatrixClientPeg.get().getUserId();
if (targetIds.length === 1 && !isSelf) {
createRoomOptions.dmUserId = targetIds[0];
createRoomPromise = createRoom(createRoomOptions);
} else if (isSelf) {
createRoomPromise = createRoom(createRoomOptions);
} else {
// Create a boring room and try to invite the targets manually.
createRoomPromise = createRoom(createRoomOptions).then(roomId => {
@ -577,7 +594,9 @@ export default class InviteDialog extends React.PureComponent {
_inviteUsers = () => {
this.setState({busy: true});
const targetIds = this.state.targets.map(t => t.userId);
this._convertFilter();
const targets = this._convertFilter();
const targetIds = targets.map(t => t.userId);
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
if (!room) {
@ -634,13 +653,14 @@ export default class InviteDialog extends React.PureComponent {
// While we're here, try and autocomplete a search result for the mxid itself
// if there's no matches (and the input looks like a mxid).
if (term[0] === '@' && term.indexOf(':') > 1 && r.results.length === 0) {
if (term[0] === '@' && term.indexOf(':') > 1) {
try {
const profile = await MatrixClientPeg.get().getProfileInfo(term);
if (profile) {
// If we have a profile, we have enough information to assume that
// the mxid can be invited - add it to the list
r.results.push({
// the mxid can be invited - add it to the list. We stick it at the
// top so it is most obviously presented to the user.
r.results.splice(0, 0, {
user_id: term,
display_name: profile['displayname'],
avatar_url: profile['avatar_url'],
@ -649,6 +669,14 @@ export default class InviteDialog extends React.PureComponent {
} catch (e) {
console.warn("Non-fatal error trying to make an invite for a user ID");
console.warn(e);
// Add a result anyways, just without a profile. We stick it at the
// top so it is most obviously presented to the user.
r.results.splice(0, 0, {
user_id: term,
display_name: term,
avatar_url: null,
});
}
}
@ -773,7 +801,7 @@ export default class InviteDialog extends React.PureComponent {
];
const toAdd = [];
const failed = [];
const potentialAddresses = text.split(/[\s,]+/);
const potentialAddresses = text.split(/[\s,]+/).map(p => p.trim()).filter(p => !!p); // filter empty strings
for (const address of potentialAddresses) {
const member = possibleMembers.find(m => m.userId === address);
if (member) {
@ -1018,7 +1046,7 @@ export default class InviteDialog extends React.PureComponent {
"If you can't find someone, ask them for their username, share your " +
"username (%(userId)s) or <a>profile link</a>.",
{userId},
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>},
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noreferrer noopener" target="_blank">{sub}</a>},
);
buttonText = _t("Go");
goButtonFn = this._startDm;
@ -1027,12 +1055,17 @@ export default class InviteDialog extends React.PureComponent {
helpText = _t(
"If you can't find someone, ask them for their username (e.g. @user:server.com) or " +
"<a>share this room</a>.", {},
{a: (sub) => <a href={makeRoomPermalink(this.props.roomId)} rel="noopener" target="_blank">{sub}</a>},
{
a: (sub) =>
<a href={makeRoomPermalink(this.props.roomId)} rel="noreferrer noopener" target="_blank">{sub}</a>,
},
);
buttonText = _t("Invite");
goButtonFn = this._inviteUsers;
}
const hasSelection = this.state.targets.length > 0
|| (this.state.filterText && this.state.filterText.includes('@'));
return (
<BaseDialog
className='mx_InviteDialog'
@ -1049,7 +1082,7 @@ export default class InviteDialog extends React.PureComponent {
kind="primary"
onClick={goButtonFn}
className='mx_InviteDialog_goButton'
disabled={this.state.busy}
disabled={this.state.busy || !hasSelection}
>
{buttonText}
</AccessibleButton>

View file

@ -19,9 +19,10 @@ import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
import { replaceableComponent } from '../../../utils/replaceableComponent';
import DeviceVerifyDialog from './DeviceVerifyDialog';
import VerificationRequestDialog from './VerificationRequestDialog';
import BaseDialog from './BaseDialog';
import DialogButtons from '../elements/DialogButtons';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
@replaceableComponent("views.dialogs.NewSessionReviewDialog")
export default class NewSessionReviewDialog extends React.PureComponent {
@ -35,12 +36,18 @@ export default class NewSessionReviewDialog extends React.PureComponent {
this.props.onFinished(false);
}
onContinueClick = () => {
onContinueClick = async () => {
const { userId, device } = this.props;
Modal.createTrackedDialog('New Session Verification', 'Starting dialog', DeviceVerifyDialog, {
const cli = MatrixClientPeg.get();
const request = await cli.requestVerification(
userId,
device,
}, null, /* priority = */ false, /* static = */ true);
[device.deviceId],
);
this.props.onFinished(true);
Modal.createTrackedDialog('New Session Verification', 'Starting dialog', VerificationRequestDialog, {
verificationRequest: request,
});
}
render() {

View file

@ -31,6 +31,7 @@ export default createReactClass({
danger: PropTypes.bool,
focus: PropTypes.bool,
onFinished: PropTypes.func.isRequired,
headerImage: PropTypes.string,
},
getDefaultProps: function() {
@ -63,6 +64,7 @@ export default createReactClass({
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished}
title={this.props.title}
contentId='mx_Dialog_content'
headerImage={this.props.headerImage}
hasCancel={this.props.hasCancelButton}
>
<div className="mx_Dialog_content" id='mx_Dialog_content'>

View file

@ -218,7 +218,7 @@ export default class ShareDialog extends React.Component {
</div>
<div className="mx_ShareDialog_social_container">
{
socials.map((social) => <a rel="noopener"
socials.map((social) => <a rel="noreferrer noopener"
target="_blank"
key={social.name}
name={social.name}

View file

@ -135,7 +135,7 @@ export default class TermsDialog extends React.PureComponent {
rows.push(<tr key={termDoc[termsLang].url}>
<td className="mx_TermsDialog_service">{serviceName}</td>
<td className="mx_TermsDialog_summary">{summary}</td>
<td>{termDoc[termsLang].name} <a rel="noopener" target="_blank" href={termDoc[termsLang].url}>
<td>{termDoc[termsLang].name} <a rel="noreferrer noopener" target="_blank" href={termDoc[termsLang].url}>
<span className="mx_TermsDialog_link" />
</a></td>
<td><TermsCheckbox

View file

@ -0,0 +1,55 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
export default class VerificationRequestDialog extends React.Component {
static propTypes = {
verificationRequest: PropTypes.object.isRequired,
onFinished: PropTypes.func.isRequired,
};
constructor(...args) {
super(...args);
this.onFinished = this.onFinished.bind(this);
}
render() {
const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel");
return <BaseDialog className="mx_InfoDialog" onFinished={this.onFinished}
contentId="mx_Dialog_content"
title={_t("Verification Request")}
hasCancel={true}
>
<EncryptionPanel
layout="dialog"
verificationRequest={this.props.verificationRequest}
onClose={this.props.onFinished}
member={MatrixClientPeg.get().getUser(this.props.verificationRequest.otherUserId)}
/>
</BaseDialog>;
}
onFinished() {
this.props.verificationRequest.cancel();
this.props.onFinished();
}
}

View file

@ -22,7 +22,6 @@ import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import { MatrixClient } from 'matrix-js-sdk';
import Modal from '../../../../Modal';
import { _t } from '../../../../languageHandler';
import {Key} from "../../../../Keyboard";
import { accessSecretStorage } from '../../../../CrossSigningManager';
const RESTORE_TYPE_PASSPHRASE = 0;
@ -125,6 +124,8 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
}
_onRecoveryKeyNext = async () => {
if (!this.state.recoveryKeyValid) return;
this.setState({
loading: true,
restoreError: null,
@ -157,18 +158,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
});
}
_onPassPhraseKeyPress = (e) => {
if (e.key === Key.ENTER) {
this._onPassPhraseNext();
}
}
_onRecoveryKeyKeyPress = (e) => {
if (e.key === Key.ENTER && this.state.recoveryKeyValid) {
this._onRecoveryKeyNext();
}
}
async _restoreWithSecretStorage() {
this.setState({
loading: true,
@ -305,21 +294,22 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
"messaging by entering your recovery passphrase.",
)}</p>
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
<form className="mx_RestoreKeyBackupDialog_primaryContainer">
<input type="password"
className="mx_RestoreKeyBackupDialog_passPhraseInput"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
autoFocus={true}
/>
<DialogButtons primaryButton={_t('Next')}
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNext}
primaryIsSubmit={true}
hasCancel={true}
onCancel={this._onCancel}
focus={false}
/>
</div>
</form>
{_t(
"If you've forgotten your recovery passphrase you can "+
"<button1>use your recovery key</button1> or " +
@ -371,7 +361,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
<input className="mx_RestoreKeyBackupDialog_recoveryKeyInput"
onChange={this._onRecoveryKeyChange}
onKeyPress={this._onRecoveryKeyKeyPress}
value={this.state.recoveryKey}
autoFocus={true}
/>

View file

@ -1,6 +1,6 @@
/*
Copyright 2018, 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019, 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.
@ -21,7 +21,6 @@ import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import { _t } from '../../../../languageHandler';
import { Key } from "../../../../Keyboard";
/*
* Access Secure Secret Storage by requesting the user's passphrase.
@ -68,7 +67,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
});
}
_onPassPhraseNext = async () => {
_onPassPhraseNext = async (e) => {
e.preventDefault();
if (this.state.passPhrase.length <= 0) return;
this.setState({ keyMatches: null });
const input = { passphrase: this.state.passPhrase };
const keyMatches = await this.props.checkPrivateKey(input);
@ -79,7 +82,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
}
}
_onRecoveryKeyNext = async () => {
_onRecoveryKeyNext = async (e) => {
e.preventDefault();
if (!this.state.recoveryKeyValid) return;
this.setState({ keyMatches: null });
const input = { recoveryKey: this.state.recoveryKey };
const keyMatches = await this.props.checkPrivateKey(input);
@ -97,18 +104,6 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
});
}
_onPassPhraseKeyPress = (e) => {
if (e.key === Key.ENTER && this.state.passPhrase.length > 0) {
this._onPassPhraseNext();
}
}
_onRecoveryKeyKeyPress = (e) => {
if (e.key === Key.ENTER && this.state.recoveryKeyValid) {
this._onRecoveryKeyNext();
}
}
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
@ -135,7 +130,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
)}
</div>;
} else {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>;
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
}
content = <div>
@ -149,23 +144,25 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
"identity for verifying other sessions by entering your passphrase.",
)}</p>
<div className="mx_AccessSecretStorageDialog_primaryContainer">
<input type="password"
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onPassPhraseNext}>
<input
type="password"
className="mx_AccessSecretStorageDialog_passPhraseInput"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
autoFocus={true}
autoComplete="new-password"
/>
{keyStatus}
<DialogButtons primaryButton={_t('Next')}
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNext}
hasCancel={true}
onCancel={this._onCancel}
focus={false}
primaryDisabled={this.state.passPhrase.length === 0}
/>
</div>
</form>
{_t(
"If you've forgotten your passphrase you can "+
"<button1>use your recovery key</button1> or " +
@ -192,11 +189,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
let keyStatus;
if (this.state.recoveryKey.length === 0) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>;
} else if (this.state.recoveryKeyValid) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
</div>;
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
} else if (this.state.keyMatches === false) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4E "}{_t(
@ -204,6 +197,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
"entered the correct recovery key.",
)}
</div>;
} else if (this.state.recoveryKeyValid) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
</div>;
} else {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4E "}{_t("Not a valid recovery key")}
@ -221,22 +218,22 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
"identity for verifying other sessions by entering your recovery key.",
)}</p>
<div className="mx_AccessSecretStorageDialog_primaryContainer">
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onRecoveryKeyNext}>
<input className="mx_AccessSecretStorageDialog_recoveryKeyInput"
onChange={this._onRecoveryKeyChange}
onKeyPress={this._onRecoveryKeyKeyPress}
value={this.state.recoveryKey}
autoFocus={true}
/>
{keyStatus}
<DialogButtons primaryButton={_t('Next')}
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onRecoveryKeyNext}
hasCancel={true}
onCancel={this._onCancel}
focus={false}
primaryDisabled={!this.state.recoveryKeyValid}
/>
</div>
</form>
{_t(
"If you've forgotten your recovery key you can "+
"<button>set up new recovery options</button>."

View file

@ -552,7 +552,7 @@ export default class AppTile extends React.Component {
// Using Object.assign workaround as the following opens in a new window instead of a new tab.
// window.open(this._getSafeUrl(), '_blank', 'noopener=yes');
Object.assign(document.createElement('a'),
{ target: '_blank', href: this._getSafeUrl(), rel: 'noopener'}).click();
{ target: '_blank', href: this._getSafeUrl(), rel: 'noreferrer noopener'}).click();
}
_onReloadWidgetClick() {

View file

@ -91,7 +91,7 @@ export default class ImageView extends React.Component {
getName() {
let name = this.props.name;
if (name && this.props.link) {
name = <a href={ this.props.link } target="_blank" rel="noopener">{ name }</a>;
name = <a href={ this.props.link } target="_blank" rel="noreferrer noopener">{ name }</a>;
}
return name;
}
@ -216,7 +216,7 @@ export default class ImageView extends React.Component {
{ this.getName() }
</div>
{ eventMeta }
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } target="_blank" rel="noopener">
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } rel="noreferrer noopener">
<div className="mx_ImageView_download">
{ _t('Download this file') }<br />
<span className="mx_ImageView_size">{ sizeRes }</span>

View file

@ -23,7 +23,6 @@ import classNames from 'classnames';
import { Room, RoomMember } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { getDisplayAliasForRoom } from '../../../Rooms';
import FlairStore from "../../../stores/FlairStore";
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
@ -128,7 +127,8 @@ const Pill = createReactClass({
case Pill.TYPE_ROOM_MENTION: {
const localRoom = resourceId[0] === '#' ?
MatrixClientPeg.get().getRooms().find((r) => {
return r.getAliases().includes(resourceId);
return r.getCanonicalAlias() === resourceId ||
r.getAltAliases().includes(resourceId);
}) : MatrixClientPeg.get().getRoom(resourceId);
room = localRoom;
if (!localRoom) {
@ -211,7 +211,7 @@ const Pill = createReactClass({
if (room) {
linkText = "@room";
if (this.props.shouldShowPillAvatar) {
avatar = <RoomAvatar room={room} width={16} height={16} />;
avatar = <RoomAvatar room={room} width={16} height={16} aria-hidden="true" />;
}
pillClass = 'mx_AtRoomPill';
}
@ -225,7 +225,7 @@ const Pill = createReactClass({
member.rawDisplayName = member.rawDisplayName || '';
linkText = member.rawDisplayName;
if (this.props.shouldShowPillAvatar) {
avatar = <MemberAvatar member={member} width={16} height={16} />;
avatar = <MemberAvatar member={member} width={16} height={16} aria-hidden="true" />;
}
pillClass = 'mx_UserPill';
href = null;
@ -236,12 +236,12 @@ const Pill = createReactClass({
case Pill.TYPE_ROOM_MENTION: {
const room = this.state.room;
if (room) {
linkText = (room ? getDisplayAliasForRoom(room) : null) || resource;
linkText = resource;
if (this.props.shouldShowPillAvatar) {
avatar = <RoomAvatar room={room} width={16} height={16} />;
avatar = <RoomAvatar room={room} width={16} height={16} aria-hidden="true" />;
}
pillClass = 'mx_RoomPill';
}
pillClass = 'mx_RoomPill';
}
break;
case Pill.TYPE_GROUP_MENTION: {
@ -251,7 +251,7 @@ const Pill = createReactClass({
linkText = groupId;
if (this.props.shouldShowPillAvatar) {
avatar = <BaseAvatar name={name || groupId} width={16} height={16}
avatar = <BaseAvatar name={name || groupId} width={16} height={16} aria-hidden="true"
url={avatarUrl ? cli.mxcUrlToHttp(avatarUrl, 16, 16) : null} />;
}
pillClass = 'mx_GroupPill';

View file

@ -17,40 +17,151 @@ limitations under the License.
import React from "react";
import PropTypes from "prop-types";
import {replaceableComponent} from "../../../../utils/replaceableComponent";
import * as qs from "qs";
import QRCode from "qrcode-react";
import {MatrixClientPeg} from "../../../../MatrixClientPeg";
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import {ToDeviceChannel} from "matrix-js-sdk/src/crypto/verification/request/ToDeviceChannel";
import {decodeBase64} from "matrix-js-sdk/src/crypto/olmlib";
import Spinner from "../Spinner";
import * as QRCode from "qrcode";
const CODE_VERSION = 0x02; // the version of binary QR codes we support
const BINARY_PREFIX = "MATRIX"; // ASCII, used to prefix the binary format
const MODE_VERIFY_OTHER_USER = 0x00; // Verifying someone who isn't us
const MODE_VERIFY_SELF_TRUSTED = 0x01; // We trust the master key
const MODE_VERIFY_SELF_UNTRUSTED = 0x02; // We do not trust the master key
@replaceableComponent("views.elements.crypto.VerificationQRCode")
export default class VerificationQRCode extends React.PureComponent {
static propTypes = {
// Common for all kinds of QR codes
keys: PropTypes.array.isRequired, // array of [Key ID, Base64 Key] pairs
action: PropTypes.string.isRequired,
keyholderUserId: PropTypes.string.isRequired,
// User verification use case only
secret: PropTypes.string,
otherUserKey: PropTypes.string, // Base64 key being verified
requestEventId: PropTypes.string,
prefix: PropTypes.string.isRequired,
version: PropTypes.number.isRequired,
mode: PropTypes.number.isRequired,
transactionId: PropTypes.string.isRequired, // or requestEventId
firstKeyB64: PropTypes.string.isRequired,
secondKeyB64: PropTypes.string.isRequired,
secretB64: PropTypes.string.isRequired,
};
static defaultProps = {
action: "verify",
};
static async getPropsForRequest(verificationRequest: VerificationRequest) {
const cli = MatrixClientPeg.get();
const myUserId = cli.getUserId();
const otherUserId = verificationRequest.otherUserId;
render() {
const query = {
request: this.props.requestEventId,
action: this.props.action,
other_user_key: this.props.otherUserKey,
secret: this.props.secret,
};
for (const key of this.props.keys) {
query[`key_${key[0]}`] = key[1];
let mode = MODE_VERIFY_OTHER_USER;
if (myUserId === otherUserId) {
// Mode changes depending on whether or not we trust the master cross signing key
const myTrust = cli.checkUserTrust(myUserId);
if (myTrust.isCrossSigningVerified()) {
mode = MODE_VERIFY_SELF_TRUSTED;
} else {
mode = MODE_VERIFY_SELF_UNTRUSTED;
}
}
const uri = `https://matrix.to/#/${this.props.keyholderUserId}?${qs.stringify(query)}`;
const requestEvent = verificationRequest.requestEvent;
const transactionId = requestEvent.getId()
? requestEvent.getId()
: ToDeviceChannel.getTransactionId(requestEvent);
return <QRCode value={uri} size={512} logoWidth={64} logo={require("../../../../../res/img/matrix-m.svg")} />;
const qrProps = {
prefix: BINARY_PREFIX,
version: CODE_VERSION,
mode,
transactionId,
firstKeyB64: '', // worked out shortly
secondKeyB64: '', // worked out shortly
secretB64: verificationRequest.encodedSharedSecret,
};
const myCrossSigningInfo = cli.getStoredCrossSigningForUser(myUserId);
const myDevices = (await cli.getStoredDevicesForUser(myUserId)) || [];
if (mode === MODE_VERIFY_OTHER_USER) {
// First key is our master cross signing key
qrProps.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other user's master cross signing key
const otherUserCrossSigningInfo = cli.getStoredCrossSigningForUser(otherUserId);
qrProps.secondKeyB64 = otherUserCrossSigningInfo.getId("master");
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
// First key is our master cross signing key
qrProps.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other device's device key
const otherDevice = verificationRequest.targetDevice;
const otherDeviceId = otherDevice ? otherDevice.deviceId : null;
const device = myDevices.find(d => d.deviceId === otherDeviceId);
qrProps.secondKeyB64 = device.getFingerprint();
} else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
// First key is our device's key
qrProps.firstKeyB64 = cli.getDeviceEd25519Key();
// Second key is what we think our master cross signing key is
qrProps.secondKeyB64 = myCrossSigningInfo.getId("master");
}
return qrProps;
}
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() {
let buf = Buffer.alloc(0); // we'll concat our way through life
const appendByte = (b: number) => {
const tmpBuf = Buffer.from([b]);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendInt = (i: number) => {
const tmpBuf = Buffer.alloc(4);
tmpBuf.writeInt8(i, 0);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendStr = (s: string, enc: string) => {
const tmpBuf = Buffer.from(s, enc);
appendInt(tmpBuf.byteLength);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendEncBase64 = (b64: string) => {
const b = decodeBase64(b64);
const tmpBuf = Buffer.from(b);
buf = Buffer.concat([buf, tmpBuf]);
};
// Actually build the buffer for the QR code
appendStr(this.props.prefix, "ascii");
appendByte(this.props.version);
appendByte(this.props.mode);
appendStr(this.props.transactionId, "utf-8");
appendEncBase64(this.props.firstKeyB64);
appendEncBase64(this.props.secondKeyB64);
appendEncBase64(this.props.secretB64);
// Now actually assemble the QR code's data URI
const uri = await QRCode.toDataURL([{data: buf, 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' />;
}
}

View file

@ -20,7 +20,7 @@ import * as HtmlUtils from '../../../HtmlUtils';
import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils';
import {formatTime} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk';
import {pillifyLinks} from '../../../utils/pillify';
import {pillifyLinks, unmountPills} from '../../../utils/pillify';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
@ -53,6 +53,7 @@ export default class EditHistoryMessage extends React.PureComponent {
this.state = {canRedact, sendStatus: event.getAssociatedStatus()};
this._content = createRef();
this._pills = [];
}
_onAssociatedStatusChanged = () => {
@ -81,7 +82,7 @@ export default class EditHistoryMessage extends React.PureComponent {
pillifyLinks() {
// not present for redacted events
if (this._content.current) {
pillifyLinks(this._content.current.children, this.props.mxEvent);
pillifyLinks(this._content.current.children, this.props.mxEvent, this._pills);
}
}
@ -90,6 +91,7 @@ export default class EditHistoryMessage extends React.PureComponent {
}
componentWillUnmount() {
unmountPills(this._pills);
const event = this.props.mxEvent;
if (event.localRedactionEvent()) {
event.localRedactionEvent().off("status", this._onAssociatedStatusChanged);

View file

@ -26,7 +26,7 @@ import {decryptFile} from '../../../utils/DecryptFile';
import Tinter from '../../../Tinter';
import request from 'browser-request';
import Modal from '../../../Modal';
import SdkConfig from "../../../SdkConfig";
import AccessibleButton from "../elements/AccessibleButton";
// A cached tinted copy of require("../../../../res/img/download.svg")
@ -94,84 +94,6 @@ Tinter.registerTintable(updateTintedDownloadImage);
// The downside of using a second domain is that it complicates hosting,
// the downside of using a sandboxed iframe is that the browers are overly
// restrictive in what you are allowed to do with the generated URL.
//
// For now given how unusable the blobs generated in sandboxed iframes are we
// default to using a renderer hosted on "usercontent.riot.im". This is
// overridable so that people running their own version of the client can
// choose a different renderer.
//
// To that end the current version of the blob generation is the following
// html:
//
// <html><head><script>
// var params = window.location.search.substring(1).split('&');
// var lockOrigin;
// for (var i = 0; i < params.length; ++i) {
// var parts = params[i].split('=');
// if (parts[0] == 'origin') lockOrigin = decodeURIComponent(parts[1]);
// }
// window.onmessage=function(e){
// if (lockOrigin === undefined || e.origin === lockOrigin) eval("("+e.data.code+")")(e);
// }
// </script></head><body></body></html>
//
// This waits to receive a message event sent using the window.postMessage API.
// When it receives the event it evals a javascript function in data.code and
// runs the function passing the event as an argument. This version adds
// support for a query parameter controlling the origin from which messages
// will be processed as an extra layer of security (note that the default URL
// is still 'v1' since it is backwards compatible).
//
// In particular it means that the rendering function can be written as a
// ordinary javascript function which then is turned into a string using
// toString().
//
const DEFAULT_CROSS_ORIGIN_RENDERER = "https://usercontent.riot.im/v1.html";
/**
* Render the attachment inside the iframe.
* We can't use imported libraries here so this has to be vanilla JS.
*/
function remoteRender(event) {
const data = event.data;
const img = document.createElement("img");
img.id = "img";
img.src = data.imgSrc;
const a = document.createElement("a");
a.id = "a";
a.rel = data.rel;
a.target = data.target;
a.download = data.download;
a.style = data.style;
a.style.fontFamily = "Arial, Helvetica, Sans-Serif";
a.href = window.URL.createObjectURL(data.blob);
a.appendChild(img);
a.appendChild(document.createTextNode(data.textContent));
const body = document.body;
// Don't display scrollbars if the link takes more than one line
// to display.
body.style = "margin: 0px; overflow: hidden";
body.appendChild(a);
}
/**
* Update the tint inside the iframe.
* We can't use imported libraries here so this has to be vanilla JS.
*/
function remoteSetTint(event) {
const data = event.data;
const img = document.getElementById("img");
img.src = data.imgSrc;
img.style = data.imgStyle;
const a = document.getElementById("a");
a.style = data.style;
}
/**
* Get the current CSS style for a DOMElement.
@ -283,7 +205,6 @@ export default createReactClass({
// will be inside the iframe so we wont be able to update
// it directly.
this._iframe.current.contentWindow.postMessage({
code: remoteSetTint.toString(),
imgSrc: tintedDownloadImageURL,
style: computedStyle(this._dummyLink.current),
}, "*");
@ -306,7 +227,7 @@ export default createReactClass({
// Wait for the user to click on the link before downloading
// and decrypting the attachment.
let decrypting = false;
const decrypt = () => {
const decrypt = (e) => {
if (decrypting) {
return false;
}
@ -323,16 +244,15 @@ export default createReactClass({
});
}).finally(() => {
decrypting = false;
return;
});
};
return (
<span className="mx_MFileBody">
<div className="mx_MFileBody_download">
<a href="javascript:void(0)" onClick={decrypt}>
<AccessibleButton onClick={decrypt}>
{ _t("Decrypt %(text)s", { text: text }) }
</a>
</AccessibleButton>
</div>
</span>
);
@ -341,7 +261,6 @@ export default createReactClass({
// When the iframe loads we tell it to render a download link
const onIframeLoad = (ev) => {
ev.target.contentWindow.postMessage({
code: remoteRender.toString(),
imgSrc: tintedDownloadImageURL,
style: computedStyle(this._dummyLink.current),
blob: this.state.decryptedBlob,
@ -349,19 +268,13 @@ export default createReactClass({
// will have the correct name when the user tries to download it.
// We can't provide a Content-Disposition header like we would for HTTP.
download: fileName,
rel: "noopener",
target: "_blank",
textContent: _t("Download %(text)s", { text: text }),
}, "*");
};
// If the attachment is encryped then put the link inside an iframe.
let renderer_url = DEFAULT_CROSS_ORIGIN_RENDERER;
const appConfig = SdkConfig.get();
if (appConfig && appConfig.cross_origin_renderer_url) {
renderer_url = appConfig.cross_origin_renderer_url;
}
renderer_url += "?origin=" + encodeURIComponent(window.location.origin);
const url = "usercontent/"; // XXX: this path should probably be passed from the skin
// If the attachment is encrypted then put the link inside an iframe.
return (
<span className="mx_MFileBody">
<div className="mx_MFileBody_download">
@ -373,14 +286,18 @@ export default createReactClass({
*/ }
<a ref={this._dummyLink} />
</div>
<iframe src={renderer_url} onLoad={onIframeLoad} ref={this._iframe} />
<iframe
src={`${url}?origin=${encodeURIComponent(window.location.origin)}`}
onLoad={onIframeLoad}
ref={this._iframe}
sandbox="allow-scripts allow-downloads" />
</div>
</span>
);
} else if (contentUrl) {
const downloadProps = {
target: "_blank",
rel: "noopener",
rel: "noreferrer noopener",
// We set the href regardless of whether or not we intercept the download
// because we don't really want to convert the file to a blob eagerly, and

View file

@ -40,7 +40,10 @@ export default class MKeyVerificationConclusion extends React.Component {
if (request) {
request.off("change", this._onRequestChanged);
}
MatrixClientPeg.removeListener("userTrustStatusChanged", this._onTrustChanged);
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("userTrustStatusChanged", this._onTrustChanged);
}
}
_onRequestChanged = () => {

View file

@ -27,6 +27,7 @@ import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
export default class MKeyVerificationRequest extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
@ -93,10 +94,20 @@ export default class MKeyVerificationRequest extends React.Component {
_cancelledLabel(userId) {
const client = MatrixClientPeg.get();
const myUserId = client.getUserId();
const {cancellationCode} = this.props.mxEvent.verificationRequest;
const declined = cancellationCode === "m.user";
if (userId === myUserId) {
return _t("You cancelled");
if (declined) {
return _t("You declined");
} else {
return _t("You cancelled");
}
} else {
return _t("%(name)s cancelled", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
if (declined) {
return _t("%(name)s declined", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
} else {
return _t("%(name)s cancelled", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
}
}
}
@ -115,15 +126,19 @@ export default class MKeyVerificationRequest extends React.Component {
let subtitle;
let stateNode;
const accepted = request.ready || request.started || request.done;
if (accepted || request.cancelled) {
if (!request.canAccept) {
let stateLabel;
const accepted = request.ready || request.started || request.done;
if (accepted) {
stateLabel = (<AccessibleButton onClick={this._openRequest}>
{this._acceptedLabel(request.receivingUserId)}
</AccessibleButton>);
} else {
} else if (request.cancelled) {
stateLabel = this._cancelledLabel(request.cancellingUserId);
} else if (request.accepting) {
stateLabel = _t("accepting …");
} else if (request.declining) {
stateLabel = _t("declining …");
}
stateNode = (<div className="mx_cryptoEvent_state">{stateLabel}</div>);
}
@ -134,7 +149,7 @@ export default class MKeyVerificationRequest extends React.Component {
_t("%(name)s wants to verify", {name})}</div>);
subtitle = (<div className="mx_cryptoEvent_subtitle">{
userLabelForEventRoom(request.requestingUserId, mxEvent.getRoomId())}</div>);
if (request.requested && !request.observeOnly) {
if (request.canAccept) {
stateNode = (<div className="mx_cryptoEvent_buttons">
<FormButton kind="danger" onClick={this._onRejectClicked} label={_t("Decline")} />
<FormButton onClick={this._onAcceptClicked} label={_t("Accept")} />

View file

@ -30,7 +30,7 @@ import { _t } from '../../../languageHandler';
import * as ContextMenu from '../../structures/ContextMenu';
import SettingsStore from "../../../settings/SettingsStore";
import ReplyThread from "../elements/ReplyThread";
import {pillifyLinks} from '../../../utils/pillify';
import {pillifyLinks, unmountPills} from '../../../utils/pillify';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
import {toRightOf} from "../../structures/ContextMenu";
@ -92,6 +92,7 @@ export default createReactClass({
componentDidMount: function() {
this._unmounted = false;
this._pills = [];
if (!this.props.editState) {
this._applyFormatting();
}
@ -103,7 +104,7 @@ export default createReactClass({
// pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer
// are still sent as plaintext URLs. If these are ever pillified in the composer,
// we should be pillify them here by doing the linkifying BEFORE the pillifying.
pillifyLinks([this._content.current], this.props.mxEvent);
pillifyLinks([this._content.current], this.props.mxEvent, this._pills);
HtmlUtils.linkifyElement(this._content.current);
this.calculateUrlPreview();
@ -146,6 +147,7 @@ export default createReactClass({
componentWillUnmount: function() {
this._unmounted = true;
unmountPills(this._pills);
},
shouldComponentUpdate: function(nextProps, nextState) {
@ -372,7 +374,9 @@ export default createReactClass({
const height = window.screen.height > 800 ? 800 : window.screen.height;
const left = (window.screen.width - width) / 2;
const top = (window.screen.height - height) / 2;
window.open(completeUrl, '_blank', `height=${height}, width=${width}, top=${top}, left=${left},`);
const features = `height=${height}, width=${width}, top=${top}, left=${left},`;
const wnd = window.open(completeUrl, '_blank', features);
wnd.opener = null;
},
});
});

View file

@ -23,20 +23,23 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {ensureDMExists} from "../../../createRoom";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import Modal from "../../../Modal";
import {PHASE_REQUESTED} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import * as sdk from "../../../index";
import {_t} from "../../../languageHandler";
// cancellation codes which constitute a key mismatch
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
const EncryptionPanel = ({verificationRequest, member, onClose}) => {
const EncryptionPanel = ({verificationRequest, member, onClose, layout}) => {
const [request, setRequest] = useState(verificationRequest);
useEffect(() => {
setRequest(verificationRequest);
}, [verificationRequest]);
const [phase, setPhase] = useState(request && request.phase);
useEffect(() => {
setRequest(verificationRequest);
if (verificationRequest) {
setPhase(verificationRequest.phase);
}
}, [verificationRequest]);
const changeHandler = useCallback(() => {
// handle transitions -> cancelled for mismatches which fire a modal instead of showing a card
if (request && request.cancelled && MISMATCHES.includes(request.cancellationCode)) {
@ -69,14 +72,16 @@ const EncryptionPanel = ({verificationRequest, member, onClose}) => {
const roomId = await ensureDMExists(cli, member.userId);
const verificationRequest = await cli.requestVerificationDM(member.userId, roomId);
setRequest(verificationRequest);
setPhase(verificationRequest.phase);
}, [member.userId]);
const requested = request && (phase === PHASE_REQUESTED || phase === undefined);
const requested = request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined);
if (!request || requested) {
return <EncryptionInfo onStartVerification={onStartVerification} member={member} pending={requested} />;
} else {
return (
<VerificationPanel
layout={layout}
onClose={onClose}
member={member}
request={request}
@ -89,6 +94,7 @@ EncryptionPanel.propTypes = {
member: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
verificationRequest: PropTypes.object,
layout: PropTypes.string,
};
export default EncryptionPanel;

View file

@ -25,7 +25,7 @@ import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import createRoom from '../../../createRoom';
import createRoom, {findDMForUser} from '../../../createRoom';
import DMRoomMap from '../../../utils/DMRoomMap';
import AccessibleButton from '../elements/AccessibleButton';
import SdkConfig from '../../../SdkConfig';
@ -135,19 +135,53 @@ function useIsEncrypted(cli, room) {
return isEncrypted;
}
function verifyDevice(userId, device) {
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
userId: userId,
device: device,
}, null, /* priority = */ false, /* static = */ true);
async function verifyDevice(userId, device) {
const cli = MatrixClientPeg.get();
const member = cli.getUser(userId);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog("Verification warning", "unverified session", QuestionDialog, {
headerImage: require("../../../../res/img/e2e/warning.svg"),
title: _t("Not Trusted"),
description: <div>
<p>{_t("%(name)s (%(userId)s) signed in to a new session without verifying it:", {name: member.displayName, userId})}</p>
<p>{device.getDisplayName()} ({device.deviceId})</p>
<p>{_t("Ask this user to verify their session, or manually verify it below.")}</p>
</div>,
onFinished: async (doneClicked) => {
const manuallyVerifyClicked = !doneClicked;
if (!manuallyVerifyClicked) {
return;
}
const cli = MatrixClientPeg.get();
const verificationRequest = await cli.requestVerification(
userId,
[device.deviceId],
);
dis.dispatch({
action: "set_right_panel_phase",
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
refireParams: {member, verificationRequest},
});
},
primaryButton: _t("Done"),
cancelButton: _t("Manually Verify"),
});
}
function verifyUser(user) {
const cli = MatrixClientPeg.get();
const dmRoom = findDMForUser(cli, user.userId);
let existingRequest;
if (dmRoom) {
existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId);
}
dis.dispatch({
action: "set_right_panel_phase",
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
refireParams: {member: user},
refireParams: {
member: user,
verificationRequest: existingRequest,
},
});
}

View file

@ -19,8 +19,9 @@ import PropTypes from "prop-types";
import * as sdk from '../../../index';
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {_t} from "../../../languageHandler";
import E2EIcon from "../rooms/E2EIcon";
import {
@ -29,12 +30,13 @@ import {
PHASE_READY,
PHASE_DONE,
PHASE_STARTED,
PHASE_CANCELLED,
PHASE_CANCELLED, VerificationRequest,
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import Spinner from "../elements/Spinner";
export default class VerificationPanel extends React.PureComponent {
static propTypes = {
layout: PropTypes.string,
request: PropTypes.object.isRequired,
member: PropTypes.object.isRequired,
phase: PropTypes.oneOf([
@ -50,68 +52,122 @@ export default class VerificationPanel extends React.PureComponent {
constructor(props) {
super(props);
this.state = {};
this.state = {
qrCodeProps: null, // generated by the VerificationQRCode component itself
};
this._hasVerifier = false;
if (this.props.request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD)) {
this._generateQRCodeProps(props.request);
}
}
async _generateQRCodeProps(verificationRequest: VerificationRequest) {
try {
this.setState({qrCodeProps: await VerificationQRCode.getPropsForRequest(verificationRequest)});
} catch (e) {
console.error(e);
// Do nothing - we won't render a QR code.
}
}
renderQRPhase(pending) {
const {member, request} = this.props;
const showSAS = request.methods.includes(verificationMethods.SAS);
const showQR = this.props.request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let button;
if (pending) {
button = <Spinner />;
} else {
button = (
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={this._startSAS}>
{_t("Verify by emoji")}
</AccessibleButton>
const noCommonMethodError = !showSAS && !showQR ?
<p>{_t("The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.")}</p> :
null;
if (this.props.layout === 'dialog') {
// HACK: This is a terrible idea.
let qrBlock;
let sasBlock;
if (showQR) {
let qrCode;
if (this.state.qrCodeProps) {
qrCode = <VerificationQRCode {...this.state.qrCodeProps} />;
} else {
qrCode = <div className='mx_VerificationPanel_QRPhase_noQR'><Spinner /></div>;
}
qrBlock =
<div className='mx_VerificationPanel_QRPhase_startOption'>
<p>{_t("Scan this unique code")}</p>
{qrCode}
</div>;
}
if (showSAS) {
sasBlock =
<div className='mx_VerificationPanel_QRPhase_startOption'>
<p>{_t("Compare unique emoji")}</p>
<span className='mx_VerificationPanel_QRPhase_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this._startSAS} kind='primary'>
{_t("Start")}
</AccessibleButton>
</div>;
}
const or = qrBlock && sasBlock ?
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
return (
<div>
{_t("Verify this session by completing one of the following:")}
<div className='mx_VerificationPanel_QRPhase_startOptions'>
{qrBlock}
{or}
{sasBlock}
{noCommonMethodError}
</div>
</div>
);
}
const cli = MatrixClientPeg.get();
const crossSigningInfo = cli.getStoredCrossSigningForUser(request.otherUserId);
if (!crossSigningInfo || !request.requestEvent || !request.requestEvent.getId()) {
// for whatever reason we can't generate a QR code, offer only SAS Verification
return <div className="mx_UserInfo_container">
<h3>Verify by emoji</h3>
<p>{_t("Verify by comparing unique emoji.")}</p>
{ button }
</div>;
}
const myKeyId = cli.getCrossSigningId();
const qrCodeKeys = [
[cli.getDeviceId(), cli.getDeviceEd25519Key()],
[myKeyId, myKeyId],
];
// TODO: add way to open camera to scan a QR code
return <React.Fragment>
<div className="mx_UserInfo_container">
<h3>Verify by scanning</h3>
let qrBlock;
if (this.state.qrCodeProps) {
qrBlock = <div className="mx_UserInfo_container">
<h3>{_t("Verify by scanning")}</h3>
<p>{_t("Ask %(displayName)s to scan your code:", {
displayName: member.displayName || member.name || member.userId,
})}</p>
<div className="mx_VerificationPanel_qrCode">
<VerificationQRCode
keyholderUserId={MatrixClientPeg.get().getUserId()}
requestEventId={request.requestEvent.getId()}
otherUserKey={crossSigningInfo.getId("master")}
secret={request.encodedSharedSecret}
keys={qrCodeKeys}
/>
<VerificationQRCode {...this.state.qrCodeProps} />
</div>
</div>
<div className="mx_UserInfo_container">
<h3>Verify by emoji</h3>
<p>{_t("If you can't scan the code above, verify by comparing unique emoji.")}</p>
</div>;
}
let sasBlock;
if (showSAS) {
let button;
if (pending) {
button = <Spinner />;
} else {
const disabled = this.state.emojiButtonClicked;
button = (
<AccessibleButton disabled={disabled} kind="primary" className="mx_UserInfo_wideButton" onClick={this._startSAS}>
{_t("Verify by emoji")}
</AccessibleButton>
);
}
const sasLabel = this.state.qrCodeProps ?
_t("If you can't scan the code above, verify by comparing unique emoji.") :
_t("Verify by comparing unique emoji.");
sasBlock = <div className="mx_UserInfo_container">
<h3>{_t("Verify by emoji")}</h3>
<p>{sasLabel}</p>
{ button }
</div>
</div>;
}
const noCommonMethodBlock = noCommonMethodError ?
<div className="mx_UserInfo_container">{noCommonMethodError}</div> :
null;
// TODO: add way to open camera to scan a QR code
return <React.Fragment>
{qrBlock}
{sasBlock}
{noCommonMethodBlock}
</React.Fragment>;
}
@ -125,7 +181,7 @@ export default class VerificationPanel extends React.PureComponent {
<p>{_t("You've successfully verified %(displayName)s!", {
displayName: member.displayName || member.name || member.userId,
})}</p>
<E2EIcon isUser={true} status="verified" size={128} />
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
<p>Verify all users in a room to ensure it's secure.</p>
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={this.props.onClose}>
@ -196,6 +252,7 @@ export default class VerificationPanel extends React.PureComponent {
}
_startSAS = async () => {
this.setState({emojiButtonClicked: true});
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
try {
await verifier.verify();
@ -233,7 +290,11 @@ export default class VerificationPanel extends React.PureComponent {
};
componentDidMount() {
this.props.request.on("change", this._onRequestChange);
const {request} = this.props;
request.on("change", this._onRequestChange);
if (request.verifier) {
this.setState({sasEvent: request.verifier.sasEvent});
}
this._onRequestChange();
}

View file

@ -74,7 +74,6 @@ export default class AliasSettings extends React.Component {
roomId: PropTypes.string.isRequired,
canSetCanonicalAlias: PropTypes.bool.isRequired,
canSetAliases: PropTypes.bool.isRequired,
aliasEvents: PropTypes.array, // [MatrixEvent]
canonicalAliasEvent: PropTypes.object, // MatrixEvent
};
@ -92,14 +91,9 @@ export default class AliasSettings extends React.Component {
remoteDomains: [], // [ domain.com, foobar.com ]
canonicalAlias: null, // #canonical:domain.com
updatingCanonicalAlias: false,
localAliasesLoading: true,
};
const localDomain = MatrixClientPeg.get().getDomain();
state.domainToAliases = this.aliasEventsToDictionary(props.aliasEvents || []);
state.remoteDomains = Object.keys(state.domainToAliases).filter((domain) => {
return domain !== localDomain && state.domainToAliases[domain].length > 0;
});
if (props.canonicalAliasEvent) {
state.canonicalAlias = props.canonicalAliasEvent.getContent().alias;
}
@ -107,6 +101,46 @@ export default class AliasSettings extends React.Component {
this.state = state;
}
async componentWillMount() {
const cli = MatrixClientPeg.get();
try {
if (await cli.doesServerSupportUnstableFeature("org.matrix.msc2432")) {
const response = await cli.unstableGetLocalAliases(this.props.roomId);
const localAliases = response.aliases;
const localDomain = cli.getDomain();
const domainToAliases = Object.assign(
{},
// FIXME, any localhost alt_aliases will be ignored as they are overwritten by localAliases
this.aliasesToDictionary(this._getAltAliases()),
{[localDomain]: localAliases || []},
);
const remoteDomains = Object.keys(domainToAliases).filter((domain) => {
return domain !== localDomain && domainToAliases[domain].length > 0;
});
this.setState({ domainToAliases, remoteDomains });
} else {
const state = {};
const localDomain = cli.getDomain();
state.domainToAliases = this.aliasEventsToDictionary(this.props.aliasEvents || []);
state.remoteDomains = Object.keys(state.domainToAliases).filter((domain) => {
return domain !== localDomain && state.domainToAliases[domain].length > 0;
});
this.setState(state);
}
} finally {
this.setState({localAliasesLoading: false});
}
}
aliasesToDictionary(aliases) {
return aliases.reduce((dict, alias) => {
const domain = alias.split(":")[1];
dict[domain] = dict[domain] || [];
dict[domain].push(alias);
return dict;
}, {});
}
aliasEventsToDictionary(aliasEvents) { // m.room.alias events
const dict = {};
aliasEvents.forEach((event) => {
@ -117,6 +151,16 @@ export default class AliasSettings extends React.Component {
return dict;
}
_getAltAliases() {
if (this.props.canonicalAliasEvent) {
const altAliases = this.props.canonicalAliasEvent.getContent().alt_aliases;
if (Array.isArray(altAliases)) {
return altAliases;
}
}
return [];
}
changeCanonicalAlias(alias) {
if (!this.props.canSetCanonicalAlias) return;
@ -126,6 +170,8 @@ export default class AliasSettings extends React.Component {
});
const eventContent = {};
const altAliases = this._getAltAliases();
if (altAliases) eventContent["alt_aliases"] = altAliases;
if (alias) eventContent["alias"] = alias;
MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.canonical_alias",
@ -261,26 +307,34 @@ export default class AliasSettings extends React.Component {
);
}
let localAliasesList;
if (this.state.localAliasesLoading) {
const Spinner = sdk.getComponent("elements.Spinner");
localAliasesList = <Spinner />;
} else {
localAliasesList = <EditableAliasesList
id="roomAliases"
className={"mx_RoomSettings_localAliases"}
items={this.state.domainToAliases[localDomain] || []}
newItem={this.state.newAlias}
onNewItemChanged={this.onNewAliasChanged}
canRemove={this.props.canSetAliases}
canEdit={this.props.canSetAliases}
onItemAdded={this.onLocalAliasAdded}
onItemRemoved={this.onLocalAliasDeleted}
itemsLabel={_t('Local addresses for this room:')}
noItemsLabel={_t('This room has no local addresses')}
placeholder={_t(
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
)}
domain={localDomain}
/>;
}
return (
<div className='mx_AliasSettings'>
{canonicalAliasSection}
<EditableAliasesList
id="roomAliases"
className={"mx_RoomSettings_localAliases"}
items={this.state.domainToAliases[localDomain] || []}
newItem={this.state.newAlias}
onNewItemChanged={this.onNewAliasChanged}
canRemove={this.props.canSetAliases}
canEdit={this.props.canSetAliases}
onItemAdded={this.onLocalAliasAdded}
onItemRemoved={this.onLocalAliasDeleted}
itemsLabel={_t('Local addresses for this room:')}
noItemsLabel={_t('This room has no local addresses')}
placeholder={_t(
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
)}
domain={localDomain}
/>
{localAliasesList}
{remoteAliasesSection}
</div>
);

View file

@ -219,7 +219,7 @@ export default createReactClass({
if (link) {
span = (
<a href={link} target="_blank" rel="noopener">
<a href={link} target="_blank" rel="noreferrer noopener">
{ span }
</a>
);

View file

@ -392,6 +392,20 @@ export default class BasicMessageEditor extends React.Component {
} else if (event.key === Key.ENTER && (event.shiftKey || (IS_MAC && event.altKey))) {
this._insertText("\n");
handled = true;
// move selection to start of composer
} else if (modKey && event.key === Key.HOME) {
setSelection(this._editorRef, model, {
index: 0,
offset: 0,
});
handled = true;
// move selection to end of composer
} else if (modKey && event.key === Key.END) {
setSelection(this._editorRef, model, {
index: model.parts.length - 1,
offset: model.parts[model.parts.length - 1].text.length,
});
handled = true;
// autocomplete or enter to send below shouldn't have any modifier keys pressed.
} else {
const metaOrAltPressed = event.metaKey || event.altKey;
@ -490,6 +504,7 @@ export default class BasicMessageEditor extends React.Component {
}
componentWillUnmount() {
document.removeEventListener("selectionchange", this._onSelectionChange);
this._editorRef.removeEventListener("input", this._onInput, true);
this._editorRef.removeEventListener("compositionstart", this._onCompositionStart, true);
this._editorRef.removeEventListener("compositionend", this._onCompositionEnd, true);

View file

@ -51,7 +51,7 @@ const legacyRoomTitles = {
[E2E_STATE.VERIFIED]: _td("All sessions in this encrypted room are trusted"),
};
const E2EIcon = ({isUser, status, className, size, onClick}) => {
const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => {
const [hover, setHover] = useState(false);
const classes = classNames({
@ -82,7 +82,7 @@ const E2EIcon = ({isUser, status, className, size, onClick}) => {
const onMouseOut = () => setHover(false);
let tip;
if (hover) {
if (hover && !hideTooltip) {
tip = <Tooltip label={e2eTitle ? _t(e2eTitle) : ""} />;
}

View file

@ -136,7 +136,7 @@ export default createReactClass({
<div className="mx_LinkPreviewWidget" >
{ img }
<div className="mx_LinkPreviewWidget_caption">
<div className="mx_LinkPreviewWidget_title"><a href={this.props.link} target="_blank" rel="noopener">{ p["og:title"] }</a></div>
<div className="mx_LinkPreviewWidget_title"><a href={this.props.link} target="_blank" rel="noreferrer noopener">{ p["og:title"] }</a></div>
<div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div>
<div className="mx_LinkPreviewWidget_description" ref={this._description}>
{ description }

View file

@ -341,7 +341,7 @@ export default class MessageComposer extends React.Component {
</a>
) : '';
controls.push(<div className="mx_MessageComposer_replaced_wrapper">
controls.push(<div className="mx_MessageComposer_replaced_wrapper" key="room_replaced">
<div className="mx_MessageComposer_replaced_valign">
<img className="mx_MessageComposer_roomReplaced_icon" src={require("../../../../res/img/room_replaced.svg")} />
<span className="mx_MessageComposer_roomReplaced_header">

View file

@ -314,7 +314,7 @@ export default createReactClass({
return (
<div className="mx_RoomHeader light-panel">
<div className="mx_RoomHeader_wrapper">
<div className="mx_RoomHeader_wrapper" aria-owns="mx_RightPanel">
<div className="mx_RoomHeader_avatar">{ roomAvatar }{ e2eIcon }</div>
{ privateIcon }
{ name }

View file

@ -509,7 +509,7 @@ export default createReactClass({
"<issueLink>submit a bug report</issueLink>.",
{ errcode: this.props.error.errcode },
{ issueLink: label => <a href="https://github.com/vector-im/riot-web/issues/new/choose"
target="_blank" rel="noopener">{ label }</a> },
target="_blank" rel="noreferrer noopener">{ label }</a> },
),
];
break;

View file

@ -166,7 +166,9 @@ export default createReactClass({
});
/* Check all verified user devices. */
for (const userId of [...verified, cli.getUserId()]) {
/* Don't alarm if no other users are verified */
const targets = (verified.length > 0) ? [...verified, cli.getUserId()] : verified;
for (const userId of targets) {
const devices = await cli.getStoredDevicesForUser(userId);
const allDevicesVerified = devices.every(({deviceId}) => {
return cli.checkDeviceTrust(userId, deviceId).isVerified();

View file

@ -213,7 +213,7 @@ export default createReactClass({
}
return (
<li className="mx_WhoIsTypingTile">
<li className="mx_WhoIsTypingTile" aria-atomic="true">
<div className="mx_WhoIsTypingTile_avatars">
{ this._renderTypingIndicatorAvatars(usersTyping, this.props.whoIsTypingLimit) }
</div>

View file

@ -119,7 +119,7 @@ export default createReactClass({
'In future this will be improved.',
) }
{' '}
<a href="https://github.com/vector-im/riot-web/issues/2671" target="_blank" rel="noopener">
<a href="https://github.com/vector-im/riot-web/issues/2671" target="_blank" rel="noreferrer noopener">
https://github.com/vector-im/riot-web/issues/2671
</a>
</div>,
@ -253,20 +253,24 @@ export default createReactClass({
<form className={this.props.className} onSubmit={this.onClickChange}>
{ currentPassword }
<div className={rowClassName}>
<Field id="mx_ChangePassword_newPassword"
<Field
id="mx_ChangePassword_newPassword"
type="password"
label={passwordLabel}
value={this.state.newPassword}
autoFocus={this.props.autoFocusNewPasswordInput}
onChange={this.onChangeNewPassword}
autoComplete="new-password"
/>
</div>
<div className={rowClassName}>
<Field id="mx_ChangePassword_newPasswordConfirm"
<Field
id="mx_ChangePassword_newPasswordConfirm"
type="password"
label={_t("Confirm password")}
value={this.state.newPasswordConfirm}
onChange={this.onChangeNewPasswordConfirm}
autoComplete="new-password"
/>
</div>
<AccessibleButton className={buttonClassName} kind={this.props.buttonKind} onClick={this.onClickChange}>

View file

@ -20,6 +20,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import { accessSecretStorage } from '../../../CrossSigningManager';
import Modal from '../../../Modal';
export default class CrossSigningPanel extends React.PureComponent {
constructor(props) {
@ -86,11 +87,12 @@ export default class CrossSigningPanel extends React.PureComponent {
* 2. Access existing secret storage by requesting passphrase and accessing
* cross-signing keys as needed.
* 3. All keys are loaded and there's nothing to do.
* @param {bool} [force] Bootstrap again even if keys already present
*/
_bootstrapSecureSecretStorage = async () => {
_bootstrapSecureSecretStorage = async (force=false) => {
this.setState({ error: null });
try {
await accessSecretStorage();
await accessSecretStorage(() => undefined, force);
} catch (e) {
this.setState({ error: e });
console.error("Error bootstrapping secret storage", e);
@ -99,6 +101,18 @@ export default class CrossSigningPanel extends React.PureComponent {
this._getUpdatedStatus();
}
onDestroyStorage = (act) => {
if (!act) return;
this._bootstrapSecureSecretStorage(true);
}
_destroySecureSecretStorage = () => {
const ConfirmDestoryCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog");
Modal.createDialog(ConfirmDestoryCrossSigningDialog, {
onFinished: this.onDestroyStorage,
});
}
render() {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const {
@ -114,13 +128,12 @@ export default class CrossSigningPanel extends React.PureComponent {
}
const enabled = (
crossSigningPublicKeysOnDevice &&
crossSigningPrivateKeysInStorage &&
secretStorageKeyInAccount
);
let summarisedStatus;
if (enabled) {
if (enabled && crossSigningPublicKeysOnDevice) {
summarisedStatus = <p> {_t(
"Cross-signing and secret storage are enabled.",
)}</p>;
@ -142,6 +155,12 @@ export default class CrossSigningPanel extends React.PureComponent {
{_t("Bootstrap cross-signing and secret storage")}
</AccessibleButton>
</div>;
} else {
bootstrapButton = <div className="mx_CrossSigningPanel_buttonRow">
<AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
{_t("Reset cross-signing and secret storage")}
</AccessibleButton>
</div>;
}
return (

View file

@ -37,21 +37,29 @@ export default class EventIndexPanel extends React.Component {
};
}
async updateCurrentRoom(room) {
updateCurrentRoom = async (room) => {
const eventIndex = EventIndexPeg.get();
const stats = await eventIndex.getStats();
let stats;
try {
stats = await eventIndex.getStats();
} catch {
// This call may fail if sporadically, not a huge issue as we will
// try later again and probably succeed.
return;
}
this.setState({
eventIndexSize: stats.size,
roomCount: stats.roomCount,
});
}
};
componentWillUnmount(): void {
const eventIndex = EventIndexPeg.get();
if (eventIndex !== null) {
eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom.bind(this));
eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom);
}
}
@ -68,11 +76,17 @@ export default class EventIndexPanel extends React.Component {
let roomCount = 0;
if (eventIndex !== null) {
eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this));
eventIndex.on("changedCheckpoint", this.updateCurrentRoom);
const stats = await eventIndex.getStats();
eventIndexSize = stats.size;
roomCount = stats.roomCount;
try {
const stats = await eventIndex.getStats();
eventIndexSize = stats.size;
roomCount = stats.roomCount;
} catch {
// This call may fail if sporadically, not a huge issue as we
// will try later again in the updateCurrentRoom call and
// probably succeed.
}
}
this.setState({
@ -158,7 +172,7 @@ export default class EventIndexPanel extends React.Component {
{},
{
'nativeLink': (sub) => <a href={nativeLink} target="_blank"
rel="noopener">{sub}</a>,
rel="noreferrer noopener">{sub}</a>,
},
)
}
@ -174,7 +188,7 @@ export default class EventIndexPanel extends React.Component {
{},
{
'riotLink': (sub) => <a href="https://riot.im/download/desktop"
target="_blank" rel="noopener">{sub}</a>,
target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)
}

View file

@ -132,10 +132,10 @@ export default class ProfileSettings extends React.Component {
{_t(
"<a>Upgrade</a> to your own domain", {},
{
a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener">{sub}</a>,
a: sub => <a href={hostingSignupLink} target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)}
<a href={hostingSignupLink} target="_blank" rel="noopener">
<a href={hostingSignupLink} target="_blank" rel="noreferrer noopener">
<img src={require("../../../../res/img/external-link.svg")} width="11" height="10" alt='' />
</a>
</span>;

View file

@ -68,7 +68,7 @@ export default class BridgeSettingsTab extends React.Component {
{
// TODO: We don't have this link yet: this will prevent the translators
// having to re-translate the string when we do.
a: sub => <a href={BRIDGES_LINK} target="_blank" rel="noopener">{sub}</a>,
a: sub => <a href={BRIDGES_LINK} target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)}</p>
<ul className="mx_RoomSettingsDialog_BridgeList">
@ -82,7 +82,7 @@ export default class BridgeSettingsTab extends React.Component {
{
// TODO: We don't have this link yet: this will prevent the translators
// having to re-translate the string when we do.
a: sub => <a href={BRIDGES_LINK} target="_blank" rel="noopener">{sub}</a>,
a: sub => <a href={BRIDGES_LINK} target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)}</p>;
}

View file

@ -107,20 +107,20 @@ export default class RolesRoomSettingsTab extends React.Component {
};
componentDidMount(): void {
MatrixClientPeg.get().on("RoomState.members", this._onRoomMembership.bind(this));
MatrixClientPeg.get().on("RoomState.members", this._onRoomMembership);
}
componentWillUnmount(): void {
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("RoomState.members", this._onRoomMembership.bind(this));
client.removeListener("RoomState.members", this._onRoomMembership);
}
}
_onRoomMembership(event, state, member) {
_onRoomMembership = (event, state, member) => {
if (state.roomId !== this.props.roomId) return;
this.forceUpdate();
}
};
_populateDefaultPlEvents(eventsSection, stateLevel, eventsLevel) {
for (const desiredEvent of Object.keys(plEventsToShow)) {

View file

@ -36,11 +36,12 @@ export default class SecurityRoomSettingsTab extends React.Component {
joinRule: "invite",
guestAccess: "can_join",
history: "shared",
hasAliases: false,
encrypted: false,
};
}
componentWillMount(): void {
async componentWillMount(): void {
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
@ -63,6 +64,8 @@ export default class SecurityRoomSettingsTab extends React.Component {
);
const encrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.roomId);
this.setState({joinRule, guestAccess, history, encrypted});
const hasAliases = await this._hasAliases();
this.setState({hasAliases});
}
_pullContentPropertyFromEvent(event, key, defaultValue) {
@ -94,7 +97,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
{},
{
'a': (sub) => {
return <a rel='noopener' target='_blank'
return <a rel='noreferrer noopener' target='_blank'
href='https://about.riot.im/help#end-to-end-encryption'>{sub}</a>;
},
},
@ -201,13 +204,25 @@ export default class SecurityRoomSettingsTab extends React.Component {
MatrixClientPeg.get().getRoom(this.props.roomId).setBlacklistUnverifiedDevices(checked);
};
async _hasAliases() {
const cli = MatrixClientPeg.get();
if (await cli.doesServerSupportUnstableFeature("org.matrix.msc2432")) {
const response = await cli.unstableGetLocalAliases(this.props.roomId);
const localAliases = response.aliases;
return Array.isArray(localAliases) && localAliases.length !== 0;
} else {
const room = cli.getRoom(this.props.roomId);
const aliasEvents = room.currentState.getStateEvents("m.room.aliases") || [];
const hasAliases = !!aliasEvents.find((ev) => (ev.getContent().aliases || []).length > 0);
return hasAliases;
}
}
_renderRoomAccess() {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const joinRule = this.state.joinRule;
const guestAccess = this.state.guestAccess;
const aliasEvents = room.currentState.getStateEvents("m.room.aliases") || [];
const hasAliases = !!aliasEvents.find((ev) => (ev.getContent().aliases || []).length > 0);
const canChangeAccess = room.currentState.mayClientSendStateEvent("m.room.join_rules", client)
&& room.currentState.mayClientSendStateEvent("m.room.guest_access", client);
@ -226,7 +241,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
}
let aliasWarning = null;
if (joinRule === 'public' && !hasAliases) {
if (joinRule === 'public' && !this.state.hasAliases) {
aliasWarning = (
<div className='mx_SecurityRoomSettingsTab_warning'>
<img src={require("../../../../../../res/img/warning.svg")} width={15} height={15} />

View file

@ -37,7 +37,7 @@ const ghVersionLabel = function(repo, token='') {
} else {
url = `https://github.com/${repo}/commit/${token.split('-')[0]}`;
}
return <a target="_blank" rel="noopener" href={url}>{ token }</a>;
return <a target="_blank" rel="noreferrer noopener" href={url}>{ token }</a>;
};
export default class HelpUserSettingsTab extends React.Component {
@ -110,7 +110,7 @@ export default class HelpUserSettingsTab extends React.Component {
const legalLinks = [];
for (const tocEntry of SdkConfig.get().terms_and_conditions_links) {
legalLinks.push(<div key={tocEntry.url}>
<a href={tocEntry.url} rel="noopener" target="_blank">{tocEntry.text}</a>
<a href={tocEntry.url} rel="noreferrer noopener" target="_blank">{tocEntry.text}</a>
</div>);
}
@ -132,27 +132,27 @@ export default class HelpUserSettingsTab extends React.Component {
<span className='mx_SettingsTab_subheading'>{_t("Credits")}</span>
<ul>
<li>
The <a href="themes/riot/img/backgrounds/valley.jpg" rel="noopener" target="_blank">
The <a href="themes/riot/img/backgrounds/valley.jpg" rel="noreferrer noopener" target="_blank">
default cover photo</a> is ©&nbsp;
<a href="https://www.flickr.com/golan" rel="noopener" target="_blank">Jesús Roncero</a>{' '}
<a href="https://www.flickr.com/golan" rel="noreferrer noopener" target="_blank">Jesús Roncero</a>{' '}
used under the terms of&nbsp;
<a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="noopener" target="_blank">
<a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="noreferrer noopener" target="_blank">
CC-BY-SA 4.0</a>.
</li>
<li>
The <a href="https://github.com/matrix-org/twemoji-colr" rel="noopener" target="_blank">
twemoji-colr</a> font is ©&nbsp;
<a href="https://mozilla.org" rel="noopener" target="_blank">Mozilla Foundation</a>{' '}
The <a href="https://github.com/matrix-org/twemoji-colr" rel="noreferrer noopener"
target="_blank"> twemoji-colr</a> font is ©&nbsp;
<a href="https://mozilla.org" rel="noreferrer noopener" target="_blank">Mozilla Foundation</a>{' '}
used under the terms of&nbsp;
<a href="http://www.apache.org/licenses/LICENSE-2.0" rel="noopener" target="_blank">
<a href="http://www.apache.org/licenses/LICENSE-2.0" rel="noreferrer noopener" target="_blank">
Apache 2.0</a>.
</li>
<li>
The <a href="https://twemoji.twitter.com/" rel="noopener" target="_blank">
The <a href="https://twemoji.twitter.com/" rel="noreferrer noopener" target="_blank">
Twemoji</a> emoji art is ©&nbsp;
<a href="https://twemoji.twitter.com/" rel="noopener" target="_blank">Twitter, Inc and other
<a href="https://twemoji.twitter.com/" rel="noreferrer noopener" target="_blank">Twitter, Inc and other
contributors</a> used under the terms of&nbsp;
<a href="https://creativecommons.org/licenses/by/4.0/" rel="noopener" target="_blank">
<a href="https://creativecommons.org/licenses/by/4.0/" rel="noreferrer noopener" target="_blank">
CC-BY 4.0</a>.
</li>
</ul>
@ -162,7 +162,8 @@ export default class HelpUserSettingsTab extends React.Component {
render() {
let faqText = _t('For help with using Riot, click <a>here</a>.', {}, {
'a': (sub) => <a href="https://about.riot.im/need-help/" rel='noopener' target='_blank'>{sub}</a>,
'a': (sub) =>
<a href="https://about.riot.im/need-help/" rel='noreferrer noopener' target='_blank'>{sub}</a>,
});
if (SdkConfig.get().welcomeUserId && getCurrentLanguage().startsWith('en')) {
faqText = (
@ -170,7 +171,7 @@ export default class HelpUserSettingsTab extends React.Component {
{
_t('For help with using Riot, click <a>here</a> or start a chat with our ' +
'bot using the button below.', {}, {
'a': (sub) => <a href="https://about.riot.im/need-help/" rel='noopener'
'a': (sub) => <a href="https://about.riot.im/need-help/" rel='noreferrer noopener'
target='_blank'>{sub}</a>,
})
}

View file

@ -55,7 +55,7 @@ export default class LabsUserSettingsTab extends React.Component {
'<a>Learn more</a>.', {}, {
'a': (sub) => {
return <a href="https://github.com/vector-im/riot-web/blob/develop/docs/labs.md"
rel='noopener' target='_blank'>{sub}</a>;
rel='noreferrer noopener' target='_blank'>{sub}</a>;
},
})
}

View file

@ -32,6 +32,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
];
static TIMELINE_SETTINGS = [
'showTypingNotifications',
'autoplayGifsAndVideos',
'urlPreviewsEnabled',
'TextualBody.enableBigEmoji',

View file

@ -77,7 +77,7 @@ export default class InlineTermsAgreement extends React.Component {
"Accept <policyLink /> to continue:", {}, {
policyLink: () => {
return (
<a href={policy.url} rel='noopener' target='_blank'>
<a href={policy.url} rel='noreferrer noopener' target='_blank'>
{policy.name}
<span className='mx_InlineTermsAgreement_link' />
</a>

View file

@ -58,10 +58,7 @@ export default class VerificationRequestToast extends React.PureComponent {
_checkRequestIsPending = () => {
const {request} = this.props;
const isPendingInRoomRequest = request.channel.roomId &&
!(request.ready || request.started || request.done || request.cancelled || request.observeOnly);
const isPendingDeviceRequest = request.channel.deviceId && request.started;
if (!isPendingInRoomRequest && !isPendingDeviceRequest) {
if (!request.canAccept) {
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
}
};
@ -79,15 +76,15 @@ export default class VerificationRequestToast extends React.PureComponent {
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
const {request} = this.props;
// no room id for to_device requests
const cli = MatrixClientPeg.get();
try {
await request.accept();
if (request.channel.roomId) {
dis.dispatch({
action: 'view_room',
room_id: request.channel.roomId,
should_peek: false,
});
await request.accept();
const cli = MatrixClientPeg.get();
dis.dispatch({
action: "set_right_panel_phase",
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
@ -96,11 +93,10 @@ export default class VerificationRequestToast extends React.PureComponent {
member: cli.getUser(request.otherUserId),
},
});
} else if (request.channel.deviceId && request.verifier) {
// show to_device verifications in dialog still
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
verifier: request.verifier,
} else {
const VerificationRequestDialog = sdk.getComponent("views.dialogs.VerificationRequestDialog");
Modal.createTrackedDialog('Incoming Verification', '', VerificationRequestDialog, {
verificationRequest: request,
}, null, /* priority = */ false, /* static = */ true);
}
} catch (err) {

View file

@ -0,0 +1,82 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import AccessibleButton from "../elements/AccessibleButton";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import Spinner from "../elements/Spinner";
@replaceableComponent("views.verification.VerificationQREmojiOptions")
export default class VerificationQREmojiOptions extends React.Component {
static propTypes = {
request: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
onStartEmoji: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
qrProps: null,
};
this._prepareQrCode(props.request);
}
async _prepareQrCode(request: VerificationRequest) {
try {
const props = await VerificationQRCode.getPropsForRequest(request);
this.setState({qrProps: props});
} catch (e) {
console.error(e);
// We just won't show a QR code
}
}
render() {
let qrCode = <div className='mx_VerificationQREmojiOptions_noQR'><Spinner /></div>;
if (this.state.qrProps) {
qrCode = <VerificationQRCode {...this.state.qrProps} />;
}
return (
<div>
{_t("Verify this session by completing one of the following:")}
<div className='mx_IncomingSasDialog_startOptions'>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Scan this unique code")}</p>
{qrCode}
</div>
<div className='mx_IncomingSasDialog_betweenText'>{_t("or")}</div>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Compare unique emoji")}</p>
<span className='mx_IncomingSasDialog_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
<AccessibleButton onClick={this.props.onStartEmoji} kind='primary'>
{_t("Start")}
</AccessibleButton>
</div>
</div>
<AccessibleButton onClick={this.props.onCancel} kind='danger'>
{_t("Cancel")}
</AccessibleButton>
</div>
);
}
}

View file

@ -23,6 +23,7 @@ import dis from "./dispatcher";
import * as Rooms from "./Rooms";
import DMRoomMap from "./utils/DMRoomMap";
import {getAddressType} from "./UserAddress";
import SettingsStore from "./settings/SettingsStore";
/**
* Create a new room, and switch to it.
@ -159,7 +160,7 @@ export default function createRoom(opts) {
});
}
export async function ensureDMExists(client, userId) {
export function findDMForUser(client, userId) {
const roomIds = DMRoomMap.shared().getDMRoomsForUserId(userId);
const rooms = roomIds.map(id => client.getRoom(id));
const suitableDMRooms = rooms.filter(r => {
@ -169,12 +170,60 @@ export async function ensureDMExists(client, userId) {
}
return false;
});
let roomId;
if (suitableDMRooms.length) {
const room = suitableDMRooms[0];
roomId = room.roomId;
return suitableDMRooms[0];
}
}
/*
* Try to ensure the user is already in the megolm session before continuing
* NOTE: this assumes you've just created the room and there's not been an opportunity
* for other code to run, so we shouldn't miss RoomState.newMember when it comes by.
*/
export async function _waitForMember(client, roomId, userId, opts = { timeout: 1500 }) {
const { timeout } = opts;
let handler;
return new Promise((resolve) => {
handler = function(_event, _roomstate, member) {
if (member.userId !== userId) return;
if (member.roomId !== roomId) return;
resolve(true);
};
client.on("RoomState.newMember", handler);
/* We don't want to hang if this goes wrong, so we proceed and hope the other
user is already in the megolm session */
setTimeout(resolve, timeout, false);
}).finally(() => {
client.removeListener("RoomState.newMember", handler);
});
}
/*
* Ensure that for every user in a room, there is at least one device that we
* can encrypt to.
*/
export async function canEncryptToAllUsers(client, userIds) {
const usersDeviceMap = await client.downloadKeys(userIds);
// { "@user:host": { "DEVICE": {...}, ... }, ... }
return Object.values(usersDeviceMap).every((userDevices) =>
// { "DEVICE": {...}, ... }
Object.keys(userDevices).length > 0,
);
}
export async function ensureDMExists(client, userId) {
const existingDMRoom = findDMForUser(client, userId);
let roomId;
if (existingDMRoom) {
roomId = existingDMRoom.roomId;
} else {
roomId = await createRoom({dmUserId: userId, spinner: false, andView: false});
let encryption;
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
encryption = canEncryptToAllUsers(client, [userId]);
}
roomId = await createRoom({encryption, dmUserId: userId, spinner: false, andView: false});
await _waitForMember(client, roomId, userId);
}
return roomId;
}

View file

@ -102,7 +102,7 @@ export default class AutocompleteWrapperModel {
const text = completion.completion;
switch (completion.type) {
case "room":
return [this._partCreator.roomPill(completionId), this._partCreator.plain(completion.suffix)];
return [this._partCreator.roomPill(text, completionId), this._partCreator.plain(completion.suffix)];
case "at-room":
return [this._partCreator.atRoomPill(completionId), this._partCreator.plain(completion.suffix)];
case "user":

View file

@ -254,8 +254,8 @@ class RoomPillPart extends PillPart {
let initialLetter = "";
let avatarUrl = Avatar.avatarUrlForRoom(this._room, 16 * window.devicePixelRatio, 16 * window.devicePixelRatio);
if (!avatarUrl) {
initialLetter = Avatar.getInitialLetter(this._room.name);
avatarUrl = `../../${Avatar.defaultAvatarUrlForString(this._room.roomId)}`;
initialLetter = Avatar.getInitialLetter(this._room ? this._room.name : this.resourceId);
avatarUrl = `../../${Avatar.defaultAvatarUrlForString(this._room ? this._room.roomId : this.resourceId)}`;
}
this._setAvatarVars(node, avatarUrl, initialLetter);
}
@ -422,14 +422,15 @@ export class PartCreator {
return new PillCandidatePart(text, this._autoCompleteCreator);
}
roomPill(alias) {
roomPill(alias, roomId) {
let room;
if (alias[0] === '#') {
room = this._client.getRooms().find((r) => {
return r.getCanonicalAlias() === alias || r.getAliases().includes(alias);
});
if (roomId || alias[0] !== "#") {
room = this._client.getRoom(roomId || alias);
} else {
room = this._client.getRoom(alias);
room = this._client.getRooms().find((r) => {
return r.getCanonicalAlias() === alias ||
r.getAltAliases().includes(alias);
});
}
return new RoomPillPart(alias, room);
}

View file

@ -1419,7 +1419,7 @@
"View Servers in Room": "Zobrazit servery v místnosti",
"Sign out and remove encryption keys?": "Odhlásit a odstranit šifrovací klíče?",
"To help us prevent this in future, please <a>send us logs</a>.": "Abychom tomu mohli pro příště předejít, <a>pošlete nám prosím logy</a>.",
"Missing session data": "Chybějící data sezení",
"Missing session data": "Chybějící data relace",
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Některá data sezení, například klíče od šifrovaných zpráv, nám chybí. Přihlašte se prosím znovu a obnovte si klíče ze zálohy.",
"Your browser likely removed this data when running low on disk space.": "Prohlížeč data možná smazal aby ušetřil místo na disku.",
"Upload files (%(current)s of %(total)s)": "Nahrát soubory (%(current)s z %(total)s)",
@ -1825,7 +1825,7 @@
"This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.": "Toto běžně ovlivňuje jak je místnost zpracovávána na serveru. Pokud máte problém s Riotem, <a>nahlaste nám prosím bug</a>.",
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "Upgradujeme tuto místnost z <oldVersion /> na <newVersion />.",
"Upgrade": "Upgradovat",
"Enter secret storage passphrase": "Zadejte tajné heslo k úložišti",
"Enter secret storage passphrase": "Zadejte tajné heslo k bezpečnému úložišti",
"Unable to access secret storage. Please verify that you entered the correct passphrase.": "Nepovedlo se přistoupit k bezpečnému úložišti. Zkontrolujte prosím, že je zadané správné heslo.",
"<b>Warning</b>: You should only access secret storage from a trusted computer.": "<b>Varování</b>: Přistupujte k bezpečnému úložišti pouze z důvěryhodných počítačů.",
"If you've forgotten your passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.": "Pokud si nepamatujete heslo, můžete použít <button1>váš obnovovací klíč</button1> nebo <button2>si nastavte nové možnosti obnovení</button2>.",
@ -1861,5 +1861,290 @@
"%(count)s verified sessions|one": "1 ověřená relace",
"Language Dropdown": "Menu jazyků",
"Help": "Pomoc",
"Country Dropdown": "Menu států"
"Country Dropdown": "Menu států",
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "V místnosti jsou neověřené relace: pokud budete pokračovat bez ověření, bude možné váš hovor odposlouchávat.",
"Verify this session": "Ověřit tuto relaci",
"Encryption upgrade available": "Je dostupná aktualizace šifrování",
"Set up encryption": "Připravit šifrování",
"Unverified session": "Neověřená relace",
"Verifies a user, session, and pubkey tuple": "Ověří uživatele, relaci a veřejné klíče",
"Unknown (user, session) pair:": "Neznámý pár (uživatel, relace):",
"Session already verified!": "Relace je už ověřená!",
"WARNING: Session already verified, but keys do NOT MATCH!": "VAROVÁNÍ: Relace je už ověřená, ale klíče NEODPOVÍDAJÍ!",
"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!": "VAROVÁNÍ: OVĚŘENÍ KLÍČŮ SELHALO! Podpisový klíč pro uživatele %(userId)s a relaci %(deviceId)s je \"%(fprint)s\", což neodpovídá klíči \"%(fingerprint)s\". Může to znamenat, že je vaše komunikace rušena!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Zadaný podpisový klíč odpovídá klíči relace %(deviceId)s od uživatele %(userId)s. Relace byla označena za platnou.",
"%(senderName)s added %(addedAddresses)s and %(count)s other addresses to this room|other": "%(senderName)s přidal/a této místnosti %(addedAddresses)s a dalších %(count)s adres",
"%(senderName)s removed %(removedAddresses)s and %(count)s other addresses from this room|other": "%(senderName)s odstranil/a této místnosti %(removedAddresses)s a dalších %(count)s adres",
"%(senderName)s removed %(countRemoved)s and added %(countAdded)s addresses to this room": "%(senderName)s odstranil/a této místnosti %(countRemoved)s adres a přidal/a %(countAdded)s adres",
"a few seconds ago": "před pár vteřinami",
"about a minute ago": "před minutou",
"%(num)s minutes ago": "před %(num)s minutami",
"about an hour ago": "asi před hodinou",
"%(num)s hours ago": "před %(num)s hodinami",
"about a day ago": "před jedním dnem",
"%(num)s days ago": "před %(num)s dny",
"a few seconds from now": "za pár vteřin",
"about a minute from now": "asi za minutu",
"%(num)s minutes from now": "za %(num)s minut",
"about an hour from now": "asi za hodinu",
"%(num)s hours from now": "za %(num)s hodin",
"about a day from now": "asi za den",
"%(num)s days from now": "za %(num)s dní",
"Show a presence dot next to DMs in the room list": "V seznamu místností zobrazovat informaci o přítomnosti",
"Enable cross-signing to verify per-user instead of per-session (in development)": "Povolit cross-signing pro verifikaci uživatelů místo zařízení (experimentální)",
"Show info about bridges in room settings": "Zobrazovat v nastavení místnosti informace o propojeních",
"Show padlocks on invite only rooms": "Zobrazovat zámek u místností vyžadujících pozvání",
"Never send encrypted messages to unverified sessions from this session": "Nikdy neposílat šifrované zprávy neověřených zařízením",
"Never send encrypted messages to unverified sessions in this room from this session": "Nikdy v této místnosti neposílat šifrované zprávy neověřeným relacím",
"Enable message search in encrypted rooms": "Povolit vyhledávání v zašifrovaných místnostech",
"Keep secret storage passphrase in memory for this session": "Pro toto přihlášení si uchovat heslo k bezpečnému úložišti",
"How fast should messages be downloaded.": "Jak rychle se mají zprávy stahovat.",
"Confirm the emoji below are displayed on both devices, in the same order:": "Ověřte, že následující emotikony jsou zobrazeny na obou zařízeních ve stejném pořadí:",
"Verify this device by confirming the following number appears on its screen.": "Ověřit zařízení potvrzením, že jsou následující čísla zobrazena na jeho obrazovce.",
"Waiting for %(displayName)s to verify…": "Čekám až nás %(displayName)s ověří…",
"They match": "Odpovídají",
"They don't match": "Neodpovídají",
"To be secure, do this in person or use a trusted way to communicate.": "Aby to bylo bezpečné, udělejte to osobně nebo použijte důvěryhodný komunikační prostředek.",
"Lock": "Zamknout",
"Verify yourself & others to keep your chats safe": "Ověřte sebe a ostatní, aby byla vaše komunikace bezpečná",
"Other users may not trust it": "Ostatní uživatelé této relaci nemusí věřit",
"Later": "Později",
"Review": "Prohlédnout",
"This bridge was provisioned by <user />.": "Toto propojení poskytuje <user />.",
"This bridge is managed by <user />.": "Toto propojení spravuje <user />.",
"Workspace: %(networkName)s": "Workspace: %(networkName)s",
"Channel: %(channelName)s": "Kanál: %(channelName)s",
"Show less": "Skrýt detaily",
"Show more": "Zobrazit víc",
"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.": "Změna hesla resetuje šifrovací klíče pro všechny vaše relace. Pokud si nejdřív nevyexportujete klíče místností a po změně je znovu neimportujete, nedostanete se k historickým zprávám. V budoucnu se toto zjednoduší.",
"Cross-signing and secret storage are enabled.": "Cross-signing a bezpečné úložiště jsou zapnuté.",
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Váš účet má v bezpečném úložišti identitu pro cross-signing, ale v této relaci jí zatím nevěříte.",
"Cross-signing and secret storage are not yet set up.": "Zatím nemáte nastavený cross-signing a bezpečné úložiště.",
"Bootstrap cross-signing and secret storage": "Zapnout cross-signing a bezpečné úložiště",
"Cross-signing public keys:": "Veřejné klíče pro cross-signing:",
"in memory": "v paměti",
"Cross-signing private keys:": "Soukromé klíče pro cross-signing:",
"in secret storage": "v bezpečném úložišti",
"Secret storage public key:": "Veřejný klíč bezpečného úložiště:",
"in account data": "v datech účtu",
"Your homeserver does not support session management.": "Váš domovský server nepodporuje správu relací.",
"Unable to load session list": "Nepovedlo se načíst seznam relací",
"Delete %(count)s sessions|other": "Smazat %(count)s relací",
"Delete %(count)s sessions|one": "Smazat %(count)s relaci",
"Securely cache encrypted messages locally for them to appear in search results, using ": "Bezpečně uchovávat šifrované zprávy na tomto zařízení, aby se v nich dalo vyhledávat pomocí ",
" to store messages from ": " na uchování zpráv z ",
"rooms.": "místností.",
"Manage": "Spravovat",
"Securely cache encrypted messages locally for them to appear in search results.": "Bezpečně uchovávat zprávy na tomto zařízení aby se v nich dalo vyhledávat.",
"Enable": "Povolit",
"Riot can't securely cache encrypted messages locally while running in a web browser. Use <riotLink>Riot Desktop</riotLink> for encrypted messages to appear in search results.": "Riot neumí bezpečně uchovávat zprávy když běží v prohlížeči. Pokud chcete vyhledávat v šifrovaných zprávách, použijte <riotLink>Riot Desktop</riotLink>.",
"This session is backing up your keys. ": "Tato relace zálohuje vaše klíče. ",
"This session is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.": "Tato relace <b>nezálohuje vaše klíče</b>, ale už máte zálohu ze které je můžete obnovit.",
"Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Než se odhlásíce, připojte tuto relaci k záloze klíčů abyste nepřišli o klíče, které můžou být jen v této relaci.",
"Connect this session to Key Backup": "Připojit k zálohování klíčů",
"not stored": "není uložen",
"Backup has a signature from <verify>unknown</verify> session with ID %(deviceId)s": "Záloha je podepsaná z <verify>neznámé</verify> relace s ID %(deviceId)s",
"Backup has a <validity>valid</validity> signature from this session": "Záloha má <validity>platný</validity> podpis z této relace",
"Backup has an <validity>invalid</validity> signature from this session": "Záloha má <validity>neplatný</validity> podpis z této relace",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> session <device></device>": "Záloha má <validity>platný</validity> podpis z <verify>ověřené</verify> relace <device></device>",
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> session <device></device>": "Záloha má <validity>platný</validity> podpis z <verify>neověřené</verify> relace <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> session <device></device>": "Záloha má <validity>neplatný</validity> podpis z <verify>ověřené</verify> relace <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> session <device></device>": "Záloha má <validity>neplatný</validity> podpis z <verify>neověřené</verify> relace <device></device>",
"Backup is not signed by any of your sessions": "Záloha nemá podpis z žádné vaší relace",
"This backup is trusted because it has been restored on this session": "Záloze věříme, protože už byla v této relaci načtena",
"Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Zálohovací klíč je uložen v bezpečném úložišti, ale jeho načtení není v této relaci povolené. Zapněte prosím cross-signing v Experimentálních funkcích abyste mohli modifikovat stav zálohy.",
"Backup key stored: ": "Zálohovací klíč je uložen: ",
"Your keys are <b>not being backed up from this session</b>.": "Vaše klíče <b>nejsou z této relace zálohovány</b>.",
"Enable desktop notifications for this session": "Povolit v této relaci notifikace",
"Enable audible notifications for this session": "Povolit v této relaci hlasové notifikace",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Heslo bylo změněno. Dokud se znovu nepřihlásíte na ostatních zařízeních, nebudete na nich dostávat notifikace",
"Session ID:": "ID relace:",
"Session key:": "Klíč relace:",
"Message search": "Vyhledávání ve zprávách",
"Cross-signing": "Cross-signing",
"Sessions": "Relace",
"A session's public name is visible to people you communicate with": "Lidé se kterými komunikujete si můžou jméno relace zobrazit",
"This room is bridging messages to the following platforms. <a>Learn more.</a>": "Tato místnost je propojena s následujícími platformami. <a>Více informací</a>",
"This room isnt bridging messages to any platforms. <a>Learn more.</a>": "Tato místnost není propojená s žádnými dalšími platformami. <a>Více informací.</a>",
"Bridges": "Propojení",
"This user has not verified all of their sessions.": "Tento uživatel zatím neověřil všechny své relace.",
"You have not verified this user.": "Tohoto uživatele jste neověřil.",
"You have verified this user. This user has verified all of their sessions.": "Tohoto uživatele jste ověřili a on ověřil všechny své relace.",
"Someone is using an unknown session": "Někdo používá neznámou relaci",
"This room is end-to-end encrypted": "Místnost je E2E šifrovaná",
"Everyone in this room is verified": "V této místnosti jsou všichni ověřeni",
"Some sessions for this user are not trusted": "Některé relace tohoto uživatele jsou nedůvěryhodné",
"All sessions for this user are trusted": "Všem relacím tohoto uživatele věříme",
"Some sessions in this encrypted room are not trusted": "Některé relace v této místnosti jsou nedůvěryhodné",
"All sessions in this encrypted room are trusted": "Všem relacím v této místosti věříme",
"Mod": "Moderátor",
"Your key share request has been sent - please check your other sessions for key share requests.": "Požadavek k nasdílení klíčů byl odeslán - podívejte se prosím na své ostatní relace, jestli vám přišel.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Požadavek se sdílení klíčů se vaše ostatní relace odesílá automaticky. Pokud jste ho zamítli nebo ignorovali, tímto tlačítkem si jej můžete poslat znovu.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Pokud vaše ostatní relace nemají klíč pro tuto zprávu, nebudete mít možnost jí dešifrovat.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Znovu zažádat o šifrovací klíče</requestLink> z vašich ostatních relací.",
"Encrypted by an unverified session": "Zašifrované v neověřené relaci",
"Encrypted by a deleted session": "Zašifrované smazanou relací",
"Invite only": "Pouze na pozvání",
"No sessions with registered encryption keys": "Žádné relace se šifrovacími klíči",
"Send a reply…": "Odpovědět…",
"Send a message…": "Napsat zprávu…",
"Direct Messages": "Přímé zprávy",
"Reject & Ignore user": "Odmítnout & ignorovat uživatele",
"Unknown Command": "Neznámý příkaz",
"Unrecognised command: %(commandText)s": "Nerozpoznaný příkaz: %(commandText)s",
"You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Můžete použít <code>/help</code> na vypsání všech příkazů. Nebo jste text chtěli odeslat jako zprávu?",
"Hint: Begin your message with <code>//</code> to start it with a slash.": "Tip: Zprávu můžete začít <code>//</code>, pokud chcete aby začínala lomítkem.",
"Send as message": "Odeslat jako zprávu",
"Waiting for %(displayName)s to accept…": "Čekáme, než %(displayName)s přijme…",
"Start Verification": "Začít s verifikací",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Vaše zprávy jsou zabezpečené - pouze Vy a příjemce mají klíče k jejich přečtení.",
"Verify User": "Ověřit uživatele",
"For extra security, verify this user by checking a one-time code on both of your devices.": "Pro lepší bezpečnost, ověřte uživatele zkontrolováním jednorázového kódu na vašich zařízeních.",
"Your messages are not secure": "Vaše zprávy nejsou zabezpečené",
"One of the following may be compromised:": "Něco z následujích věcí může být kompromitováno:",
"Your homeserver": "Váš domovský server",
"The homeserver the user youre verifying is connected to": "Domovský server ověřovaného uživatele",
"Yours, or the other users internet connection": "Vaše připojení k internetu a nebo připojení ověřovaného uživatele",
"Yours, or the other users session": "Vaše relace a nebo relace ověřovaného uživatele",
"%(count)s sessions|other": "%(count)s relací",
"%(count)s sessions|one": "%(count)s relace",
"Hide sessions": "Skrýt relace",
"Verify by emoji": "Ověřit emotikonami",
"Verify by comparing unique emoji.": "Oveření porovnáním několika emotikon.",
"Ask %(displayName)s to scan your code:": "Ověříte se, když %(displayName)s naskenuje váš kód:",
"If you can't scan the code above, verify by comparing unique emoji.": "Pokud vám skenování kódů nefunguje, ověřte se porovnáním emotikon.",
"You've successfully verified %(displayName)s!": "%(displayName)s je úspěšně ověřen/a!",
"Got it": "Dobře",
"Verification timed out. Start verification again from their profile.": "Čas na verifikaci vypršel. Začněte s verifikací znovu z druhého profilu.",
"%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s zrušil/a verifikaci. Začněte s verifikací znovu z druhého profilu.",
"Encryption enabled": "Šifrování je zapnuté",
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Zprávy v této místnosti jsou E2E šifrované. Více informací & verifikace uživatele je v jeho profilu.",
"Encryption not enabled": "Šifrování je vypnuté",
"The encryption used by this room isn't supported.": "Šifrování používané v této místnosti není podporované.",
"Clear all data in this session?": "Smazat všechna data v této relaci?",
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Výmaz všech dat v relaci je nevratný. Pokud nemáte zálohované šifrovací klíče, přijdete o šifrované zprávy.",
"Verify session": "Ověřit relaci",
"To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Abychom ověřili, že je relace důvěryhodná, zkontrolujte prosím, že klíč v uživatelském nastavení na tom zařízení odpovídá následujícímu klíči:",
"To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Abychom ověřili, že tato relace je důvěryhodná, kontaktujte prosím jejího vlastníka nějakým dalším způsobem (osobně, telefonicky, apod.). Zkontrolujte spolu, že klíč v uživatelském nastavení na druhém zařízení odpovídá následujícímu klíči:",
"Session name": "Jméno relace",
"Session key": "Klíč relace",
"If it matches, press the verify button below. If it doesn't, then someone else is intercepting this session and you probably want to press the blacklist button instead.": "Pokud odpovídá, zmáčkněte tlačítko ověřit. Pokud ne, druhé zařízení je pravděpodobně falešné a chcete ho zablokovat.",
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Ověření uživatele označí jeho relace za důvěryhodné a Vaše relace budou důvěryhodné pro něj.",
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Ověření zařízení ho označí za důvěryhodné. Ověření konkrétního zařízení vám dát trochu klidu mysli navíc při používání šifrovaných zpráv.",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Ověření zařízení ho označí za důvěryhodné a uživatelé, kteří věří vám budou také tomuto zařízení důvěřovat.",
"Failed to invite the following users to chat: %(csvUsers)s": "Následující uživatele se nepovedlo pozvat do chatu: %(csvUsers)s",
"We couldn't create your DM. Please check the users you want to invite and try again.": "Nepovedlo se nám vyrobit soukromou konverzaci. Zkontrolujte prosím, že pozvaný uživatel opravdu existuje a pak to zkuste znovu.",
"Something went wrong trying to invite the users.": "Při odesílání pozvánek se něco pokazilo.",
"We couldn't invite those users. Please check the users you want to invite and try again.": "Nemůžeme pozvat tyto uživatele. Zkontrolujte prosím, že opravdu existují a zkuste to znovu.",
"Failed to find the following users": "Nepovedlo se najít následující uživatele",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Následující uživatelé asi neexistují nebo jsou neplatní a nelze je pozvat: %(csvNames)s",
"Recent Conversations": "Nedávné konverzace",
"Suggestions": "Návrhy",
"Recently Direct Messaged": "Nedávno kontaktovaní",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Pokud někoho nemůžete nalézt, zeptejte se na username, pošlete jim svůj (%(userId)s) a nebo pošlete <a>odkaz na svůj profil</a>.",
"Go": "Ok",
"If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.": "Pokud někoho nemůžete nalézt, zeptejte se na username (například @user:server.com) a nebo pošlete <a>odkaz na tuto místnost</a>.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "Přidali jste novou relaci '%(displayName)s', která požaduje šifrovací klíče.",
"Your unverified session '%(displayName)s' is requesting encryption keys.": "Vaše neověřená relace '%(displayName)s' požaduje šifrovací klíče.",
"Loading session info...": "Načítám informace o relaci...",
"New session": "Nová relace",
"Use this session to verify your new one, granting it access to encrypted messages:": "Použijte tuto relaci abyste ověřili novou a dali jí přístup ke svým šifrovaným zprávám:",
"If you didnt sign in to this session, your account may be compromised.": "Pokud jste se do této nové relace nepřihlásili, váš účet může být kompromitován.",
"This wasn't me": "Nebyl/a jsem to já",
"This will allow you to return to your account after signing out, and sign in on other sessions.": "Umožní vám to se přihlásit do dalších relací a vrátit se ke svému účtu poté co se odhlásíte.",
"You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "Máte zakázané posílání zpráv neověřeným relacím; abyste mohli zprávu odeslat, musíte je ověřit.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Doporučujeme si projít ověřovací proces pro každou relaci abyste ověřili, že patří tomu, komu očekáváte. Můžete ale poslat zprávu i bez ověření, pokud chcete.",
"Room contains unknown sessions": "V místnosti jsou neověřené relace",
"\"%(RoomName)s\" contains sessions that you haven't seen before.": "V místnosti \"%(RoomName)s\" jsou relace, které jste ještě nikdy neviděli.",
"Unknown sessions": "Neznámá relace",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Zadejte svoje heslo a získejte přístup k bezpečnému úložišti historie zpráv a k identitě pro cross-signing pro snadné ověřování vašich relací.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Zadejte svůj obnovovací klíč a získejte přístup k bezpečnému úložišti historie zpráv a k identitě pro cross-signing pro snadné ověřování vašich relací.",
"Recovery key mismatch": "Obnovovací klíč neodpovídá",
"Incorrect recovery passphrase": "Nesprávné heslo pro obnovení",
"Backup restored": "Záloha byla nahrána",
"Enter recovery passphrase": "Zadejte heslo pro obnovení zálohy",
"Enter recovery key": "Zadejte obnovovací klíč",
"Confirm your identity by entering your account password below.": "Potvrďte svou identitu zadáním hesla ke svému účtu.",
"Message not sent due to unknown sessions being present": "Zpráva nebyla odeslána, protože jsou v místnosti neznámé relace",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Zobrazit relace</showSessionsText>, <sendAnywayText>i tak odeslat</sendAnywayText>, a nebo <cancelText>zrušit</cancelText>.",
"Verify this session to grant it access to encrypted messages.": "Ověřte relaci aby získala přístup k šifrovaným zprávám.",
"Start": "Začít",
"Session verified": "Relace je ověřena",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Vaše nová relace je teď ověřená. Má přístup k šifrovaným zprávám a ostatní uživatelé jí budou věřit.",
"Your new session is now verified. Other users will see it as trusted.": "Vaše nová relace je teď ověřená. Ostatní uživatelé jí budou věřit.",
"Done": "Hotovo",
"Without completing security on this session, it wont have access to encrypted messages.": "Bez dokončení ověření nebude mít nová relace přístup k šifrovaným zprávám.",
"Go Back": "Zpět",
"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.": "Změna hesla resetuje šifrovací klíče ve všech vašich přihlášených relacích a přijdete tak o přístup k historickým zprávám. Před změnou hesla si nastavte zálohu klíčů nebo si klíče pro místnosti exportujte.",
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Všude jsme Vás odhlásili, takže nedostáváte žádné upozornění. Můžete je znovu povolit tím, že se na všech svych zařízeních znovu přihlásíte.",
"Regain access to your account and recover encryption keys stored in this session. Without them, you wont be able to read all of your secure messages in any session.": "Získejte znovu přístup k účtu a obnovte si šifrovací klíče uložené v této relaci. Bez nich nebudete schopni číst zabezpečené zprávy na některých zařízeních.",
"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.": "Varování: Vaše osobní data (včetně šifrovacích klíčů) jsou tu pořád uložena. Smažte je, pokud chcete tuto relaci zahodit nebo se přihlašte pod jiný účet.",
"Sender session information": "Informace o relaci odesílatele",
"If you cancel now, you won't complete verifying the other user.": "Pokud teď proces zrušíte, tak nebude druhý uživatel ověřen.",
"If you cancel now, you won't complete verifying your other session.": "Pokud teď proces zrušíte, tak nebude druhá relace ověřena.",
"If you cancel now, you won't complete your secret storage operation.": "Pokud teď proces zrušíte, tak se nedokončí operace s bezpečným úložištěm.",
"Cancel entering passphrase?": "Zrušit zadávání hesla?",
"Setting up keys": "Příprava klíčů",
"Riot is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom Riot Desktop with <nativeLink>search components added</nativeLink>.": "Riotu chybí nějaké komponenty, které jsou potřeba pro vyhledávání v zabezpečených místnostech. Pokud chcete s touto funkcí experimentovat, tak si pořiďte vlastní Riot Desktop s <nativeLink>přidanými komponentami</nativeLink>.",
"Subscribing to a ban list will cause you to join it!": "Odebíráním seznamu zablokovaných uživatelů se přidáte do jeho místnosti!",
"If this isn't what you want, please use a different tool to ignore users.": "Pokud to nechcete, tak prosím použijte jiný nástroj na blokování uživatelů.",
"You cancelled verification. Start verification again from their profile.": "Zrušili jste verifikaci. Můžete jí začít znovu z druhého profilu.",
"Complete security": "Dokončení ověření",
"Restore your key backup to upgrade your encryption": "Pro aktualizaci šifrování obnovte klíče ze zálohy",
"Restore": "Obnovit",
"Enter your account password to confirm the upgrade:": "Potvrďte, že chcete aktualizaci provést zadáním svého uživatelského hesla:",
"You'll need to authenticate with the server to confirm the upgrade.": "Server si vás potřebuje ověřit, abychom mohli provést aktualizaci.",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Aktualizujte tuto přihlášenou relaci abyste mohli ověřovat ostatní relace. Tím jim dáte přístup k šifrovaným konverzacím a ostatní uživatelé je jim budou automaticky věřit.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Zapněte v této přihlášené relaci šifrování abyste mohli ověřovat ostatní relace. Tím jim dáte přístup k šifrovaným konverzacím a ostatní uživatelé je jim budou automaticky věřit.",
"Show typing notifications": "Zobrazovat notifikace \"... právě píše...\"",
"Reset cross-signing and secret storage": "Resetovat bezpečné úložiště a cross-signing",
"Destroy cross-signing keys?": "Nenávratně smazat klíče pro cross-signing?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Smazání klíčů pro cross-signing je definitivní. Každý kdo vás ověřil teď uvidí bezpečnostní varování. Pokud jste zrovna neztratili všechna zařízení ze kterých se můžete ověřit, tak to asi nechcete udělat.",
"Clear cross-signing keys": "Smazat klíče pro cross-signing",
"Secure your encryption keys with a passphrase. For maximum security this should be different to your account password:": "Zabezpečte si šifrovací klíče silným heslem. Pro lepší bezpečnost by mělo být jiné než vaše heslo k přihlášení:",
"Enter a passphrase": "Zadejte heslo",
"The version of Riot": "Verze Riotu",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Jestli používáte Riot na dotykovém zařízení",
"Whether you're using Riot as an installed Progressive Web App": "Jestli používáte Riot jako nainstalovanou Progresivní Webovou Aplikaci",
"Your user agent": "Identifikace vašeho prohlížeče",
"The information being sent to us to help make Riot better includes:": "Abychom mohli Riot zlepšovat si posíláme následující informace:",
"Verify this session by completing one of the following:": "Ověřte tuto relaci dokončením jednoho z následujících:",
"Scan this unique code": "Naskenujte tento jedinečný kód",
"or": "nebo",
"Compare unique emoji": "Porovnejte jedinečnou kombinaci emoji",
"Compare a unique set of emoji if you don't have a camera on either device": "Porovnejte jedinečnou kombinaci emoji pokud na žádném zařízení nemáte kameru",
"Not Trusted": "Nedůvěryhodné",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) se přihlásil do nové relace a neověřil ji:",
"Ask this user to verify their session, or manually verify it below.": "Poproste tohoto uživatele aby svojí relaci ověřil a nebo jí níže můžete ověřit manuálně.",
"Manually Verify": "Ověřit manuálně",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "Relace, kterou se snažíte ověřit neumožňuje ověření QR kódem ani pomocí emoji, což je to co Riot podporuje. Zkuste použít jiného klienta.",
"Verify by scanning": "Ověřte naskenováním",
"You declined": "Odmítli jste",
"%(name)s declined": "%(name)s odmítl/a",
"accepting …": "přijímání …",
"declining …": "odmítání …",
"Back up my encryption keys, securing them with the same passphrase": "Zazálohovat šifrovací klíče zabezpečené tím stejným heslem",
"Enter your passphrase a second time to confirm it.": "Pro ověření zadejte své heslo podruhé.",
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Uschovejte si kopii na bezpečném místě, například ve správci hesel nebo v trezoru.",
"Your recovery key": "Váš obnovovací klíč",
"Copy": "Zkopírovat",
"You can now verify your other devices, and other users to keep your chats safe.": "Teď můžete ověřit své ostatní zařízení a další uživatelé, aby vaše komunikace byla bezpečná.",
"Upgrade your encryption": "Aktualizovat šifrování",
"Make a copy of your recovery key": "Vytvořit kopii svého obnovovacího klíče",
"You're done!": "Hotovo!",
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Bez nastavení Bezpečného Obnovení Zpráv nebudete moci obnovit historii šifrovaných zpráv pokud se odhlásíte nebo použijete jinou relaci.",
"Create key backup": "Vytvořit zálohu klíčů",
"This session is encrypting history using the new recovery method.": "Tato relace šifruje historii zpráv s podporou nového způsobu obnovení.",
"This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Tato relace zjistila, že klíč a heslo k obnovení zpráv byly odstraněny.",
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Pokud se vám to stalo neůmyslně, můžete znovu nastavit zálohu zpráv pro tuto relaci. To znovu zašifruje historii zpráv novým způsobem.",
"If disabled, messages from encrypted rooms won't appear in search results.": "Když je to zakázané, zprávy v šifrovaných místnostech se nebudou objevovat ve výsledcích vyhledávání.",
"Disable": "Zakázat",
"Not currently downloading messages for any room.": "Aktuálně se nestahují žádné zprávy.",
"Downloading mesages for %(currentRoom)s.": "Stahují se zprávy pro %(currentRoom)s.",
"Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot si bezpečně uchovává šifrované zprávy lokálně, aby v nich mohl vyhledávat:",
"Space used:": "Použitý prostor:",
"Indexed messages:": "Indexované zprávy:",
"Indexed rooms:": "Indexované místnosti:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s z %(totalRooms)s",
"Message downloading sleep time(ms)": "Čas na stažení zprávy (ms)"
}

View file

@ -516,5 +516,8 @@
"Multiple integration managers": "Flere integrationsmanagere",
"Enable Emoji suggestions while typing": "Aktiver emoji forslag under indtastning",
"Use compact timeline layout": "Brug kompakt tidslinje",
"Show a placeholder for removed messages": "Vis en pladsholder for fjernede beskeder"
"Show a placeholder for removed messages": "Vis en pladsholder for fjernede beskeder",
"The version of Riot": "Riot versionen",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Hvorvidt du benytter Riot på en enhed, hvor touch er den primære input-grænseflade",
"Your user agent": "Din user agent"
}

View file

@ -869,7 +869,7 @@
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Bitte hilf uns Riot.im zu verbessern, in dem du <UsageDataLink>anonyme Nutzungsdaten</UsageDataLink> schickst. Dies wird ein Cookie benutzen (bitte beachte auch unsere <PolicyLink>Cookie-Richtlinie</PolicyLink>).",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Bitte hilf uns Riot.im zu verbessern, in dem du <UsageDataLink>anonyme Nutzungsdaten</UsageDataLink> schickst. Dies wird ein Cookie benutzen.",
"Yes, I want to help!": "Ja, ich möchte helfen!",
"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>": "Dies wird deinen Account permanent unbenutzbar machen. Du wirst nicht in der Lage sein, dich anzumelden und keiner wird dieselbe Benutzer-ID erneut registrieren können. Alle Räume, in denen der Account ist, werden verlassen und deine Account-Daten werden vom Identitätsserver gelöscht. <b>Diese Aktion ist irreversibel!</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>": "Dies wird deinen Account permanent unbenutzbar machen. Du wirst nicht in der Lage sein, dich anzumelden und keiner wird dieselbe Benutzer-ID erneut registrieren können. Alle Räume, in denen der Account ist, werden verlassen und deine Account-Daten werden vom Identitätsserver gelöscht. <b>Diese Aktion ist unumkehrbar.</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.": "Standardmäßig werden <b>die von dir gesendeten Nachrichten beim Deaktiveren nicht gelöscht</b>. Wenn du dies von uns möchtest, aktivere das Auswalfeld unten.",
"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.": "Sie Sichtbarkeit der Nachrichten in Matrix ist vergleichbar mit E-Mails: Wenn wir deine Nachrichten vergessen heißt das, dass diese nicht mit neuen oder nicht registrierten Nutzern teilen werden, aber registrierte Nutzer, die bereits zugriff haben, werden Zugriff auf ihre Kopie behalten.",
"Please forget all messages I have sent when my account is deactivated (<b>Warning:</b> this will cause future users to see an incomplete view of conversations)": "Bitte vergesst alle Nachrichten, die ich gesendet habe, wenn mein Account deaktiviert wird. (<b>Warnung:</b> Zukünftige Nutzer werden eine unvollständige Konversation sehen)",
@ -892,7 +892,7 @@
"No Audio Outputs detected": "Keine Ton-Ausgabe erkannt",
"Audio Output": "Ton-Ausgabe",
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In verschlüsselten Räumen, wie diesem, ist die Link-Vorschau standardmäßig deaktiviert damit dein Heimserver (auf dem die Vorschau erzeugt wird) keine Informationen über Links in diesem Raum bekommt.",
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Wenn jemand eine Nachricht mit einem Link schickt, kann die Link-Vorschau mehr Informationen, wie Titel, Beschreibung und Bild der Webseite, über den Link anzeigen.",
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Wenn jemand eine URL in seine Nachricht einfügt, kann eine URL-Vorschau angezeigt werden, um mehr Informationen über diesen Link zu erhalten, wie z.B. den Titel, die Beschreibung und ein Bild von der Website.",
"The email field must not be blank.": "Das E-Mail-Feld darf nicht leer sein.",
"The phone number field must not be blank.": "Das Telefonnummern-Feld darf nicht leer sein.",
"The password field must not be blank.": "Das Passwort-Feld darf nicht leer sein.",
@ -945,9 +945,9 @@
"%(senderName)s removed the main address for this room.": "%(senderName)s entfernte die Hauptadresse von diesem Raum.",
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s fügte %(addedAddresses)s hinzu und entfernte %(removedAddresses)s als Adressen von diesem Raum.",
"Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.": "Bevor du Log-Dateien übermittelst, musst du ein <a>GitHub-Issue erstellen</a> um dein Problem zu beschreiben.",
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot benutzt nun 3-5x weniger Arbeitsspeicher, indem Informationen über andere Nutzer erst bei Bedarf geladen werden. Bitte warte, während die Daten erneut mit dem Server abgeglichen werden!",
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot benutzt nun 3-5-mal weniger Arbeitsspeicher, indem Informationen über andere Nutzer erst bei Bedarf geladen werden. Bitte warte, während die Daten erneut mit dem Server abgeglichen werden!",
"Updating Riot": "Aktualisiere Riot",
"You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "Du hast zuvor Riot auf %(host)s ohne verzögertem Laden von Mitgliedern genutzt. In dieser Version war das verzögerte Laden deaktiviert. Da die lokal zwischengespeicherten Daten zwischen diesen Einstellungen nicht kompatibel ist, muss Riot dein Konto neu synchronisieren.",
"You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "Du hast zuvor Riot auf %(host)s ohne das verzögerte Laden von Mitgliedern genutzt. In dieser Version war das verzögerte Laden deaktiviert. Da die lokal zwischengespeicherten Daten zwischen diesen Einstellungen nicht kompatibel sind, muss Riot dein Konto neu synchronisieren.",
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Wenn Riot mit der alten Version in einem anderen Tab geöffnet ist, schließe dies bitte, da das parallele Nutzen von Riot auf demselben Host mit aktivierten und deaktivierten verzögertem Laden, Probleme verursachen wird.",
"Incompatible local cache": "Inkompatibler lokaler Zwischenspeicher",
"Clear cache and resync": "Zwischenspeicher löschen und erneut synchronisieren",
@ -960,7 +960,7 @@
"Delete Backup": "Sicherung löschen",
"Backup version: ": "Sicherungsversion: ",
"Algorithm: ": "Algorithmus: ",
"To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "Um deinen Chatverlauf nicht zu verlieren, musst du deine Raum-Schlüssel exportieren, bevor du dich abmeldest. Du musst zurück zu einer neueren Riot-Version gehen, um dies zu tun",
"To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "Um zu vermeiden, dass Ihr Chat-Verlauf verloren geht, müssen Sie Ihre Raum-Schlüssel exportieren, bevor Sie sich abmelden. Dazu müssen Sie auf die neuere Version von Riot zurückgehen",
"Incompatible Database": "Inkompatible Datenbanken",
"Continue With Encryption Disabled": "Mit deaktivierter Verschlüsselung fortfahren",
"Enter a passphrase...": "Passphrase eingeben...",
@ -984,7 +984,7 @@
"Backup Restored": "Sicherung wiederhergestellt",
"Enter Recovery Passphrase": "Gebe Wiederherstellungs-Passphrase ein",
"Enter Recovery Key": "Gebe Wiederherstellungsschlüssel ein",
"This looks like a valid recovery key!": "Dies sieht nach einem validen Wiederherstellungsschlüssel aus",
"This looks like a valid recovery key!": "Dies sieht wie ein gültiger Wiederherstellungsschlüssel aus!",
"Not a valid recovery key": "Kein valider Wiederherstellungsschlüssel",
"There was an error joining the room": "Es gab einen Fehler beim Raum-Beitreten",
"Use a few words, avoid common phrases": "Benutze einige Worte, vermeide gängige Phrasen",
@ -1026,7 +1026,7 @@
"Unable to load key backup status": "Konnte Status des Schlüsselbackups nicht laden",
"Don't ask again": "Nicht erneut fragen",
"Set up": "Einrichten",
"Please review and accept all of the homeserver's policies": "Bitte sieh dir die Heimserver-Regularien an und akzeptiere sie",
"Please review and accept all of the homeserver's policies": "Bitte prüfen und akzeptieren Sie alle Richtlinien des Heimservers",
"Failed to load group members": "Konnte Gruppenmitglieder nicht laden",
"That doesn't look like a valid email address": "Sieht nicht nach einer validen E-Mail-Adresse aus",
"Unable to load commit detail: %(msg)s": "Konnte Commit-Details nicht laden: %(msg)s",
@ -1034,10 +1034,10 @@
"Unable to load backup status": "Konnte Backupstatus nicht laden",
"Failed to decrypt %(failedCount)s sessions!": "Konnte %(failedCount)s Sitzungen nicht entschlüsseln!",
"Restored %(sessionCount)s session keys": "%(sessionCount)s Sitzungsschlüssel wiederhergestellt",
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Greife auf deinen sicheren Chatverlauf zu und richte sicheres Schreiben ein indem du deine Wiederherstellungspassphrase eingibst.",
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Greifen Sie auf Ihre sichere Nachrichtenhistorie zu und richten Sie einen sicheren Nachrichtenversand ein, indem Sie Ihre Wiederherstellungspassphrase eingeben.",
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "Wenn du deinen Wiederherstellungspassphrase vergessen hast, kannst du <button1>deinen Wiederherstellungsschlüssel benutzen</button1> oder <button2>neue Wiederherstellungsoptionen einrichten</button2>",
"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. ": "Du hast kürzlich eine neuere Version von Riot auf %(host)s verwendet. Um diese Version erneut mit Ende-zu-Ende-Verschlüsselung zu nutzen, musst du dich ab- und wieder anmelden. ",
"Access your secure message history and set up secure messaging by entering your recovery key.": "Auf sichere Nachrichtenhistorie zugreifen und sicheren Nachrichtenversand einrichten indem du deinen Wiederherstellungsschlüssel eingibst.",
"Access your secure message history and set up secure messaging by entering your recovery key.": "Greifen Sie auf Ihren sicheren Nachrichtenverlauf zu und richten Sie durch Eingabe Ihres Wiederherstellungsschlüssels einen sicheren Nachrichtenversand ein.",
"Set a new status...": "Setze einen neuen Status...",
"Clear status": "Status löschen",
"Invalid homeserver discovery response": "Ungültige Antwort beim Aufspüren des Heimservers",
@ -1051,10 +1051,10 @@
"Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Ohne Sichere Nachrichten-Wiederherstellung einzurichten, wirst du deine sichere Nachrichtenhistorie verlieren, wenn du dich abmeldest.",
"If you don't want to set this up now, you can later in Settings.": "Wenn du dies jetzt nicht einrichten willst, kannst du dies später in den Einstellungen tun.",
"New Recovery Method": "Neue Wiederherstellungsmethode",
"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.": "Wenn du die neue Wiederherstellungsmethode nicht eingerichtet hast, kann ein Angreifer versuchen Zugang zu deinem Konto zu erlangen. Ändere sofort dein Passwort und setze eine neue Wiederherstellungsmethode.",
"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.": "Wenn Sie die neue Wiederherstellungsmethode nicht festgelegt haben, versucht ein Angreifer möglicherweise, auf Ihr Konto zuzugreifen. Ändern Sie Ihr Kontopasswort und legen Sie sofort eine neue Wiederherstellungsmethode in den Einstellungen fest.",
"Set up Secure Messages": "Richte sichere Nachrichten ein",
"Go to Settings": "Gehe zu Einstellungen",
"Sign in with single sign-on": "Mit Single Sign-On anmelden",
"Sign in with single sign-on": "Melden Sie sich mit Single Sign-On an",
"Waiting for %(userId)s to confirm...": "Warte auf Bestätigung für %(userId)s ...",
"Unrecognised address": "Nicht erkannte Adresse",
"User %(user_id)s may or may not exist": "Existenz der Benutzer %(user_id)s unsicher",
@ -1246,11 +1246,11 @@
"Manually export keys": "Manueller Schlüssel Export",
"Composer": "Nachrichteneingabefeld",
"Nothing appearing? Not all clients support interactive verification yet. <button>Use legacy verification</button>.": "Es ist nichts aufgetaucht? Noch nicht alle Clients unterstützen die interaktive Verifikation. <button>Nutze alte Verifikation</button>.",
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verifiziere diesen Benutzer und markiere ihn als \"vertraut\". Dies gibt dir bei Ende-zu-Ende-verschlüsselten Nachrichten extra Seelenfrieden.",
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Überprüfen Sie diesen Benutzer, um ihn als vertrauenswürdig zu kennzeichnen. Benutzern zu vertrauen gibt Ihnen zusätzliche Sicherheit bei der Verwendung von Ende-zu-Ende-verschlüsselten Nachrichten.",
"I don't want my encrypted messages": "Ich möchte meine verschlüsselten Nachrichten nicht",
"You'll lose access to your encrypted messages": "Du wirst den Zugang zu deinen verschlüsselten Nachrichten verlieren",
"If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Wenn du Fehler bemerkst oder eine Rückmeldung geben möchtest, teile dies uns auf GitHub mit.",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "Um doppelte Issues zu vermeiden, <existingIssuesLink>schau dir zuerst die existierenden Issues an</existingIssuesLink> (und füge hinzu oder <newIssueLink>erstelle ein neues Issue</newIssueLink>, wenn du keins finden kannst.",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "Um doppelte Issues zu vermeiden, <existingIssuesLink>schauen Sie bitte zuerst die existierenden Issues an</existingIssuesLink> (und fügen Sie ein \"+1\" hinzu), oder <newIssueLink>erstellen Sie ein neues Issue</newIssueLink>, wenn Sie keines finden können.",
"Report bugs & give feedback": "Melde Fehler & gebe Rückmeldungen",
"Recovery Key Mismatch": "Wiederherstellungsschlüssel passt nicht",
"Update status": "Aktualisiere Status",
@ -1302,7 +1302,7 @@
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Gib die Adresse deines Modular-Heimservers an. Es kann deine eigene Domain oder eine Subdomain von <a>modular.im</a> sein.",
"Unable to query for supported registration methods.": "Konnte unterstützte Registrierungsmethoden nicht abrufen.",
"Bulk options": "Sammeloptionen",
"Join millions for free on the largest public server": "Tritt wie Millionen andere kostenlos dem größten öffentlichen Server bei",
"Join millions for free on the largest public server": "Schließen Sie sich auf dem größten öffentlichen Server kostenlos Millionen von Menschen an",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Fügt ¯\\_(ツ)_/¯ vor einer Klartextnachricht ein",
"Changes your display nickname in the current room only": "Ändert den Anzeigenamen ausschließlich für den aktuellen Raum",
"%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s aktivierte Abzeichen der Gruppen %(groups)s für diesen Raum.",
@ -1542,5 +1542,104 @@
"Unsubscribe": "Deabonnieren",
"View rules": "Regeln betrachten",
"You are currently subscribed to:": "Du abonnierst momentan:",
"⚠ These settings are meant for advanced users.": "⚠ Diese Einstellungen sind für fortgeschrittene Nutzer gedacht."
"⚠ These settings are meant for advanced users.": "⚠ Diese Einstellungen sind für fortgeschrittene Nutzer gedacht.",
"The version of Riot": "Die Version von Riot",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Ob Sie Riot auf einem Gerät verwenden, bei dem Berührung der primäre Eingabemechanismus ist",
"Whether you're using Riot as an installed Progressive Web App": "Ob Sie Riot als installierte progressive Web-App verwenden",
"Your user agent": "Ihr User-Agent",
"The information being sent to us to help make Riot better includes:": "Zu den Informationen, die uns zugesandt werden, um zu helfen, Riot besser zu machen, gehören:",
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Es sind unbekannte Sitzungen in diesem Raum: Wenn Sie ohne Verifizierung fortfahren, wird es für jemanden möglich sein, Ihren Anruf zu belauschen.",
"If you cancel now, you won't complete verifying the other user.": "Wenn Sie jetzt abbrechen, werden Sie die Verifizierung des anderen Nutzers nicht beenden können.",
"If you cancel now, you won't complete verifying your other session.": "Wenn Sie jetzt abbrechen, werden Sie die Verifizierung der anderen Sitzung nicht beenden können.",
"If you cancel now, you won't complete your secret storage operation.": "Wenn Sie jetzt abbrechen, werden Sie die Geheimlagerungsoperation nicht beenden können.",
"Cancel entering passphrase?": "Eingabe der Passphrase abbrechen?",
"Setting up keys": "Einrichten der Schlüssel",
"Encryption upgrade available": "Verschlüsselungs-Update verfügbar",
"Unverified session": "Ungeprüfte Sitzung",
"Verifies a user, session, and pubkey tuple": "Verifiziert einen Benutzer, eine Sitzung und Pubkey-Tupel",
"Unknown (user, session) pair:": "Unbekanntes (Nutzer-, Sitzungs-) Paar:",
"Session already verified!": "Sitzung bereits verifiziert!",
"WARNING: Session already verified, but keys do NOT MATCH!": "ACHTUNG: Sitzung bereits verifiziert, aber die Schlüssel passen NICHT ZUSAMMEN!",
"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!": "ACHTUNG: SCHLÜSSEL-VERIFIZIERUNG FEHLGESCHLAGEN! Der Signierschlüssel für %(userId)s und Sitzung %(deviceId)s ist \"%(fprint)s\", was nicht mit dem bereitgestellten Schlüssel \"%(fingerprint)s\" übereinstimmt. Das könnte bedeuten, dass Ihre Kommunikation abgehört wird!",
"Never send encrypted messages to unverified sessions from this session": "Sende niemals verschlüsselte Nachrichten von dieser Sitzung zu unverifizierten Sitzungen",
"Never send encrypted messages to unverified sessions in this room from this session": "Sende niemals verschlüsselte Nachrichten von dieser Sitzung zu unverifizierten Sitzungen in diesem Raum",
"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.": "Durch die Änderung des Passworts werden derzeit alle End-zu-End-Verschlüsselungsschlüssel in allen Sitzungen zurückgesetzt, sodass der verschlüsselte Chat-Verlauf nicht mehr lesbar ist, es sei denn, Sie exportieren zuerst Ihre Raumschlüssel und importieren sie anschließend wieder. In Zukunft wird dies verbessert werden.",
"Delete %(count)s sessions|other": "Lösche %(count)s Sitzungen",
"Backup is not signed by any of your sessions": "Die Sicherung ist von keiner Ihrer Sitzungen unterzeichnet.",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Ihr Passwort wurde erfolgreich geändert. Sie erhalten keine Push-Benachrichtigungen zu anderen Sitzungen, bis Sie sich wieder bei diesen anmelden",
"Sessions": "Sitzungen",
"Notification sound": "Benachrichtigungston",
"Set a new custom sound": "Setze einen neuen benutzerdefinierten Sound",
"Browse": "Durchsuche",
"Direct Messages": "Direkte Nachrichten",
"You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Sie können <code>/help</code> benutzen, um verfügbare Befehle aufzulisten. Wollten Sie dies als Nachricht senden?",
"Direct message": "Direkte Nachricht",
"Set a room alias to easily share your room with other people.": "Setze ein Raum-Alias, um deinen Raum einfach mit anderen Personen zu teilen.",
"Suggestions": "Vorschläge",
"Recently Direct Messaged": "Kürzlich direkt verschickt",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Wenn Sie niemanden finden können, fragen Sie nach deren Benutzernamen, teilen Sie ihren Benutzernamen (%(userId)s) oder <a>Profil-Link</a>.",
"Go": "Los",
"If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.": "Wenn Sie niemanden finden können, fragen Sie nach deren Benutzernamen (z.B. @benutzer:server.de) oder <a>teilen Sie diesen Raum</a>.",
"Command Help": "Befehl Hilfe",
"To help us prevent this in future, please <a>send us logs</a>.": "Um uns zu helfen, dies in Zukunft zu vermeiden, <a>senden Sie uns bitte Logs</a>.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Wir empfehlen Ihnen, den Verifizierungsprozess für jede Sitzung zu durchlaufen, um zu bestätigen, dass sie ihrem rechtmäßigen Eigentümer gehören, aber Sie können die Nachricht auch ohne Verifizierung erneut senden, wenn Sie dies bevorzugen.",
"Notification settings": "Benachrichtigungseinstellungen",
"Help": "Hilfe",
"Filter": "Filter",
"Filter rooms…": "Räume filtern…",
"You have %(count)s unread notifications in a prior version of this room.|one": "Sie haben %(count)s ungelesene Benachrichtigungen in einer früheren Version dieses Raumes.",
"Go Back": "Gehe zurück",
"Notification Autocomplete": "Benachrichtigung Autovervollständigen",
"If disabled, messages from encrypted rooms won't appear in search results.": "Wenn deaktiviert, werden Nachrichten von verschlüsselten Räumen nicht in den Ergebnissen auftauchen.",
"This user has not verified all of their sessions.": "Dieser Benutzer hat nicht alle seine Sitzungen verifiziert.",
"You have verified this user. This user has verified all of their sessions.": "Sie haben diesen Benutzer verifiziert. Dieser Benutzer hat alle seine Sitzungen verifiziert.",
"Some sessions for this user are not trusted": "Einige Sitzungen für diesen Benutzer sind nicht vertrauenswürdig",
"All sessions for this user are trusted": "Alle Sitzungen für diesen Benutzer sind vertrauenswürdig",
"Some sessions in this encrypted room are not trusted": "Einige Sitzungen in diesem verschlüsselten Raum sind nicht vertrauenswürdig",
"All sessions in this encrypted room are trusted": "Alle Sitzungen in diesem verschlüsselten Raum sind vertrauenswürdig",
"Your key share request has been sent - please check your other sessions for key share requests.": "Ihre Anfrage zur Schlüssel-Teilung wurde gesendet - bitte überprüfen Sie Ihre anderen Sitzungen auf Anfragen zur Schlüssel-Teilung.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Anfragen zum Teilen von Schlüsseln werden automatisch an Ihre anderen Sitzungen gesendet. Wenn Sie die Anfragen zum Teilen von Schlüsseln in Ihren anderen Sitzungen abgelehnt oder abgewiesen haben, klicken Sie hier, um die Schlüssel für diese Sitzung erneut anzufordern.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Wenn Ihre anderen Sitzungen nicht über den Schlüssel für diese Nachricht verfügen, können Sie sie nicht entschlüsseln.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Fordern Sie Verschlüsselungsschlüssel aus Ihren anderen Sitzungen erneut an</requestLink>.",
"No sessions with registered encryption keys": "Keine Sitzungen mit registrierten Verschlüsselungsschlüsseln",
"Room %(name)s": "Raum %(name)s",
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Ein Upgrade dieses Raums schaltet die aktuelle Instanz des Raums ab und erstellt einen aktualisierten Raum mit demselben Namen.",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) hat sich zu einer neuen Sitzung angemeldet, ohne sie zu überprüfen:",
"%(count)s verified sessions|other": "%(count)s verifizierte Sitzungen",
"Hide verified sessions": "Verifizierte Sitzungen ausblenden",
"%(count)s sessions|other": "%(count)s Sitzungen",
"Hide sessions": "Sitzungen ausblenden",
"Encryption enabled": "Verschlüsselung aktiviert",
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Nachrichten in diesem Raum sind Ende-zu-Ende verschlüsselt. Erfahren Sie mehr & überprüfen Sie diesen Benutzer in seinem Benutzerprofil.",
"Encryption not enabled": "Verschlüsselung nicht aktiviert",
"You verified %(name)s": "Du hast %(name)s verifiziert",
"You cancelled verifying %(name)s": "Sie haben die Verifizierung von %(name)s abgebrochen",
"%(name)s cancelled verifying": "%(name)s hat die Verifizierung abgebrochen",
"%(name)s accepted": "%(name)s hat akzeptiert",
"%(name)s declined": "%(name)s hat abgelehnt",
"%(name)s cancelled": "%(name)s hat abgebrochen",
"%(name)s wants to verify": "%(name)s will eine Verifizierung",
"Your display name": "Ihr Anzeigename",
"Please enter a name for the room": "Bitte geben Sie einen Namen für den Raum ein",
"This room is private, and can only be joined by invitation.": "Dieser Raum ist privat und kann nur auf Einladung betreten werden.",
"Create a private room": "Erstelle einen privaten Raum",
"Topic (optional)": "Thema (optional)",
"Make this room public": "Machen Sie diesen Raum öffentlich",
"Hide advanced": "Fortgeschrittenes ausblenden",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Hindere Benutzer auf anderen Matrix-Homeservern daran, diesem Raum beizutreten (Diese Einstellung kann später nicht geändert werden!)",
"Session name": "Name der Sitzung",
"This will allow you to return to your account after signing out, and sign in on other sessions.": "So können Sie nach der Abmeldung zu Ihrem Konto zurückkehren und sich bei anderen Sitzungen anmelden.",
"Use bots, bridges, widgets and sticker packs": "Benutze Bots, Bridges, Widgets und Sticker-Packs",
"You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "Sie blockieren derzeit nicht verifizierte Sitzungen; um Nachrichten an diese Sitzungen zu senden, müssen Sie sie verifizieren.",
"Room contains unknown sessions": "Raum enthält unbekannte Sitzungen",
"\"%(RoomName)s\" contains sessions that you haven't seen before.": "\"%(RoomName)s\" enthält Sitzungen, die Sie noch nie zuvor gesehen haben.",
"Unknown sessions": "Unbekannte Sitzungen",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Greifen Sie durch Eingabe Ihrer Passphrase auf Ihren sicheren Nachrichtenverlauf und Ihre Quersignatur-Identität zu, um andere Sitzungen zu überprüfen.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Greifen Sie durch Eingabe Ihres Wiederherstellungsschlüssels auf Ihren sicheren Nachrichtenverlauf und Ihre Quersignatur-Identität zur Überprüfung anderer Sitzungen zu.",
"Message not sent due to unknown sessions being present": "Nachricht wird nicht gesendet, da unbekannte Sitzungen vorhanden sind",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Sitzungen anzeigen</showSessionsText>, <sendAnywayText>trotzdem senden</sendAnywayText> oder <cancelText>abbrechen</cancelText>.",
"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.": "Wenn Sie Ihr Passwort ändern, werden alle End-to-End-Verschlüsselungsschlüssel für alle Ihre Sitzungen zurückgesetzt, sodass der verschlüsselte Chat-Verlauf nicht mehr lesbar ist. Richten Sie ein Schlüssel-Backup ein oder exportieren Sie Ihre Raumschlüssel aus einer anderen Sitzung, bevor Sie Ihr Passwort zurücksetzen.",
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Sie wurden von allen Sitzungen abgemeldet und erhalten keine Push-Benachrichtigungen mehr. Um die Benachrichtigungen wieder zu aktivieren, melden Sie sich auf jedem Gerät erneut an.",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Aktualisieren Sie diese Sitzung, damit sie andere Sitzungen verifizieren kann, indem sie ihnen Zugang zu verschlüsselten Nachrichten gewährt und sie für andere Benutzer als vertrauenswürdig markiert.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Richten Sie für diese Sitzung eine Verschlüsselung ein, damit sie andere Sitzungen verifizieren kann, indem sie ihnen Zugang zu verschlüsselten Nachrichten gewährt und sie für andere Benutzer als vertrauenswürdig markiert."
}

View file

@ -5,21 +5,22 @@
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
"Add Phone Number": "Add Phone Number",
"The platform you're on": "The platform you're on",
"The version of Riot.im": "The version of Riot.im",
"The version of Riot": "The version of Riot",
"Whether or not you're logged in (we don't record your username)": "Whether or not you're logged in (we don't record your username)",
"Your language of choice": "Your language of choice",
"Which officially provided instance you are using, if any": "Which officially provided instance you are using, if any",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)",
"Your homeserver's URL": "Your homeserver's URL",
"Your identity server's URL": "Your identity server's URL",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Whether you're using Riot on a device where touch is the primary input mechanism",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)",
"Whether you're using Riot as an installed Progressive Web App": "Whether you're using Riot as an installed Progressive Web App",
"e.g. %(exampleValue)s": "e.g. %(exampleValue)s",
"Every page you use in the app": "Every page you use in the app",
"e.g. <CurrentPageURL>": "e.g. <CurrentPageURL>",
"Your User Agent": "Your User Agent",
"Your user agent": "Your user agent",
"Your device resolution": "Your device resolution",
"Analytics": "Analytics",
"The information being sent to us to help make Riot.im better includes:": "The information being sent to us to help make Riot.im better includes:",
"The information being sent to us to help make Riot better includes:": "The information being sent to us to help make Riot better includes:",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.",
"Error": "Error",
"Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.",
@ -60,6 +61,12 @@
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
"The server does not support the room version specified.": "The server does not support the room version specified.",
"Failure to create room": "Failure to create room",
"If you cancel now, you won't complete verifying the other user.": "If you cancel now, you won't complete verifying the other user.",
"If you cancel now, you won't complete verifying your other session.": "If you cancel now, you won't complete verifying your other session.",
"If you cancel now, you won't complete your secret storage operation.": "If you cancel now, you won't complete your secret storage operation.",
"Cancel entering passphrase?": "Cancel entering passphrase?",
"Enter passphrase": "Enter passphrase",
"Cancel": "Cancel",
"Setting up keys": "Setting up keys",
"Send anyway": "Send anyway",
"Send": "Send",
@ -391,6 +398,7 @@
"Show avatars in user and room mentions": "Show avatars in user and room mentions",
"Enable big emoji in chat": "Enable big emoji in chat",
"Send typing notifications": "Send typing notifications",
"Show typing notifications": "Show typing notifications",
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
"Mirror local video feed": "Mirror local video feed",
"Enable Community Filter Panel": "Enable Community Filter Panel",
@ -445,12 +453,17 @@
"You've successfully verified this user.": "You've successfully verified this user.",
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.",
"Got It": "Got It",
"Verify this session by completing one of the following:": "Verify this session by completing one of the following:",
"Scan this unique code": "Scan this unique code",
"or": "or",
"Compare unique emoji": "Compare unique emoji",
"Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device",
"Start": "Start",
"Confirm the emoji below are displayed on both devices, in the same order:": "Confirm the emoji below are displayed on both devices, in the same order:",
"Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.",
"Verify this device by confirming the following number appears on its screen.": "Verify this device by confirming the following number appears on its screen.",
"Verify this user by confirming the following number appears on their screen.": "Verify this user by confirming the following number appears on their screen.",
"Unable to find a supported verification method.": "Unable to find a supported verification method.",
"Cancel": "Cancel",
"Waiting for %(displayName)s to verify…": "Waiting for %(displayName)s to verify…",
"They match": "They match",
"They don't match": "They don't match",
@ -553,6 +566,7 @@
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
"Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.",
"Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage",
"Reset cross-signing and secret storage": "Reset cross-signing and secret storage",
"Cross-signing public keys:": "Cross-signing public keys:",
"in memory": "in memory",
"not found": "not found",
@ -1161,6 +1175,11 @@
"Yours, or the other users session": "Yours, or the other users session",
"Members": "Members",
"Files": "Files",
"Not Trusted": "Not Trusted",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) signed in to a new session without verifying it:",
"Ask this user to verify their session, or manually verify it below.": "Ask this user to verify their session, or manually verify it below.",
"Done": "Done",
"Manually Verify": "Manually Verify",
"Trusted": "Trusted",
"Not trusted": "Not trusted",
"%(count)s verified sessions|other": "%(count)s verified sessions",
@ -1179,10 +1198,12 @@
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
"Security": "Security",
"Verify by emoji": "Verify by emoji",
"Verify by comparing unique emoji.": "Verify by comparing unique emoji.",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.",
"Verify by scanning": "Verify by scanning",
"Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:",
"Verify by emoji": "Verify by emoji",
"If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.",
"Verify by comparing unique emoji.": "Verify by comparing unique emoji.",
"You've successfully verified %(displayName)s!": "You've successfully verified %(displayName)s!",
"Got it": "Got it",
"Verification timed out. Start verification again from their profile.": "Verification timed out. Start verification again from their profile.",
@ -1220,8 +1241,12 @@
"%(name)s cancelled verifying": "%(name)s cancelled verifying",
"You accepted": "You accepted",
"%(name)s accepted": "%(name)s accepted",
"You declined": "You declined",
"You cancelled": "You cancelled",
"%(name)s declined": "%(name)s declined",
"%(name)s cancelled": "%(name)s cancelled",
"accepting …": "accepting …",
"declining …": "declining …",
"%(name)s wants to verify": "%(name)s wants to verify",
"You sent a verification request": "You sent a verification request",
"Error decrypting video": "Error decrypting video",
@ -1430,6 +1455,9 @@
"Changelog": "Changelog",
"You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)",
"Removing…": "Removing…",
"Destroy cross-signing keys?": "Destroy cross-signing keys?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.",
"Clear cross-signing keys": "Clear cross-signing keys",
"Confirm Removal": "Confirm Removal",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.",
"Clear all data in this session?": "Clear all data in this session?",
@ -1496,6 +1524,7 @@
"Explore Room State": "Explore Room State",
"Explore Account Data": "Explore Account Data",
"View Servers in Room": "View Servers in Room",
"Verification Requests": "Verification Requests",
"Toolbox": "Toolbox",
"Developer Tools": "Developer Tools",
"An error has occurred.": "An error has occurred.",
@ -1633,6 +1662,7 @@
"Upload %(count)s other files|one": "Upload %(count)s other file",
"Cancel All": "Cancel All",
"Upload Error": "Upload Error",
"Verification Request": "Verification Request",
"A widget would like to verify your identity": "A widget would like to verify your identity",
"A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.",
"Remember my selection for this widget": "Remember my selection for this widget",
@ -1644,8 +1674,8 @@
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.",
"If you've forgotten your passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.": "If you've forgotten your passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.",
"Enter secret storage recovery key": "Enter secret storage recovery key",
"This looks like a valid recovery key!": "This looks like a valid recovery key!",
"Unable to access secret storage. Please verify that you entered the correct recovery key.": "Unable to access secret storage. Please verify that you entered the correct recovery key.",
"This looks like a valid recovery key!": "This looks like a valid recovery key!",
"Not a valid recovery key": "Not a valid recovery key",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.",
"If you've forgotten your recovery key you can <button>set up new recovery options</button>.": "If you've forgotten your recovery key you can <button>set up new recovery options</button>.",
@ -1844,7 +1874,6 @@
"Review terms and conditions": "Review terms and conditions",
"Old cryptography data detected": "Old cryptography data detected",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
"Verification Request": "Verification Request",
"Logout": "Logout",
"%(creator)s created and configured the room.": "%(creator)s created and configured the room.",
"Your Communities": "Your Communities",
@ -1919,11 +1948,9 @@
"Could not load user profile": "Could not load user profile",
"Complete security": "Complete security",
"Verify this session to grant it access to encrypted messages.": "Verify this session to grant it access to encrypted messages.",
"Start": "Start",
"Session verified": "Session verified",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
"Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
"Done": "Done",
"Without completing security on this session, it wont have access to encrypted messages.": "Without completing security on this session, it wont have access to encrypted messages.",
"Go Back": "Go Back",
"Failed to send email": "Failed to send email",
@ -2020,7 +2047,6 @@
"Export room keys": "Export room keys",
"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.": "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.",
"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.": "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.",
"Enter passphrase": "Enter passphrase",
"Confirm passphrase": "Confirm passphrase",
"Export": "Export",
"Import room keys": "Import room keys",
@ -2094,8 +2120,8 @@
"Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot is securely caching encrypted messages locally for them to appear in search results:",
"Space used:": "Space used:",
"Indexed messages:": "Indexed messages:",
"Number of rooms:": "Number of rooms:",
"of ": "of ",
"Indexed rooms:": "Indexed rooms:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s out of %(totalRooms)s",
"Message downloading sleep time(ms)": "Message downloading sleep time(ms)",
"Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",

View file

@ -1182,13 +1182,13 @@
"Your Riot is misconfigured": "Riot tiene un error de configuración",
"Whether or not you're logged in (we don't record your username)": "Hayas o no iniciado sesión (no guardamos tu nombre de usuario)",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Uses o no los 'breadcrumbs' (iconos sobre la lista de salas)",
"A conference call could not be started because the integrations server is not available": "No se puede iniciar la llamada porque no hay servidor de integraciones disponible",
"A conference call could not be started because the integrations server is not available": "No se pudo iniciar la conferencia porque el servidor de integraciones no está disponible",
"Replying With Files": "Respondiendo con archivos",
"At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "En este momento no es posible responder con un archivo. ¿Te gustaría subir el archivo sin responder?",
"The file '%(fileName)s' failed to upload.": "Falló en subir el archivo '%(fileName)s'.",
"The server does not support the room version specified.": "El servidor no soporta la versión de sala especificada.",
"Name or Matrix ID": "Nombre o identificador (ID) Matrix",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Pone ¯\\_(ツ)_/¯ al principio de un mensaje de texto",
"Name or Matrix ID": "Nombre o Matrix ID",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Añade ¯\\_(ツ)_/¯ al principio de un mensaje de texto plano",
"<b>Warning</b>: Upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> 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.": "<b>Aviso</b>: Actualizar una sala <i>no migrará automáticamente a sus miembros a la nueva versión de la sala.</i> Incluiremos un enlace a la nueva sala en la versión antigüa de la misma - los miembros tendrán que seguir ese enlace para unirse a la nueva sala.",
"Changes your display nickname in the current room only": "Cambia tu apodo sólo en la sala actual",
"Changes your avatar in this current room only": "Cambia tu avatar sólo en la sala actual",
@ -1203,7 +1203,7 @@
"%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s ha revocado la invitación para que %(targetDisplayName)s se una a la sala.",
"Cannot reach homeserver": "No se puede conectar con el servidor",
"Ensure you have a stable internet connection, or get in touch with the server admin": "Asegúrate de tener conexión a internet, o contacta con el administrador del servidor",
"Ask your Riot admin to check <a>your config</a> for incorrect or duplicate entries.": "Solicita al administrador de Riot que compruebe <a>tu configuración</a> por si hubiera errores o entradas duplicadas.",
"Ask your Riot admin to check <a>your config</a> for incorrect or duplicate entries.": "Solicita al administrador de Riot que compruebe si hay entradas duplicadas o erróneas en <a>tu configuración</a>.",
"Cannot reach identity server": "No se puede conectar con el servidor de identidad",
"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.": "Te puedes registrar, pero algunas funcionalidades no estarán disponibles hasta que se pueda conectar con el servidor de identidad. Si continúas viendo este aviso, comprueba tu configuración o contacta con el administrador del servidor.",
"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.": "Puedes cambiar tu contraseña, pero algunas funcionalidades no estarán disponibles hasta que el servidor de identidad esté disponible. Si continúas viendo este aviso, comprueba tu configuración o contacta con el administrador del servidor.",
@ -1214,10 +1214,10 @@
"User %(userId)s is already in the room": "El usuario %(userId)s ya está en la sala",
"The user must be unbanned before they can be invited.": "El usuario debe ser desbloqueado antes de poder ser invitado.",
"The user's homeserver does not support the version of the room.": "El servidor del usuario no soporta la versión de la sala.",
"Show read receipts sent by other users": "Mostrar las confirmaciones de lectura de otros usuarios",
"Show read receipts sent by other users": "Mostrar las confirmaciones de lectura enviadas por otros usuarios",
"Order rooms in the room list by most important first instead of most recent": "Ordenar la lista de salas por importancia en vez de por reciente",
"Show recently visited rooms above the room list": "Mostrar salas visitadas recientemente sobre la lista de salas",
"Show hidden events in timeline": "Mostrar eventos ocultos en la línea del tiempo",
"Show hidden events in timeline": "Mostrar eventos ocultos en la línea de tiempo",
"Low bandwidth mode": "Modo de ancho de banda bajo",
"Got It": "Entendido",
"Scissors": "Tijeras",
@ -1276,7 +1276,7 @@
"Jump to first unread room.": "Saltar a la primera sala sin leer.",
"You have %(count)s unread notifications in a prior version of this room.|other": "Tiene %(count)s notificaciones sin leer en una versión anterior de esta sala.",
"You have %(count)s unread notifications in a prior version of this room.|one": "Tiene %(count)s notificaciones sin leer en una versión anterior de esta sala.",
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Hay sesiones desconocidas en esta sala: si continuas sin verificarlas, será posible que alguien escuche secretamente tu llamada.",
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Hay sesiones desconocidas en esta sala: si continúas sin verificarlas, será posible que alguien escuche secretamente tu llamada.",
"Setting up keys": "Configurando claves",
"Verify this session": "Verificar esta sesión",
"Encryption upgrade available": "Mejora de encriptación disponible",
@ -1287,21 +1287,21 @@
"Session already verified!": "¡La sesión ya ha sido verificada!",
"WARNING: Session already verified, but keys do NOT MATCH!": "ATENCIÓN: ¡La sesión ya ha sido verificada, pero las claves NO CONCUERDAN!",
"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!": "¡ATENCIÓN: LA VERIFICACIÓN DE LA CLAVE HA FALLADO! La clave de firma para %(userId)s y sesión %(deviceId)s es \"%(fprint)s\", la cual no coincide con la clave proporcionada \"%(fingerprint)s\". ¡Esto podría significar que tus comunicaciones están siendo interceptadas!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "La clave de firma que proporcionaste coincide con la clave de firma que reciviste de la sesión %(deviceId)s de %(userId)s. Sesión marcada como verificada.",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "La clave de firma que proporcionaste coincide con la clave de firma que recibiste de la sesión %(deviceId)s de %(userId)s. Sesión marcada como verificada.",
"%(senderName)s added %(addedAddresses)s and %(count)s other addresses to this room|other": "%(senderName)s añadió %(addedAddresses)s y %(count)s otras direcciones a esta sala",
"%(senderName)s removed %(removedAddresses)s and %(count)s other addresses from this room|other": "%(senderName)s eliminó %(removedAddresses)s y %(count)s otras direcciones de esta sala",
"%(senderName)s removed %(countRemoved)s and added %(countAdded)s addresses to this room": "%(senderName)s eliminó %(countRemoved)s y añadió %(countAdded)s direcciones a esta sala",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s eliminó la regla que banea usuarios que corresponden con %(glob)s",
"%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s eliminó la regla que banea salas que corresponden con %(glob)s",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s eliminó la regla que banea servidores que corresponden con %(glob)s",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s eliminó la regla que bloquea a usuarios que coinciden con %(glob)s",
"%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s eliminó la regla que bloquea a salas que coinciden con %(glob)s",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s eliminó la regla que bloquea a servidores que coinciden con %(glob)s",
"%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s eliminó una regla correspondiente a %(glob)s",
"%(senderName)s updated an invalid ban rule": "%(senderName)s actualizó una regla de baneo inválida",
"%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s actualizó una regla que banea usuarios que corresponden a %(glob)s por %(reason)s",
"They match": "Corresponden",
"They don't match": "No corresponden",
"%(senderName)s updated an invalid ban rule": "%(senderName)s actualizó una regla de bloqueo inválida",
"%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s actualizó una regla que bloquea a usuarios que coinciden con %(glob)s por %(reason)s",
"They match": "Coinciden",
"They don't match": "No coinciden",
"To be secure, do this in person or use a trusted way to communicate.": "Para ser seguro, haz esto en persona o usando una forma de comunicación de confianza.",
"Lock": "Bloquear",
"Verify yourself & others to keep your chats safe": "Verifícate y verifica a otros para mantener tus chats seguros",
"Verify yourself & others to keep your chats safe": "Verifícate y verifica a otros para mantener tus conversaciones seguras",
"Other users may not trust it": "Puede que otros usuarios no confíen en ello",
"Upgrade": "Actualizar",
"Verify": "Verificar",
@ -1311,13 +1311,13 @@
"Channel: %(channelName)s": "Canal: %(channelName)s",
"Show less": "Mostrar menos",
"Show more": "Mostrar más",
"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.": "Cambiar la contraseña reseteará cualquier clave de encriptación end-to-end en todas las sesiones, haciendo el historial de chat encriptado ilegible, a no ser que primero exportes tus claves de sala y las reimportes después. En un futuro esto será mejorado.",
"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.": "Cambiar la contraseña reiniciará cualquier clave de encriptación end-to-end en todas las sesiones, haciendo el historial de conversaciones encriptado ilegible, a no ser que primero exportes tus claves de sala y después las reimportes. En un futuro esto será mejorado.",
"in memory": "en memoria",
"not found": "no encontrado",
"Identity Server (%(server)s)": "Servidor de identidad %(server)s",
"Identity Server (%(server)s)": "Servidor de Identidad %(server)s",
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Estás usando actualmente <server></server>para descubrir y ser descubierto por contactos existentes que conoces. Puedes cambiar tu servidor de identidad más abajo.",
"If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.": "Si no quieres usar <server /> para descubrir y ser descubierto por contactos existentes que conoces, introduce otro servidor de identidad más abajo.",
"Identity Server": "Servidor de identidad",
"Identity Server": "Servidor de Identidad",
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "No estás usando actualmente un servidor de identidad. Para descubrir y ser descubierto por contactos existentes que conoces, introduce uno más abajo.",
"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.": "Desconectarte de tu servidor de identidad significa que no podrás ser descubierto por otros usuarios y no podrás invitar a otros por email o teléfono.",
"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.": "Usar un servidor de identidad es opcional. Si eliges no usar un servidor de identidad, no podrás ser descubierto por otros usuarios y no podrás invitar a otros por email o teléfono.",
@ -1325,15 +1325,15 @@
"Enter a new identity server": "Introducir un servidor de identidad nuevo",
"Change": "Cambiar",
"Manage integrations": "Administrar integraciones",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Los administradores de integración reciben datos de configuración, y pueden modificar widgets, enviar invitaciones de sala, y establece niveles de poder en tu nombre.",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Los administradores de integración reciben datos de configuración, y pueden modificar widgets, enviar invitaciones de sala, y establecer niveles de poder en tu nombre.",
"Something went wrong trying to invite the users.": "Algo salió mal al intentar invitar a los usuarios.",
"We couldn't invite those users. Please check the users you want to invite and try again.": "No se pudo invitar a esos usuarios. Por favor, revisa los usuarios que quieres invitar e inténtalo de nuevo.",
"Failed to find the following users": "No se encontró a los siguientes usuarios",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Puede que los siguientes usuarios no existan o sean inválidos, y no pueden ser invitados: %(csvNames)s",
"Recent Conversations": "Conversaciones Recientes",
"Recent Conversations": "Conversaciones recientes",
"Suggestions": "Sugerencias",
"Recently Direct Messaged": "Enviado Mensaje Directo recientemente",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Si no encuentras a alguien, pídele su usuario, comparte tu usuario (%(userId)s) o un <a>enlace de perfil</a>.",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Si no encuentras a alguien, pídele su usuario, comparte tu usuario (%(userId)s) o <a>enlace de perfil</a>.",
"Go": "Ir",
"If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.": "Si no encuentras a alguien, pídele su usuario (p.ej. @user:server.com) o <a>comparte esta sala</a>.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "Has añadido una nueva sesión '%(displayName)s', la cual está pidiendo claves de encriptación.",
@ -1373,7 +1373,7 @@
"Missing session data": "Faltan datos de sesión",
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Algunos datos de sesión, incluyendo claves de mensaje encriptadas, no se encuentran. Desconéctate y vuelve a conectarte para solucionarlo, reestableciendo las claves desde el backup.",
"Your browser likely removed this data when running low on disk space.": "Tu navegador probablemente borró estos datos cuando tenía poco espacio de disco.",
"Find others by phone or email": "Encontar a otros por teléfono o email",
"Find others by phone or email": "Encontrar a otros por teléfono o email",
"Be found by phone or email": "Ser encontrado por teléfono o email",
"Use bots, bridges, widgets and sticker packs": "Usar robots, puentes, widgets, o packs de pegatinas",
"Terms of Service": "Términos de servicio",
@ -1398,17 +1398,17 @@
"A widget would like to verify your identity": "Un widget quisiera verificar tu identidad",
"Remember my selection for this widget": "Recordar mi selección para este widget",
"Deny": "Rechazar",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Tu contraseña ha sido cambiada satisfactoriamente. No recibirás notificaciones de push en otras sesiones hasta que te conectes de nuevo a ellas",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Tu contraseña ha sido cambiada satisfactoriamente. No recibirás notificaciones push en otras sesiones hasta que te conectes de nuevo a ellas",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Aceptar los Términos de Servicio del servidor de identidad %(serverName)s para poder ser descubierto por dirección de email o número de teléfono.",
"Discovery": "Descubrimiento",
"Deactivate account": "Desactivar cuenta",
"Clear cache and reload": "Limpiar caché y recargar",
"Ignored/Blocked": "Ignorado/Bloqueado",
"Error adding ignored user/server": "Error añadiendo usuario/servidor ignorado",
"Error adding ignored user/server": "Error al añadir usuario/servidor ignorado",
"Error subscribing to list": "Error al suscribirse a la lista",
"Please verify the room ID or alias and try again.": "Por favor, verifica el ID de la sala o el alias e inténtalo de nuevo.",
"Error removing ignored user/server": "Error al remover usuario/servidor ignorado",
"Error unsubscribing from list": "Error al desuscribirse de la lista",
"Error removing ignored user/server": "Error al eliminar usuario/servidor ignorado",
"Error unsubscribing from list": "Error al cancelar la suscripción a la lista",
"None": "Ninguno",
"Server rules": "Reglas del servidor",
"User rules": "Reglas de usuario",
@ -1419,7 +1419,122 @@
"View rules": "Ver reglas",
"You are currently subscribed to:": "Estás actualmente suscrito a:",
"⚠ These settings are meant for advanced users.": "⚠ Estas opciones son indicadas para usuarios avanzados.",
"Personal ban list": "Lista de bans personal",
"Personal ban list": "Lista de bloqueo personal",
"Server or user ID to ignore": "Servidor o ID de usuario a ignorar",
"eg: @bot:* or example.org": "p. ej.: @bot:* o ejemplo.org"
"eg: @bot:* or example.org": "p. ej.: @bot:* o ejemplo.org",
"The version of Riot": "La version de Riot",
"Your user agent": "Tu agente de usuario",
"The information being sent to us to help make Riot better includes:": "La información que se nos envía para ayudarnos a mejorar Riot incluye:",
"If you cancel now, you won't complete verifying the other user.": "Si cancelas ahora, no completarás la verificación del otro usuario.",
"If you cancel now, you won't complete verifying your other session.": "Si cancelas ahora, no completarás la verificación de tu otra sesión.",
"If you cancel now, you won't complete your secret storage operation.": "Si cancelas ahora, no completarás tu operación de almacén secreto.",
"Cancel entering passphrase?": "¿Cancelar la introducción de frase de contraseña?",
"%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s actualizó la regla bloqueando salas que coinciden con %(glob)s por %(reason)s",
"%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s actualizó la regla bloqueando servidores que coinciden con %(glob)s por %(reason)s",
"%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s actualizó una regla de bloqueo correspondiente a %(glob)s por %(reason)s",
"%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s creó una regla bloqueando a usuarios que coinciden con %(glob)s por %(reason)s",
"%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s creó una regla bloqueando a salas que coinciden con %(glob)s por %(reason)s",
"%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s creó una regla bloqueando a servidores que coinciden con %(glob)s por %(reason)s",
"%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s creó una regla de bloqueo correspondiente a %(glob)s por %(reason)s",
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s cambió una regla que estaba bloqueando a usuarios que coinciden con %(oldGlob)s a %(newGlob)s por %(reason)s",
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s cambió una regla que estaba bloqueando a salas que coinciden con %(oldGlob)s a %(newGlob)s por %(reason)s",
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s cambió una regla que estaba bloqueando a servidores que coinciden con %(oldGlob)s a %(newGlob)s por %(reason)s",
"%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s actualizó una regla de bloqueo que correspondía a %(oldGlob)s a %(newGlob)s por %(reason)s",
"The message you are trying to send is too large.": "El mensaje que estás intentando enviar es demasiado largo.",
"a few seconds ago": "hace unos segundos",
"about a minute ago": "hace un minuto",
"%(num)s minutes ago": "hace %(num)s minutos",
"about an hour ago": "hace una hora",
"%(num)s hours ago": "hace %(num)s horas",
"about a day ago": "hace un día",
"%(num)s days ago": "hace %(num)s días",
"a few seconds from now": "dentro de unos segundos",
"about a minute from now": "dentro de un minuto",
"%(num)s minutes from now": "dentro de %(num)s minutos",
"about an hour from now": "dentro de una hora",
"%(num)s hours from now": "dentro de %(num)s horas",
"about a day from now": "dentro de un día",
"%(num)s days from now": "dentro de %(num)s días",
"Show typing notifications": "Mostrar notificaciones de escritura",
"Never send encrypted messages to unverified sessions from this session": "No enviar nunca mensajes cifrados a sesiones sin verificar desde esta sesión",
"Never send encrypted messages to unverified sessions in this room from this session": "No enviar nunca mensajes cifrados a sesiones sin verificar en esta sala desde esta sesión",
"Enable message search in encrypted rooms": "Habilitar la búsqueda de mensajes en salas cifradas",
"Keep secret storage passphrase in memory for this session": "Mantener la frase de contraseña en memoria para esta sesión",
"How fast should messages be downloaded.": "Con qué rapidez deben ser descargados los mensajes.",
"Verify this session by completing one of the following:": "Verifica esta sesión completando uno de los siguientes:",
"Scan this unique code": "Escanea este código único",
"or": "o",
"Compare unique emoji": "Comparar emoji único",
"Compare a unique set of emoji if you don't have a camera on either device": "Comparar un conjunto de emojis si no tienes cámara en ninguno de los dispositivos",
"Start": "Comenzar",
"Confirm the emoji below are displayed on both devices, in the same order:": "Confirma que los emojis a continuación son mostrados en ambos dispositivos, en el mismo orden:",
"Verify this device by confirming the following number appears on its screen.": "Verifica este dispositivo confirmando que el siguiente número aparece en su pantalla.",
"Waiting for %(displayName)s to verify…": "Esperando la verificación de %(displayName)s …",
"Review": "Revisar",
"in secret storage": "en almacén secreto",
"Secret storage public key:": "Clave pública del almacén secreto:",
"in account data": "en datos de cuenta",
"Unable to load session list": "No se pudo cargar la lista de sesiones",
"Delete %(count)s sessions|other": "Borrar %(count)s sesiones",
"Delete %(count)s sessions|one": "Borrar %(count)s sesión",
"rooms.": "salas.",
"Manage": "Administrar",
"Enable": "Activar",
"This session is backing up your keys. ": "Esta sesión está haciendo una copia de seguridad de tus claves. ",
"not stored": "no almacenado",
"Message search": "Busqueda de mensajes",
"Sessions": "Sesiones",
"Upgrade this room to the recommended room version": "Actualizar esta sala a la versión de sala recomendada",
"this room": "esta sala",
"View older messages in %(roomName)s.": "Ver mensajes más antiguos en %(roomName)s.",
"Sounds": "Sonidos",
"Notification sound": "Sonido de notificación",
"Set a new custom sound": "Usar un nuevo sonido personalizado",
"Browse": "Navegar",
"Change room avatar": "Cambiar avatar de sala",
"Change room name": "Cambiar nombre de sala",
"Change main address for the room": "Cambiar dirección principal para la sala",
"Change history visibility": "Cambiar visibilidad del historial",
"Change permissions": "Cambiar permisos",
"Change topic": "Cambiar tema",
"Upgrade the room": "Actualizar la sala",
"Enable room encryption": "Activar el cifrado de sala",
"Modify widgets": "Modificar widgets",
"Error changing power level requirement": "Error al cambiar el requerimiento de nivel de poder",
"An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Ocurrió un error cambiando los requerimientos de nivel de poder de la sala. Asegúrate de tener los permisos suficientes e inténtalo de nuevo.",
"Error changing power level": "Error al cambiar nivel de poder",
"An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Ocurrió un error cambiando los requerimientos de nivel de poder del usuario. Asegúrate de tener los permisos suficientes e inténtalo de nuevo.",
"Default role": "Rol por defecto",
"Send messages": "Enviar mensajes",
"Invite users": "Invitar usuarios",
"Change settings": "Cambiar la configuración",
"Kick users": "Echar a usuarios",
"Ban users": "Bloquear a usuarios",
"Remove messages": "Eliminar mensajes",
"Notify everyone": "Notificar a todos",
"Send %(eventType)s events": "Enviar eventos %(eventType)s",
"Select the roles required to change various parts of the room": "Selecciona los roles requeridos para cambiar varias partes de la sala",
"Enable encryption?": "¿Habilitar cifrado?",
"Your email address hasn't been verified yet": "Tu dirección de email no ha sido verificada",
"Verify the link in your inbox": "Verifica el enlace en tu bandeja de entrada",
"Complete": "Completar",
"Share": "Compartir",
"Remove %(email)s?": "¿Eliminar %(email)s?",
"Backup is not signed by any of your sessions": "La copia de seguridad no está firmada por ninguna de tus sesiones",
"This backup is trusted because it has been restored on this session": "Esta copia de seguridad es de confianza porque ha sido restaurada en esta sesión",
"Backup key stored: ": "Clave de seguridad almacenada: ",
"Your keys are <b>not being backed up from this session</b>.": "<b>No se está haciendo una copia de seguridad de tus claves en esta sesión</b>.",
"Clear notifications": "Limpiar notificaciones",
"Enable desktop notifications for this session": "Habilitar notificaciones de escritorio para esta sesión",
"Enable audible notifications for this session": "Habilitar notificaciones sonoras para esta sesión",
"Checking server": "Comprobando servidor",
"Change identity server": "Cambiar servidor de identidad",
"Disconnect from the identity server <current /> and connect to <new /> instead?": "¿Desconectarse del servidor de identidad <current /> y conectarse a <new/>?",
"Terms of service not accepted or the identity server is invalid.": "Términos de servicio no aceptados o el servidor de identidad es inválido.",
"The identity server you have chosen does not have any terms of service.": "El servidor de identidad que has elegido no tiene ningún término de servicio.",
"Disconnect identity server": "Desconectar servidor de identidad",
"Disconnect from the identity server <idserver />?": "¿Desconectarse del servidor de identidad <idserver />?",
"Disconnect": "Desconectarse",
"You should:": "Deberías:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s de %(totalRooms)s"
}

View file

@ -2075,5 +2075,38 @@
"Copy": "Kopiatu",
"You can now verify your other devices, and other users to keep your chats safe.": "Orain zure beste gailuak eta beste erabiltzaileak egiaztatu ditzakezu txatak seguru mantentzeko",
"Make a copy of your recovery key": "Egin zure berreskuratze gakoaren kopia",
"You're done!": "Bukatu duzu!"
"You're done!": "Bukatu duzu!",
"If you cancel now, you won't complete verifying the other user.": "Orain ezeztatzen baduzu, ez duzu beste erabiltzailearen egiaztaketa burutuko.",
"If you cancel now, you won't complete verifying your other session.": "Orain ezeztatzen baduzu, ez duzu beste zure beste saioaren egiaztaketa burutuko.",
"If you cancel now, you won't complete your secret storage operation.": "Orain ezeztatzen baduzu, ez duzu zure biltegi sekretuko eragiketa burutuko.",
"Cancel entering passphrase?": "Ezeztatu pasa-esaldiaren sarrera?",
"Securely cache encrypted messages locally for them to appear in search results.": "Gorde zifratutako mezuak cachean modu seguruan bilaketen emaitzetan agertu daitezen.",
"You have verified this user. This user has verified all of their sessions.": "Erabiltzaile hau egiaztatu duzu. Erabiltzaile honek bere saio guztiak egiaztatu ditu.",
"Mod": "Moderatzailea",
"Your key share request has been sent - please check your other sessions for key share requests.": "Zure gako partekatze eskaria bidali da, egiaztatu zure beste saioak gako partekatze eskaera jaso duten.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Automatikoki bidaltzen dira gako partekatze eskaerak zure beste saioetara. Beste saioetan gako partekatze eskaera ukatu edo baztertu baduzu, sakatu hemen saio honentzat gakoak berriro eskatzeko.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Zure beste saioek ez badute mezu honen gakoa ezin izango duzu deszifratu.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Eskatu berriro zifratze gakoak</requestLink> zure beste saioei.",
"No sessions with registered encryption keys": "Ez dago zifratze gako hori duen saiorik",
"Waiting for %(displayName)s to accept…": "%(displayName)s(e)k onartu bitartean zain…",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Zuon mezuak babestuta daude eta soilik zuk eta hartzaileak dituzue hauek irekitzeko giltza.",
"One of the following may be compromised:": "Hauetakoren bat konprometituta egon daiteke:",
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Egiztatu gailu hau fidagarri gisa markatzeko. Gailu hau fidagarritzat jotzeak lasaitasuna ematen du muturretik-muturrera zifratutako mezuak erabiltzean.",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Gailu hau egiaztatzean fidagarri gisa markatuko da, eta egiaztatu zaituzten erabiltzaileek fidagarri gisa ikusiko dute.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "'%(displayName)s' saio berria gehitu duzu, eta zifratze gakoak eskatzen ari da.",
"Your unverified session '%(displayName)s' is requesting encryption keys.": "Zure egiaztatu gabeko '%(displayName)s' saioa zifratze gakoak eskatzen ari da.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Egiaztaketa prozesua saio bakoitzeko egitea aholkatzen dizugu, benetan jabearenak direla baieztatzeko, baina egiaztaketa egin gabe mezua bidali dezakezu ere.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Atzitu zure mezu seguruen historiala eta zeharkako sinatzerako identitatea beste saioak egiaztatzeko zure pasa-esaldia sartuz.",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Erakutsi saioak</showSessionsText>, <sendAnywayText>bidali edonola ere</sendAnywayText> edo <cancelText>ezeztatu</cancelText>.",
"The information being sent to us to help make Riot better includes:": "Riot hobetzeko bidaltzen zaigun informazioa honakoa da, besteren artean:",
"Verify this session by completing one of the following:": "Egiaztatu saio hau hauetako bat osatuz:",
"or": "ala",
"The version of Riot": "Riot bertsioa",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Sarrera mekanismo nagusia ukimena den gailu bat erabiltzen duzun",
"Whether you're using Riot as an installed Progressive Web App": "Riot instalatutako aplikazio progresibo gisa erabiltzen duzun",
"Your user agent": "Zure erabiltzaile-agentea",
"Show typing notifications": "Erakutsi idazketa jakinarazpenak",
"Scan this unique code": "Eskaneatu kode bakan hau",
"Compare unique emoji": "Konparatu emoji bakana",
"Compare a unique set of emoji if you don't have a camera on either device": "Konparatu emoji sorta bakana gailuek kamerarik ez badute"
}

View file

@ -97,7 +97,7 @@
"Emoji": "Emoji",
"Enable Notifications": "Ota ilmoitukset käyttöön",
"End-to-end encryption information": "Osapuolten välisen salauksen tiedot",
"Enter passphrase": "Syötä salasana",
"Enter passphrase": "Syötä salalause",
"Error decrypting attachment": "Virhe purettaessa liitteen salausta",
"Event information": "Tapahtumatiedot",
"Export": "Vie",
@ -1880,5 +1880,45 @@
"Recent Conversations": "Viimeaikaiset keskustelut",
"Direct Messages": "Yksityisviestit",
"Go": "Mene",
"Lock": "Lukko"
"Lock": "Lukko",
"The version of Riot": "Riotin versio",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Käytätkö Riotia laitteella, jossa kosketus on ensisijainen syöttömekanismi",
"Whether you're using Riot as an installed Progressive Web App": "Käytätkö Riotia asennettuna PWA:na (Progressive Web App)",
"The information being sent to us to help make Riot better includes:": "Tietoihin, joita lähetetään Riotin kehittäjille sovelluksen kehittämiseksi sisältyy:",
"Cancel entering passphrase?": "Peruuta salalauseen syöttäminen?",
"Encryption upgrade available": "Salauksen päivitys saatavilla",
"%(senderName)s added %(addedAddresses)s and %(count)s other addresses to this room|other": "%(senderName)s lisäsi osoitteet %(addedAddresses)s ja %(count)s muuta osoitetta tähän huoneeseen",
"%(senderName)s removed %(removedAddresses)s and %(count)s other addresses from this room|other": "%(senderName)s poisti osoitteet %(removedAddresses)s ja %(count)s muuta osoitetta tästä huoneesta",
"%(senderName)s removed %(countRemoved)s and added %(countAdded)s addresses to this room": "%(senderName)s poisti %(countRemoved)s ja lisäsi %(countAdded)s osoitetta tähän huoneeseen",
"a few seconds ago": "muutama sekunti sitten",
"about a minute ago": "noin minuutti sitten",
"%(num)s minutes ago": "%(num)s minuuttia sitten",
"about an hour ago": "noin tunti sitten",
"%(num)s hours ago": "%(num)s tuntia sitten",
"about a day ago": "noin päivä sitten",
"%(num)s days ago": "%(num)s päivää sitten",
"Show info about bridges in room settings": "Näytä tietoa silloista huoneen asetuksissa",
"Show typing notifications": "Näytä kirjoitusilmoitukset",
"or": "tai",
"Start": "Aloita",
"Confirm the emoji below are displayed on both devices, in the same order:": "Varmista, että alla olevat emojit näkyvät molemmilla laitteilla, samassa järjestyksessä:",
"To be secure, do this in person or use a trusted way to communicate.": "Turvallisuuden varmistamiseksi tee tämä kasvokkain tai käytä luotettua viestintätapaa.",
"Later": "Myöhemmin",
"Show less": "Näytä vähemmän",
"in memory": "muistissa",
"This room isnt bridging messages to any platforms. <a>Learn more.</a>": "Tämä huone ei siltaa viestejä millekään alustalle. <a>Lue lisää.</a>",
"Bridges": "Sillat",
"Send a reply…": "Lähetä vastaus…",
"Send a message…": "Lähetä viesti…",
"Unknown Command": "Tuntematon komento",
"Unrecognised command: %(commandText)s": "Tunnistamaton komento: %(commandText)s",
"Send as message": "Lähetä viestinä",
"Waiting for %(displayName)s to accept…": "Odotetaan, että %(displayName)s hyväksyy…",
"Done": "Valmis",
"Got it": "Asia selvä",
"Failed to find the following users": "Seuraavia käyttäjiä ei löytynyt",
"Backup restored": "Varmuuskopio palautettu",
"Go Back": "Takaisin",
"Copy": "Kopioi",
"Upgrade your encryption": "Päivitä salauksesi"
}

View file

@ -148,7 +148,7 @@
"Passwords can't be empty": "Le mot de passe ne peut pas être vide",
"Permissions": "Permissions",
"Phone": "Numéro de téléphone",
"Operation failed": "L'opération a échoué",
"Operation failed": "Lopération a échoué",
"Default": "Par défaut",
"Email address": "Adresse e-mail",
"Error decrypting attachment": "Erreur lors du déchiffrement de la pièce jointe",
@ -387,7 +387,7 @@
"Accept": "Accepter",
"Active call (%(roomName)s)": "Appel en cours (%(roomName)s)",
"Alias (optional)": "Alias (facultatif)",
"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.": "Impossible de se connecter au serveur d'accueil - veuillez vérifier votre connexion, assurez-vous que le <a>certificat SSL de votre serveur d'accueil</a> est un certificat de confiance, et qu'aucune extension du navigateur ne bloque les requêtes.",
"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.": "Impossible de se connecter au serveur daccueil - veuillez vérifier votre connexion, assurez-vous que le <a>certificat SSL de votre serveur daccueil</a> est un certificat de confiance, et quaucune extension du navigateur ne bloque les requêtes.",
"Close": "Fermer",
"Custom": "Personnaliser",
"Decline": "Refuser",
@ -493,7 +493,7 @@
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s a changé les messages épinglés du salon.",
"Jump to read receipt": "Aller à l'accusé de lecture",
"World readable": "Lisible publiquement",
"Guests can join": "Accessible aux invités",
"Guests can join": "Accessible aux visiteurs",
"Invalid community ID": "Identifiant de communauté non valide",
"'%(groupId)s' is not a valid community ID": "\"%(groupId)s\" n'est pas un identifiant de communauté valide",
"%(senderName)s sent an image": "%(senderName)s a envoyé une image",
@ -1149,11 +1149,11 @@
"Begin Verifying": "Commencer la vérification",
"Waiting for partner to accept...": "Nous attendons que le partenaire accepte…",
"Use two-way text verification": "Utiliser la vérification textuelle bidirectionnelle",
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Vérifier cet utilisateur pour que ce soit un utilisateur de confiance. Faire confiance aux utilisateurs vous apporte une tranquillité desprit quand vous utilisez des messages chiffrés de bout en bout.",
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Vérifier cet utilisateur pour le marquer comme fiable. Faire confiance aux utilisateurs vous permet dêtre serein quand vous utilisez des messages chiffrés de bout en bout.",
"Waiting for partner to confirm...": "Nous attendons que le partenaire confirme…",
"Incoming Verification Request": "Demande de vérification entrante",
"Go back": "Revenir en arrière",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "Pour éviter la duplication d'issues, veuillez <existingIssuesLink>voir les issues existants</existingIssuesLink>d'abord (et ajouter un +1) ou <newIssueLink>créer un nouvel issue</newIssueLink> si vous ne le trouvez pas.",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "Pour éviter la duplication de problèmes, veuillez <existingIssuesLink>voir les problèmes existants</existingIssuesLink> d'abord (et ajouter un +1) ou <newIssueLink>créer un nouveau problème</newIssueLink> si vous ne le trouvez pas.",
"Report bugs & give feedback": "Rapporter des anomalies & Donner son avis",
"Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "La sauvegarde n'a pas pu être déchiffrée avec cette clé : veuillez vérifier que vous avez saisi la bonne clé de récupération.",
"Update status": "Mettre à jour le statut",
@ -1178,7 +1178,7 @@
"Premium hosting for organisations <a>Learn more</a>": "Hébergement premium pour les organisations <a>En savoir plus</a>",
"Other": "Autre",
"Find other public servers or use a custom server": "Trouvez d'autres serveurs publics ou utilisez un serveur personnalisé",
"Guest": "Invité",
"Guest": "Visiteur",
"Sign in instead": "Se connecter",
"Set a new password": "Définir un nouveau mot de passe",
"Create account": "Créer un compte",
@ -1193,9 +1193,9 @@
"%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s a rendu le salon public à tous ceux qui en connaissent le lien.",
"%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s a rendu le salon disponible sur invitation seulement.",
"%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s a changé la règle dadhésion en %(rule)s",
"%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s a autorisé les invités à rejoindre le salon.",
"%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s a empêché les invités de rejoindre le salon.",
"%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a changé l'accès des invités en %(rule)s",
"%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s a autorisé les visiteurs à rejoindre le salon.",
"%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s a empêché les visiteurs de rejoindre le salon.",
"%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a changé l'accès des visiteurs en %(rule)s",
"Group & filter rooms by custom tags (refresh to apply changes)": "Grouper et filtrer les salons grâce à des étiquettes personnalisées (actualiser pour appliquer les changements)",
"Verify this user by confirming the following emoji appear on their screen.": "Vérifier cet utilisateur en confirmant que les émojis suivant apparaissent sur son écran.",
"Unable to find a supported verification method.": "Impossible de trouver une méthode de vérification prise en charge.",
@ -1754,7 +1754,7 @@
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>": "Vous avez ignoré cet utilisateur, donc ses messages sont cachés. <a>Les montrer quand même.</a>",
"Custom (%(level)s)": "Personnalisé (%(level)s)",
"Trusted": "Fiable",
"Not trusted": "Non vérifié",
"Not trusted": "Non fiable",
"Direct message": "Message direct",
"<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> dans %(roomName)s",
"Messages in this room are end-to-end encrypted.": "Les messages dans ce salon sont chiffrés de bout en bout.",
@ -1850,7 +1850,7 @@
"Enter secret storage recovery key": "Saisir la clé de récupération du coffre secret",
"Unable to access secret storage. Please verify that you entered the correct recovery key.": "Impossible daccéder au coffre secret. Vérifiez que vous avez saisi la bonne clé de récupération.",
"If you've forgotten your recovery key you can <button>set up new recovery options</button>.": "Si vous avez oublié votre clé de récupération vous pouvez <button>définir de nouvelles options de récupération</button>.",
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Attention</b> : Vous devriez uniquement configurer une sauvegarde de clés depuis un ordinateur de confiance.",
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Attention</b> : Vous ne devriez configurer la sauvegarde de clés que depuis un ordinateur de confiance.",
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "Si vous avez oublié votre clé de récupération, vous pouvez <button>définir de nouvelles options de récupération</button>",
"Set up with a recovery key": "Configurer avec une clé de récupération",
"As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Par mesure de sécurité, vous pouvez lutiliser pour récupérer laccès aux messages chiffrés si vous oubliez votre phrase de passe.",
@ -1918,7 +1918,7 @@
"Other users may not trust it": "Dautres utilisateurs pourraient ne pas lui faire confiance",
"Later": "Plus tard",
"Verify User": "Vérifier lutilisateur",
"For extra security, verify this user by checking a one-time code on both of your devices.": "Pour une meilleure sécurité, vérifiez cet utilisateur en comparant un code à usage unique sur vos deux appareils.",
"For extra security, verify this user by checking a one-time code on both of your devices.": "Pour une sécurité supplémentaire, vérifiez cet utilisateur en comparant un code à usage unique sur vos deux appareils.",
"Start Verification": "Commencer la vérification",
"Unknown Command": "Commande inconnue",
"Unrecognised command: %(commandText)s": "Commande non reconnue : %(commandText)s",
@ -2051,7 +2051,7 @@
"You have not verified this user. This user has verified all of their sessions.": "Vous navez pas vérifié cet utilisateur. Cet utilisateur a vérifié toutes ses sessions.",
"You have verified this user. This user has verified all of their sessions.": "Vous avez vérifié cet utilisateur. Cet utilisateur a vérifié toutes ses sessions.",
"Someone is using an unknown session": "Quelqu'un utilise une session inconnue",
"Some sessions for this user are not trusted": "Certaines sessions de cet utilisateur ne sont par fiables",
"Some sessions for this user are not trusted": "Certaines sessions de cet utilisateur ne sont pas fiables",
"All sessions for this user are trusted": "Toutes les sessions de cet utilisateur sont fiables",
"Some sessions in this encrypted room are not trusted": "Certaines sessions dans ce salon chiffré ne sont pas fiables",
"All sessions in this encrypted room are trusted": "Toutes les sessions dans ce salon chiffré sont fiables",
@ -2101,7 +2101,7 @@
"Regain access to your account and recover encryption keys stored in this session. Without them, you wont be able to read all of your secure messages in any session.": "Récupérez laccès à votre compte et restaurez les clés de chiffrement dans cette session. Sans elles, vous ne pourrez pas lire tous vos messages chiffrés dans nimporte quelle session.",
"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.": "Attention : Vos données personnelles (y compris les clés de chiffrement) seront stockées dans cette session. Effacez-les si vous nutilisez plus cette session ou si vous voulez vous connecter à un autre compte.",
"Sender session information": "Informations de session de lexpéditeur",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Mettez à niveau cette session pour lautoriser de vérifier dautres sessions, ce qui leur permettra daccéder aux messages chiffrés et de les marquer comme fiables pour les autres utilisateurs.",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Mettez à niveau cette session pour lautoriser à vérifier dautres sessions, ce qui leur permettra daccéder aux messages chiffrés et de les marquer comme fiables pour les autres utilisateurs.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Configurez le chiffrement sur cette session pour lui permettre de vérifier dautres sessions, ce qui leur permettra daccéder aux messages chiffrés et de les marquer comme fiables pour les autres utilisateurs.",
"This session can now verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Cette session peut à présent vérifier dautres sessions, ce qui leur permettra daccéder aux messages chiffrés et de les marquer comme fiables pour les autres utilisateurs.",
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Si vous ne configurez pas la récupération de messages sécurisée, vous ne pourrez pas restaurer lhistorique de vos messages chiffrés si vous vous déconnectez ou si vous utilisez une autre session.",
@ -2126,5 +2126,37 @@
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Si vous lavez fait accidentellement, vous pouvez configurer les messages sécurisés sur cette session ce qui re-chiffrera lhistorique des messages de cette session avec une nouvelle méthode de récupération.",
"How fast should messages be downloaded.": "À quelle fréquence les messages doivent être téléchargés.",
"of ": "sur ",
"Message downloading sleep time(ms)": "Temps dattente de téléchargement des messages (ms)"
"Message downloading sleep time(ms)": "Temps dattente de téléchargement des messages (ms)",
"Cancel entering passphrase?": "Annuler la saisie de la phrase de passe ?",
"If you cancel now, you won't complete your secret storage operation!": "Si vous annulez maintenant, vous ne terminerez pas votre opération du coffre secret !",
"Indexed rooms:": "Salons indexés :",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s sur %(totalRooms)s",
"If you cancel now, you won't complete verifying the other user.": "Si vous annuler maintenant, vous ne terminerez pas la vérification de lautre utilisateur.",
"If you cancel now, you won't complete verifying your other session.": "Si vous annulez maintenant, vous ne terminerez pas la vérification de votre autre session.",
"If you cancel now, you won't complete your secret storage operation.": "Si vous annulez maintenant, vous ne terminerez pas votre opération sur le coffre secret.",
"Show typing notifications": "Afficher les notifications de saisie",
"Reset cross-signing and secret storage": "Réinitialiser la signature croisée et le coffre secret",
"Destroy cross-signing keys?": "Détruire les clés de signature croisée ?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "La suppression des clés de signature croisée est permanente. Tous ceux que vous avez vérifié vont voir des alertes de sécurité. Il est peu probable que ce soit ce que vous voulez faire, sauf si vous avez perdu tous les appareils vous permettant deffectuer une signature croisée.",
"Clear cross-signing keys": "Vider les clés de signature croisée",
"Verify this session by completing one of the following:": "Vérifiez cette session en réalisant une des actions suivantes :",
"Scan this unique code": "Scannez ce code unique",
"or": "ou",
"Compare unique emoji": "Comparez des émojis uniques",
"Compare a unique set of emoji if you don't have a camera on either device": "Comparez une liste unique démojis si vous navez dappareil photo sur aucun des deux appareils",
"Not Trusted": "Non fiable",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) sest connecté à une nouvelle session sans la vérifier :",
"Ask this user to verify their session, or manually verify it below.": "Demandez à cet utilisateur de vérifier sa session, ou vérifiez-la manuellement ci-dessous.",
"Manually Verify": "Vérifier manuellement",
"Verify by scanning": "Vérifier en scannant",
"The version of Riot": "La version de Riot",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Si vous utilisez Riot sur un appareil où le toucher est le mécanisme primaire de saisie",
"Whether you're using Riot as an installed Progressive Web App": "Si vous utilisez Riot en tant quapplication web progressive (PWA)",
"Your user agent": "Votre agent utilisateur",
"The information being sent to us to help make Riot better includes:": "Les informations qui nous sont envoyées et qui nous aident à améliorer Riot comportent :",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "La session que vous essayez de vérifier ne prend pas en charge les codes QR ou la vérification démojis, qui sont les méthodes prises en charge par Riot. Essayez avec un autre client.",
"You declined": "Vous avez refusé",
"%(name)s declined": "%(name)s a refusé",
"accepting …": "nous acceptons…",
"declining …": "nous refusons…"
}

View file

@ -3,7 +3,7 @@
"Search": "Keresés",
"OK": "Rendben",
"Custom Server Options": "Egyedi szerverbeállítások",
"Dismiss": "Eltüntet",
"Dismiss": "Eltüntetés",
"Error": "Hiba",
"Failed to forget room %(errCode)s": "Nem sikerült elfelejteni a szobát: %(errCode)s",
"Favourite": "Kedvenc",
@ -31,7 +31,7 @@
"Default Device": "Alapértelmezett eszköz",
"Microphone": "Mikrofon",
"Camera": "Kamera",
"Advanced": "Haladó",
"Advanced": "Speciális",
"Algorithm": "Algoritmus",
"Always show message timestamps": "Üzenet időbélyeg folyamatos megjelenítése",
"Authentication": "Azonosítás",
@ -59,7 +59,7 @@
"%(senderName)s banned %(targetName)s.": "%(senderName)s kitiltotta őt: %(targetName)s.",
"Ban": "Kitilt",
"Banned users": "Kitiltott felhasználók",
"Bans user with given id": "Kitiltja a felhasználót a megadott ID-vel",
"Bans user with given id": "Kitiltja a megadott azonosítójú felhasználót",
"Blacklisted": "Fekete listára téve",
"Call Timeout": "Hívás időtúllépés",
"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.": "Nem lehet kapcsolódni a Matrix szerverhez - ellenőrizd a kapcsolatot, biztosítsd, hogy a <a>Matrix szerver tanúsítványa</a> hiteles legyen, és a böngésző kiterjesztések ne blokkolják a kéréseket.",
@ -221,8 +221,8 @@
"%(senderName)s requested a VoIP conference.": "%(senderName)s VoIP konferenciát kezdeményez.",
"Results from DuckDuckGo": "Eredmények a DuckDuckGo-ból",
"Return to login screen": "Vissza a bejelentkezési képernyőre",
"Riot does not have permission to send you notifications - please check your browser settings": "Riotnak nincs jogosultsága értesítést küldeni neked - ellenőrizd a böngésző beállításait",
"Riot was not given permission to send notifications - please try again": "Riotnak nincs jogosultsága értesítést küldeni neked - próbáld újra",
"Riot does not have permission to send you notifications - please check your browser settings": "A Riotnak nincs jogosultsága értesítést küldeni neked ellenőrizd a böngésző beállításait",
"Riot was not given permission to send notifications - please try again": "A Riotnak nincs jogosultsága értesítést küldeni neked próbáld újra",
"riot-web version:": "riot-web verzió:",
"Room %(roomId)s not visible": "%(roomId)s szoba nem látható",
"Room Colour": "Szoba színe",
@ -232,7 +232,7 @@
"Save": "Mentés",
"Scroll to bottom of page": "Az oldal aljára görget",
"Search failed": "Keresés sikertelen",
"Searches DuckDuckGo for results": "Keresés DuckDuckGo-val",
"Searches DuckDuckGo for results": "Keresés DuckDuckGóval",
"Seen by %(userName)s at %(dateTime)s": "%(userName)s %(dateTime)s időpontban látta",
"Send anyway": "Küld mindenképpen",
"Send Reset Email": "Visszaállítási e-mail küldése",
@ -326,25 +326,25 @@
"You seem to be in a call, are you sure you want to quit?": "Úgy tűnik hívásban vagy, biztosan kilépsz?",
"You seem to be uploading files, are you sure you want to quit?": "Úgy tűnik fájlokat töltesz fel, biztosan kilépsz?",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Nem leszel képes visszavonni ezt a változtatást mivel a felhasználót ugyanarra a szintre emeled amin te vagy.",
"Sun": "Vas",
"Mon": "Hé",
"Tue": "K",
"Wed": "Sze",
"Thu": "Csüt",
"Fri": "Pé",
"Sat": "Szo",
"Jan": "Jan",
"Feb": "Feb",
"Mar": "Már",
"Apr": "Ápr",
"May": "Máj",
"Jun": "Jún",
"Jul": "Júl",
"Aug": "Aug",
"Sep": "Szept",
"Oct": "Okt",
"Nov": "Nov",
"Dec": "Dec",
"Sun": "vas",
"Mon": "hé",
"Tue": "k",
"Wed": "sze",
"Thu": "csü",
"Fri": "pé",
"Sat": "szo",
"Jan": "jan",
"Feb": "feb",
"Mar": "már",
"Apr": "ápr",
"May": "máj",
"Jun": "jún",
"Jul": "júl",
"Aug": "aug",
"Sep": "szept",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
@ -663,10 +663,10 @@
"expand": "kinyit",
"Call Failed": "Sikertelen hívás",
"Review Devices": "Eszközök áttekintése",
"Call Anyway": "Mindenképpen hívj",
"Answer Anyway": "Mindenképpen felvesz",
"Call Anyway": "Hívás mindenképpen",
"Answer Anyway": "Felvétel mindenképpen",
"Call": "Hívás",
"Answer": "Felvesz",
"Answer": "Felvétel",
"Send": "Elküld",
"Old cryptography data detected": "Régi titkosítási adatot találhatók",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Régebbi Riot verzióból származó adatok találhatók. Ezek hibás működéshez vezethettek a végponttól-végpontig titkosításban régebbi verzióknál. A nemrég küldött/fogadott titkosított üzenetek ha a régi adatokat használták lehetséges hogy nem lesznek visszafejthetők ebben a verzióban. Ha problémákba ütközöl jelentkezz ki és vissza. A régi üzenetek elérésének biztosításához mentsd ki a kulcsokat és töltsd be újra.",
@ -1183,7 +1183,7 @@
"Create account": "Fiók létrehozása",
"Keep going...": "Így tovább...",
"Starting backup...": "Mentés indul...",
"A new recovery passphrase and key for Secure Messages have been detected.": "A Biztonságos Üzenetekhez új visszaállítási jelmondatot és kulcsot észleltünk.",
"A new recovery passphrase and key for Secure Messages have been detected.": "A Biztonságos üzenetekhez új visszaállítási jelmondatot és kulcsot észleltünk.",
"Recovery Method Removed": "Visszaállítási eljárás törölve",
"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.": "Ha nem te törölted a visszaállítási eljárást, akkor egy támadó hozzá akar férni a fiókodhoz. Azonnal változtasd meg a jelszavadat és állíts be egy visszaállítási eljárást a Beállításokban.",
"Chat with Riot Bot": "Csevegés a Riot Robottal",
@ -1374,7 +1374,7 @@
"The homeserver may be unavailable or overloaded.": "A Matrix szerver elérhetetlen vagy túlterhelt.",
"You have %(count)s unread notifications in a prior version of this room.|other": "%(count)s olvasatlan értesítésed van a régi verziójú szobában.",
"You have %(count)s unread notifications in a prior version of this room.|one": "%(count)s olvasatlan értesítésed van a régi verziójú szobában.",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Használsz vagy nem „morzsákat” (profilképek a szobalista felett)",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Használsz-e „morzsákat” (profilképek a szobalista felett)",
"Replying With Files": "Válasz fájlokkal",
"At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Egyenlőre nem lehet fájlal válaszolni. Szeretnéd feltölteni a fájlt úgy, hogy az nem egy válasz lesz?",
"The file '%(fileName)s' failed to upload.": "A %(fileName)s fájlt nem sikerült feltölteni.",
@ -2006,5 +2006,153 @@
"Unable to load session list": "A munkamenet listát nem lehet betölteni",
"Delete %(count)s sessions|other": "%(count)s munkamenet törlése",
"Delete %(count)s sessions|one": "%(count)s munkamenet törlése",
"This session is backing up your keys. ": "Ez a munkamenet elmenti a kulcsaidat. "
"This session is backing up your keys. ": "Ez a munkamenet elmenti a kulcsaidat. ",
"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!": "FIGYELEM: KULCS ELLENŐRZÉS HIBÁS! %(userId)s aláírási kulcsa és a munkamenet: %(deviceId)s ujjlenyomata \"%(fprint)s\" ami nem egyezik meg a megadott ujjlenyomattal ami \"%(fingerprint)s\". Ez jelentheti azt, hogy a kommunikációt lehallgatják!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "A megadott aláírási kulcs megegyezik %(userId)s felhasználótól kapott aláírási kulccsal ebben a munkamenetben: %(deviceId)s. A munkamenet hitelesnek lett jelölve.",
"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.": "Jelenleg a jelszó változtatás minden munkamenet végpontok közötti titkosító kulcsait alaphelyzetbe állítja, ezáltal a titkosított üzenetek olvashatatlanok lesznek, hacsak először nem mented ki a szobák kulcsait és töltöd vissza jelszóváltoztatás után. A jövőben ezt egyszerűsítjük majd.",
"This session is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.": "Ez az munkamenet <b>nem menti el a kulcsaidat</b>, de van létező mentésed ahonnan vissza tudsz állni és továbbléphetsz.",
"Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Állítsd be ezen az munkameneten a Kulcs Mentést kijelentkezés előtt, hogy ne veszíts el olyan kulcsot ami csak ezen az eszközön van meg.",
"Connect this session to Key Backup": "Munkamenet csatlakoztatása a Kulcs Mentéshez",
"Backup has a signature from <verify>unknown</verify> session with ID %(deviceId)s": "A mentésnek <verify>ismeretlen</verify> munkamenetből származó aláírása van ezzel az azonosítóval: %(deviceId)s",
"Backup has a <validity>valid</validity> signature from this session": "A mentés <validity>érvényes</validity> aláírást tartalmaz ebből a munkamenetből",
"Backup has an <validity>invalid</validity> signature from this session": "A mentés <validity>érvénytelen</validity> aláírást tartalmaz ebből a munkamenetből",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> session <device></device>": "A mentés <validity>érvénes</validity> aláírást tartalmaz <verify>érvényes</verify> munkamenetből <device></device>",
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> session <device></device>": "A mentés <validity>érvénes</validity> aláírást tartalmaz <verify>ellenőrizetlen</verify> munkamenetből <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> session <device></device>": "A mentés <validity>érvénytelen</validity> aláírást tartalmaz <verify>érvényes</verify> munkamenetből <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> session <device></device>": "A mentés <validity>érvénytelen</validity> aláírást tartalmaz <verify>ellenőrizetlen</verify> munkamenetből <device></device>",
"Backup is not signed by any of your sessions": "A mentés nem tartalmaz aláírást egyetlen munkamenetedből sem",
"This backup is trusted because it has been restored on this session": "Ez a mentés megbízható mert ebben a munkamenetben lett visszaállítva",
"Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "A mentési kulcs a biztonsági tárolóban van elmentve, de ebben a munkamenetben ez a lehetőség nincs engedélyezve. Kérlek engedélyezd az eszközök közötti hitelesítést a Laborokban a kulcs mentés állapotának megváltoztatásához.",
"Your keys are <b>not being backed up from this session</b>.": "A kulcsaid <b>nem kerülnek elmentésre erről a munkamenetről</b>.",
"Enable desktop notifications for this session": "Asztali értesítések engedélyezése ebben a munkamenetben",
"Enable audible notifications for this session": "Hallható értesítések engedélyezése ebben a munkamenetben",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "A jelszavad sikeresen megváltozott. Addig nem kapsz push értesítéseket más munkamenetekben amíg nem jelentkezel vissza bennük",
"Session ID:": "Munkamenet azonosító:",
"Session key:": "Munkamenet kulcs:",
"Sessions": "Munkamenetek",
"A session's public name is visible to people you communicate with": "A munkamenet nyilvános neve megjelenik azoknál az embereknél, akikkel beszélgetsz",
"This user has not verified all of their sessions.": "Ez a felhasználó még nem ellenőrizte az összes munkamenetét.",
"You have not verified this user.": "Még nem ellenőrizted ezt a felhasználót.",
"You have verified this user. This user has verified all of their sessions.": "Ezt a felhasználót ellenőrizted. Ez a felhasználó hitelesítette az összes munkamenetét.",
"Someone is using an unknown session": "Valaki ismeretlen munkamenetet használ",
"Some sessions for this user are not trusted": "Ennek a felhasználónak néhány munkamenete nem megbízható",
"All sessions for this user are trusted": "A felhasználónak minden munkamenete megbízható",
"Some sessions in this encrypted room are not trusted": "Ebben a titkosított szobában nem minden munkamenet megbízható",
"All sessions in this encrypted room are trusted": "Ebben a titkosított szobában minden munkamenet megbízható",
"Mod": "Mod",
"Your key share request has been sent - please check your other sessions for key share requests.": "A kulcs megosztás kérésed el lett küldve - ellenőrizd a többi munkamenetedet a kulcskérések után.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Kulcs megosztás kérés automatikusan el lett küldve neked a másik munkamenetekből. Ha elutasítottad vagy bezártad a kérést másik munkamenetedben, ide kattintva újrakérheted őket ehhez a munkamenethez.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Ha a többi munkameneted közül egyik sem tartalmazza a kulcsot ehhez az üzenethez, akkor ezt az üzenetet nem fogod tudni elolvasni.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Titkosítási kulcsok újrakérése</requestLink> a többi munkamenetből.",
"Encrypted by an unverified session": "Ellenőrizetlen munkamenet titkosította",
"Encrypted by a deleted session": "Törölt munkamenet által lett titkosítva",
"No sessions with registered encryption keys": "Nincs munkamenet regisztrált titkosítási kulcsokkal",
"Waiting for %(displayName)s to accept…": "%(displayName)s felhasználóra várakozás az elfogadáshoz…",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Az üzeneted biztonságban van és csak neked és a címzetteknek van meg az egyedi kulcs a visszafejtéshez.",
"Your messages are not secure": "Az üzeneteid nincsenek biztonságban",
"One of the following may be compromised:": "Valamelyik az alábbiak közül kompromittált:",
"Your homeserver": "Matrix szervered",
"The homeserver the user youre verifying is connected to": "Az ellenőrizendő felhasználó ehhez a matrix szerverhez kapcsolódik:",
"Yours, or the other users internet connection": "A te vagy a másik felhasználó Internet kapcsolata",
"Yours, or the other users session": "A te vagy a másik felhasználó munkamenete",
"%(count)s sessions|other": "%(count)s munkamenet",
"%(count)s sessions|one": "%(count)s munkamenet",
"Hide sessions": "Munkamenetek elrejtése",
"Verify by emoji": "Ellenőrzés emodzsival",
"Verify by comparing unique emoji.": "Ellenőrzés egyedi emodzsik összehasonlításával.",
"Ask %(displayName)s to scan your code:": "Kérd meg %(displayName)s felhasználót, hogy olvassa be a kódot:",
"If you can't scan the code above, verify by comparing unique emoji.": "Ha nem tudod beolvasni az alábbi kódot, ellenőrizd az egyedi emodzsik összehasonlításával.",
"You've successfully verified %(displayName)s!": "Sikeresen ellenőrizted a felhasználót: %(displayName)s!",
"Got it": "Értem",
"Verification timed out. Start verification again from their profile.": "Ellenőrzés időtúllépés. Kezd újra az ellenőrzést a másik felhasználó profiljából.",
"%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s törölte az ellenőrzést. Kezd újra az ellenőrzést a felhasználó profiljából.",
"You cancelled verification. Start verification again from their profile.": "Törölted az ellenőrzést. Kezd újra az ellenőrzést a felhasználó profiljából.",
"Encryption enabled": "Titkosítás engedélyezve",
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Ebben a szobában az üzenetek végpontok között titkosítottak. Tudj meg többet és ellenőrizd ezt a felhasználót a profiljában.",
"Encryption not enabled": "Titkosítás nincs engedélyezve",
"The encryption used by this room isn't supported.": "A szobában használt titkosítás nem támogatott.",
"Clear all data in this session?": "Minden adat törlése ebben a munkamenetben?",
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Az adatok törlése ebből a munkamenetből végleges. A titkosított üzenetek elvesznek hacsak nincsenek elmentve a kulcsai.",
"Verify session": "Munkamenet ellenőrzése",
"To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "A munkamenet megbízhatóságának eldöntéséhez ellenőrizd a kulcsot a Felhasználói Beállításokban ezen az eszközön, hogy megegyezik-e az alábbi kulccsal:",
"To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "A munkamenet megbízhatóságának eldöntéséhez vedd fel a kapcsolatot a tulajdonosával valami másik módon (pl. személyesen vagy telefonhívással) és kérdezd meg, hogy a Felhasználói Beállításokban ugyanazt a kulcsot látja-e mint ami alul van:",
"Session name": "Munkamenet neve",
"Session key": "Munkamenet kulcs",
"If it matches, press the verify button below. If it doesn't, then someone else is intercepting this session and you probably want to press the blacklist button instead.": "Ha egyezik akkor nyomd meg az ellenőriz gombot alul. Ha nem akkor valaki lehallgatja a munkamenetet és valószínű, hogy inkább a tiltólista gombra kellene kattintani.",
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "A felhasználót ellenőrizve a munkamenet megbízhatónak lesz jelölve és ugyanakkor a munkamenetedet is megbízhatónak fogja jelölni náluk.",
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Eszköz ellenőrzése és beállítás megbízhatónak. Az eszközben való megbízás megnyugtató lehet, ha végpontok közötti titkosítást használsz.",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Az eszköz ellenőrzése megbízhatónak fogja jelezni az eszközt és azok akik téged ellenőriztek megbíznak majd ebben az eszközödben.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "Új munkamenetet adtál hozzá '%(displayName)s' néven ami kéri a titkosítási kulcsokat.",
"Your unverified session '%(displayName)s' is requesting encryption keys.": "Az ellenőrizetlen munkameneted '%(displayName)s' néven titkosítási kulcsokat kér.",
"Loading session info...": "Munkamenet információk betöltése...",
"This will allow you to return to your account after signing out, and sign in on other sessions.": "Ezzel visszatérhetsz a fiókodba miután kijelentkeztél majd vissza egy másik munkamenetbe.",
"You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "Jelenleg tiltólistán vannak az ellenőrizetlen munkamenetek; hogy üzeneteket tudj küldeni ezekbe a munkamenetekbe ellenőrizned kell őket.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Javasoljuk, hogy minden munkamenetet ellenőrizz, hogy a tényleges tulajdonosához tartoznak vagy újraküldheted az üzenetet ha akarod anélkül, hogy ellenőriznéd őket.",
"Room contains unknown sessions": "A szoba ismeretlen munkameneteket tartalmaz",
"\"%(RoomName)s\" contains sessions that you haven't seen before.": "\"%(RoomName)s\" még nem látott munkameneteket tartalmaz.",
"Unknown sessions": "Ismeretlen munkamenetek",
"Cancel entering passphrase?": "Megszakítod a jelmondat bevitelét?",
"If you cancel now, you won't complete your secret storage operation!": "Ha most megszakítod, akkor nem fejezed be a biztonságos tárolóval kapcsolatos műveletet!",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "A jelmondat megadásával hozzáférhetsz a biztonságos üzeneteidhez és az eszközök közötti hitelesítéshez használt személyazonosságodhoz, hogy más munkameneteket hitelesíthess.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "A visszaállítási kulcs megadásával hozzáférhetsz a biztonságos üzeneteidhez és az eszközök közötti hitelesítéshez használt személyazonosságodhoz, hogy más munkameneteket hitelesíthess.",
"Recovery key mismatch": "A visszaállítási kulcs nem megfelelő",
"Incorrect recovery passphrase": "A visszaállítási jelmondat helytelen",
"Backup restored": "Mentés visszaállítva",
"Enter recovery passphrase": "Visszaállítási jelmondat megadása",
"Enter recovery key": "Visszaállítási kulcs megadása",
"Confirm your identity by entering your account password below.": "A fiók jelszó megadásával erősítsd meg a személyazonosságodat.",
"Message not sent due to unknown sessions being present": "Ismeretlen munkamenet miatt az üzenet nem került elküldésre",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Munkamenetek megmutatása</showSessionsText>, <sendAnywayText>küldés mindenképpen</sendAnywayText> vagy <cancelText>küldés megszakítása</cancelText>.",
"Your new session is now verified. Other users will see it as trusted.": "Az új munkameneted ellenőrizve. Mások megbízhatónak fogják látni.",
"Without completing security on this session, it wont have access to encrypted messages.": "Amíg a biztonság nincs beállítva a munkameneten nem fér hozzá a titkosított üzenetekhez.",
"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.": "A jelszó változtatás minden munkamenet végpontok közötti titkosító kulcsait alaphelyzetbe állítja, ezáltal a titkosított üzenetek olvashatatlanok lesznek, hacsak először nem mented ki a szobák kulcsait és töltöd vissza jelszóváltoztatás után. A jövőben ezt egyszerűsítjük majd.",
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Kijelentkeztettünk minden eszközödből és nem kapsz értesítéseket sem. Az értesítések újra engedélyezéséhez jelentkezz be újra az eszközökön.",
"Regain access to your account and recover encryption keys stored in this session. Without them, you wont be able to read all of your secure messages in any session.": "Szerezd vissza a hozzáférést a fiókodhoz és állítsd vissza az elmentett titkosítási kulcsokat ebben a munkamenetben. Ezek nélkül egyetlen munkamenetben sem tudod elolvasni a titkosított üzeneteidet.",
"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.": "Figyelmeztetés: A személyes adataid (beleértve a titkosító kulcsokat is) továbbra is az eszközön vannak tárolva. Ha az eszközt nem használod tovább vagy másik fiókba szeretnél bejelentkezni, töröld őket.",
"Sender session information": "Küldő munkamenet információ",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Fejleszd ezt a munkamenetet, hogy más munkamenetet tudj vele hitelesíteni amivel hozzáférnek a titkosított üzenetekhez és megbízhatónak lesznek jelölve más felhasználók számára.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Állítsd be a titkosítást ezen a munkameneten, hogy más munkamenetet tudj vele hitelesíteni amivel hozzáférnek a titkosított üzenetekhez és megbízhatónak lesznek jelölve más felhasználók számára.",
"Back up my encryption keys, securing them with the same passphrase": "Mentsd el a titkosítási kulcsaimat és védd őket ugyanazzal a jelmondattal",
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "A másolatot tartsd biztonságos helyen, mint pl. egy jelszókezelő (vagy széf).",
"Your recovery key": "Visszaállítási kulcsod",
"Copy": "Másol",
"You can now verify your other devices, and other users to keep your chats safe.": "Már ellenőrizheted a többi eszközödet és más felhasználókat, hogy a beszélgetéseidet biztonságban tudhasd.",
"Make a copy of your recovery key": "Készíts másolatot a visszaállítási kulcsodról",
"You're done!": "Kész!",
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "A Biztonságos Üzenet Visszaállítás beállítása nélkül kijelentkezés után vagy másik munkamenetet használva nem tudod visszaállítani a titkosított üzeneteidet.",
"Create key backup": "Kulcs mentés készítése",
"This session is encrypting history using the new recovery method.": "Ez a munkamenet az új visszaállítási metódussal titkosítja a régi üzeneteket.",
"This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "A munkamenet észrevette, hogy a visszaállítási jelmondat és a kulcs a Biztonságos üzenetekhez törölve lett.",
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Ha véletlenül tetted, beállíthatod a Biztonságos Üzeneteket ezen a munkameneten ami újra titkosítja a régi üzeneteket az visszaállítási eljárással.",
"Indexed rooms:": "Indexált szobák:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s / %(totalRooms)s",
"Message downloading sleep time(ms)": "Üzenet letöltés alvási idő (ms)",
"If you cancel now, you won't complete verifying the other user.": "A másik felhasználó ellenőrzését nem fejezed be, ha ezt most megszakítod.",
"If you cancel now, you won't complete verifying your other session.": "A másik munkameneted ellenőrzését nem fejezed be, ha ezt most megszakítod.",
"If you cancel now, you won't complete your secret storage operation.": "A Biztonsági tárolóval kapcsolatos műveletet nem fejezed be ha ezt most megszakítod.",
"Show typing notifications": "Gépelés visszajelzés megjelenítése",
"Verify this session by completing one of the following:": "Ellenőrizd ezt a munkamenetet az alábbiak egyikével:",
"Scan this unique code": "Ennek az egyedi kódnak a beolvasása",
"or": "vagy",
"Compare unique emoji": "Egyedi emodzsik összehasonlítása",
"Compare a unique set of emoji if you don't have a camera on either device": "Hasonlítsd össze az egyedi emodzsikat ha valamelyik eszközön nincs kamera",
"Not Trusted": "Megbízhatatlan",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) új munkamenetbe lépett be anélkül, hogy ellenőrizte volna:",
"Ask this user to verify their session, or manually verify it below.": "Kérd meg a felhasználót, hogy hitelesítse a munkamenetét vagy ellenőrizd kézzel alább.",
"Manually Verify": "Manuális ellenőrzés",
"Verify by scanning": "Ellenőrzés kód beolvasással",
"Destroy cross-signing keys?": "Eszközök közti hitelesítési kulcsok megsemmisítése?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Eszközök közti hitelesítési kulcsok törlése végleges. Mindenki akit ezzel hitelesítettél biztonsági figyelmeztetéseket fog látni. Hacsak nem vesztetted el az összes eszközödet amivel eszközök közti hitelesítést tudsz végezni, nem valószínű, hogy ezt szeretnéd tenni.",
"Clear cross-signing keys": "Eszközök közti hitelesítési kulcsok törlése",
"Reset cross-signing and secret storage": "Eszközök közti hitelesítés és biztonsági tároló visszaállítása",
"The version of Riot": "Riot verziója",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Olyan eszközön használod-e a Riotot, ahol az érintés az elsődleges beviteli mód",
"Whether you're using Riot as an installed Progressive Web App": "A Riotot progresszív webalkalmazásként használod-e",
"Your user agent": "Felhasználói ügynök",
"The information being sent to us to help make Riot better includes:": "Az alábbi információk kerülnek elküldésre amivel a Riotot jobbá tudjuk tenni:",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "Az ellenőrizni kívánt munkamenet nem támogatja se a QR kód beolvasást se az emodzsi ellenőrzést, amit a Riot támogat. Próbáld meg egy másik klienssel.",
"You declined": "Elutasítottad",
"%(name)s declined": "%(name)s elutasította",
"accepting …": "elfogadás …",
"declining …": "elutasítás …"
}

View file

@ -2009,5 +2009,151 @@
"Verify this device by confirming the following number appears on its screen.": "Verifica questo dispositivo confermando che il seguente numero appare sullo schermo.",
"Verify yourself & others to keep your chats safe": "Verifica te stesso e gli altri per mantenere sicure le chat",
"Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Backup chiavi salvato nell'archivio segreto, ma questa funzione non è attiva in questa sessione. Attiva la firma incrociata in Laboratori per modificare lo stato del backup chiavi.",
"Your keys are <b>not being backed up from this session</b>.": "Il backup chiavi <b>non viene fatto per questa sessione</b>."
"Your keys are <b>not being backed up from this session</b>.": "Il backup chiavi <b>non viene fatto per questa sessione</b>.",
"Enable desktop notifications for this session": "Attiva le notifiche desktop per questa sessione",
"Enable audible notifications for this session": "Attiva le notifiche audio per questa sessione",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "La tua password è stata cambiata correttamente. Non riceverai notifiche push in altre sessioni finchè non riesegui l'accesso in esse",
"Session ID:": "ID sessione:",
"Session key:": "Chiave sessione:",
"Message search": "Ricerca messaggio",
"Sessions": "Sessioni",
"A session's public name is visible to people you communicate with": "Il nome pubblico di una sessione è visibile alle persone con cui comunichi",
"This room is bridging messages to the following platforms. <a>Learn more.</a>": "Questa stanza fa un bridge dei messaggi con le seguenti piattaforme. <a>Maggiori informazioni.</a>",
"This room isnt bridging messages to any platforms. <a>Learn more.</a>": "Questa stanza non fa un bridge dei messaggi con alcuna piattaforma. <a>Maggiori informazioni.</a>",
"Bridges": "Bridge",
"This user has not verified all of their sessions.": "Questo utente non ha verificato tutte le sue sessioni.",
"You have not verified this user.": "Non hai verificato questo utente.",
"You have verified this user. This user has verified all of their sessions.": "Hai verificato questo utente. Questo utente ha verificato tutte le sue sessioni.",
"Someone is using an unknown session": "Qualcuno sta usando una sessione sconosciuta",
"Some sessions for this user are not trusted": "Alcune sessioni di questo utente non sono fidate",
"All sessions for this user are trusted": "Tutte le sessioni di questo utente sono fidate",
"Some sessions in this encrypted room are not trusted": "Alcune sessioni in questa stanza cifrata non sono fidate",
"All sessions in this encrypted room are trusted": "Tutte le sessioni in questa stanza cifrata sono fidate",
"Your key share request has been sent - please check your other sessions for key share requests.": "La tua richiesta di condivisione chiavi è stata inviata - controlla le tue altre sessioni per le richieste.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Le richieste di condivisione chiavi vengono inviate alle tue altre sessioni automaticamente. Se sulle altre sessioni l'hai rifiutata o annullata, clicca qui per chiedere di nuovo le chiavi per questa sessione.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Se le altre sessioni non hanno la chiave per questo messaggio non sarai in grado di decifrarlo.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Chiedi di nuovo le chiavi di cifratura</requestLink> dalle altre sessioni.",
"Encrypted by an unverified session": "Cifrato da una sessione non verificata",
"Encrypted by a deleted session": "Cifrato da una sessione eliminata",
"No sessions with registered encryption keys": "Nessuna sessione con chiavi di cifratura registrate",
"Waiting for %(displayName)s to accept…": "In attesa che %(displayName)s accetti…",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.": "I tuoi messaggi sono protetti e solo tu ed il destinatario avete le chiavi univoche per sbloccarli.",
"Your messages are not secure": "I tuoi messaggi non sono sicuri",
"One of the following may be compromised:": "Uno dei seguenti potrebbe essere compromesso:",
"Your homeserver": "Il tuo homeserver",
"The homeserver the user youre verifying is connected to": "L'homeserver al quale è connesso l'utente che stai verificando",
"Yours, or the other users internet connection": "La tua connessione internet o quella degli altri utenti",
"Yours, or the other users session": "La tua sessione o quella degli altri utenti",
"%(count)s sessions|other": "%(count)s sessioni",
"%(count)s sessions|one": "%(count)s sessione",
"Hide sessions": "Nascondi sessione",
"Verify by emoji": "Verifica via emoji",
"Verify by comparing unique emoji.": "Verifica confrontando emoji specifici.",
"Ask %(displayName)s to scan your code:": "Chiedi a %(displayName)s di scansionare il tuo codice:",
"If you can't scan the code above, verify by comparing unique emoji.": "Se non riesci a scansionare il codice sopra, verifica confrontando emoji specifiche.",
"You've successfully verified %(displayName)s!": "Hai verificato correttamente %(displayName)s!",
"Got it": "Capito",
"Verification timed out. Start verification again from their profile.": "Verifica scaduta. Inizia di nuovo la verifica dal suo profilo.",
"%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s ha annullato la verifica. Inizia di nuovo la verifica dal suo profilo.",
"You cancelled verification. Start verification again from their profile.": "Hai annullato la verifica. Inizia di nuovo la verifica dal suo profilo.",
"Encryption enabled": "Cifratura attivata",
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "I messaggi in questa stanza sono cifrati end-to-end. Maggiori info e verifica di questo utente nel suo profilo.",
"Encryption not enabled": "Cifratura non attivata",
"The encryption used by this room isn't supported.": "La cifratura usata da questa stanza non è supportata.",
"Clear all data in this session?": "Svuotare tutti i dati in questa sessione?",
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Lo svuotamento dei dati di questa sessione è permanente. I messaggi cifrati andranno persi a meno non si abbia un backup delle loro chiavi.",
"Verify session": "Verifica sessione",
"To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Per verficare che questa sessione sia fidata, controlla che la chiave che vedi nelle impostazioni utente su quel dispositivo corrisponda a questa sotto:",
"To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Per verificare che questa sessione sia fidata, contatta il suo proprietario usando altri metodi (es. di persona o per telefono) e chiedi se la chiave che vede nelle sue impostazioni utente di questa sessione corrisponde con questa sotto:",
"Session name": "Nome sessione",
"Session key": "Chiave sessione",
"If it matches, press the verify button below. If it doesn't, then someone else is intercepting this session and you probably want to press the blacklist button instead.": "Se corrisponde, premi il pulsante di verifica sottostante. Se non corrisponde, qualcuno sta intercettando questa sessione e dovresti invece premere il pulsante della lista nera.",
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "La verifica di questo utente contrassegnerà come fidata la sua sessione a te e viceversa.",
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Verifica questo dispositivo per segnarlo come fidato. Fidarsi di questo dispositivo offre a te e agli altri utenti una maggiore tranquillità nell'uso di messaggi cifrati end-to-end.",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "La verifica di questo dispositivo lo segnerà come fidato e gli utenti che si sono verificati con te si fideranno di questo dispositivo.",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Se non riesci a trovare qualcuno, chiedigli il nome utente, condividi il tuo (%(userId)s) o il <a>collegamento al profilo</a>.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "Hai aggiunto una nuova sessione '%(displayName)s', che sta chiedendo le chiavi di cifratura.",
"Your unverified session '%(displayName)s' is requesting encryption keys.": "La tua sessione non verificata '%(displayName)s' sta chiedendo le chiavi di cifratura.",
"Loading session info...": "Caricamento info di sessione...",
"New session": "Nuova sessione",
"Use this session to verify your new one, granting it access to encrypted messages:": "Usa questa sessione per verificare quella nuova, dandole accesso ai messaggi cifrati:",
"If you didnt sign in to this session, your account may be compromised.": "Se non hai fatto l'accesso a questa sessione, il tuo account potrebbe essere compromesso.",
"This wasn't me": "Non ero io",
"This will allow you to return to your account after signing out, and sign in on other sessions.": "Ciò ti permetterà di tornare al tuo account dopo la disconnessione e di accedere in altre sessioni.",
"You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "Stai attualmente bloccando sessioni non verificate; per inviare messaggi a queste sessioni devi verificarle.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Ti consigliamo di eseguire il processo di verifica per ogni sessione per confermare l'appartenenza al loro legittimo proprietario, ma puoi reinviare il messaggio senza la verifica se lo preferisci.",
"Room contains unknown sessions": "La stanza contiene sessioni sconosciute",
"\"%(RoomName)s\" contains sessions that you haven't seen before.": "\"%(RoomName)s\" contiene sessioni che non hai mai visto prima.",
"Unknown sessions": "Sessioni sconosciute",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Accedi alla tua cronologia di messaggi sicuri e all'identità a firma incrociata per verificare altre sessioni inserendo la tua password.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Accedi alla tua cronologia di messaggi sicuri e all'identità a firma incrociata per verificare altre sessioni inserendo la tua chiave di recupero.",
"Recovery key mismatch": "La chiave di recupero non corrisponde",
"Incorrect recovery passphrase": "Password di recupero errata",
"Backup restored": "Backup ripristinato",
"Enter recovery passphrase": "Inserisci password di recupero",
"Enter recovery key": "Inserisci chiave di recupero",
"Confirm your identity by entering your account password below.": "Conferma la tua identità inserendo la password dell'account sotto.",
"Message not sent due to unknown sessions being present": "Messaggio non inviato a causa della presenza di sessioni sconosciute",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Mostra le sessioni</showSessionsText>, <sendAnywayText>invia comunque</sendAnywayText> o <cancelText>annulla</cancelText>.",
"Your new session is now verified. Other users will see it as trusted.": "La tua nuova sessione è ora verificata. Gli altri utenti la vedranno come fidata.",
"Without completing security on this session, it wont have access to encrypted messages.": "Senza completare la sicurezza di questa sessione, essa non avrà accesso ai messaggi cifrati.",
"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.": "La modifica della password reimposterà qualsiasi chiave di cifratura end-to-end su tutte le sessioni, rendendo illeggibile la cronologia delle chat cifrate. Configura il Backup Chiavi o esporta le tue chiavi della stanza da un'altra sessione prima di reimpostare la password.",
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Sei stato disconnesso da tutte le sessioni e non riceverai più notifiche push. Per riattivare le notifiche, riaccedi su ogni dispositivo.",
"Regain access to your account and recover encryption keys stored in this session. Without them, you wont be able to read all of your secure messages in any session.": "Riprendi l'accesso al tuo account e recupera le chiavi di cifratura memorizzate in questa sessione. Senza di esse, non sarai in grado di leggere tutti i tuoi messaggi sicuri in qualsiasi sessione.",
"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.": "Attenzione: i tuoi dati personali (incluse le chiavi di cifratura) sono ancora memorizzati in questa sessione. Cancellali se hai finito di usare questa sessione o se vuoi accedere ad un altro account.",
"Sender session information": "Informazioni sessione del mittente",
"Restore your key backup to upgrade your encryption": "Ripristina il tuo backup chiavi per aggiornare la cifratura",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Aggiorna questa sessione per consentirle di verificare altre sessioni, garantendo loro l'accesso ai messaggi cifrati e contrassegnandole come fidate per gli altri utenti.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Imposta la cifratura su questa sessione per consentirle di verificare le altre sessioni, garantendo loro l'accesso ai messaggi cifrati e contrassegnandole come fidate per gli altri utenti.",
"Back up my encryption keys, securing them with the same passphrase": "Fai il backup delle mie chiavi di cifratura, proteggendole con la stessa password",
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Conservane una copia in un luogo sicuro, come un gestore di password o una cassaforte.",
"Your recovery key": "La tua chiave di recupero",
"Copy": "Copia",
"You can now verify your other devices, and other users to keep your chats safe.": "Ora puoi verificare i tuoi altri dispositivi e gli altri utenti per tenere al sicuro le tue chat.",
"Make a copy of your recovery key": "Fai una copia della chiave di recupero",
"You're done!": "Hai finito!",
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Senza configurare il Recupero Messaggi Sicuri, non potrai ripristinare la cronologia di messaggi cifrati se ti disconnetti o se usi un'altra sessione.",
"Create key backup": "Crea backup chiavi",
"This session is encrypting history using the new recovery method.": "Questa sessione sta cifrando la cronologia usando il nuovo metodo di recupero.",
"This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Questa sessione ha rilevato che la tua password di recupero e la chiave per i Messaggi Sicuri sono state rimosse.",
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Se l'hai fatto accidentalmente, puoi configurare Messaggi Sicuri su questa sessione che cripterà nuovamente la cronologia dei messaggi con un nuovo metodo di recupero.",
"If disabled, messages from encrypted rooms won't appear in search results.": "Se disattivato, i messaggi delle stanze cifrate non appariranno nei risultati di ricerca.",
"Disable": "Disattiva",
"Not currently downloading messages for any room.": "Nessuno scaricamento di messaggi in corso per alcuna stanza.",
"Downloading mesages for %(currentRoom)s.": "Scaricamento messaggi per %(currentRoom)s.",
"Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot sta tenendo in cache localmente i messaggi cifrati in modo sicuro affinché appaiano nei risultati di ricerca:",
"of ": "di ",
"Message downloading sleep time(ms)": "Tempo di attesa scaricamento messaggi (ms)",
"If you cancel now, you won't complete verifying the other user.": "Se adesso annulli, non completerai la verifica dell'altro utente.",
"If you cancel now, you won't complete verifying your other session.": "Se adesso annulli, non completerai la verifica dell'altra tua sessione.",
"If you cancel now, you won't complete your secret storage operation.": "Se adesso annulli, non completerai l'operazione dell'archivio segreto.",
"Cancel entering passphrase?": "Annullare l'inserimento della password?",
"Mod": "Moderatore",
"Indexed rooms:": "Stanze indicizzate:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s di %(totalRooms)s",
"The version of Riot": "La versione di Riot",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Se stai usando Riot su un dispositivo dove il tocco è il metodo di input principale",
"Whether you're using Riot as an installed Progressive Web App": "Se stai usando Riot installato come Web App Progressiva",
"Your user agent": "Il tuo user agent",
"The information being sent to us to help make Riot better includes:": "Le informazioni che ci vengono inviate per aiutarci a migliorare Riot includono:",
"Show typing notifications": "Mostra notifiche di scrittura",
"Verify this session by completing one of the following:": "Verifica questa sessione completando una delle seguenti cose:",
"Scan this unique code": "Scansiona questo codice univoco",
"or": "o",
"Compare unique emoji": "Confronta emoji univoci",
"Compare a unique set of emoji if you don't have a camera on either device": "Confrontate un set di emoji univoci se non avete una fotocamera sui dispositivi",
"Reset cross-signing and secret storage": "Reimposta la firma incrociata e l'archivio segreto",
"Not Trusted": "Non fidato",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) ha fatto l'accesso con una nuova sessione senza verificarla:",
"Ask this user to verify their session, or manually verify it below.": "Chiedi a questo utente di verificare la sua sessione o verificala manualmente sotto.",
"Manually Verify": "Verifica manualmente",
"Verify by scanning": "Verifica con la scansione",
"Destroy cross-signing keys?": "Distruggere le chiavi di firma incrociata?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "L'eliminazione delle chiavi di firma incrociata è permanente. Chiunque si sia verificato con te vedrà avvisi di sicurezza. Quasi sicuramente non vuoi fare questa cosa, a meno che tu non abbia perso tutti i dispositivi da cui puoi fare l'accesso.",
"Clear cross-signing keys": "Elimina chiavi di firma incrociata",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "La sessione che stai cercando di verificare non supporta la scansione del codice QR o la verifica emoji, che sono supportate da Riot. Prova con un client diverso.",
"You declined": "Hai rifiutato",
"%(name)s declined": "%(name)s ha rifiutato",
"accepting …": "accettazione …",
"declining …": "rifiuto …"
}

View file

@ -420,7 +420,7 @@
"I have verified my email address": "Aš patvirtinau savo el. pašto adresą",
"Return to login screen": "Grįžti į prisijungimo ekraną",
"Send Reset Email": "Siųsti atstatymo el. laišką",
"Incorrect username and/or password.": "Neteisingas naudotojo vardas ir/ar slaptažodis.",
"Incorrect username and/or password.": "Neteisingas vartotojo vardas ir/arba slaptažodis.",
"Please note you are logging into the %(hs)s server, not matrix.org.": "Turėkite omenyje, kad jūs prisijungiate prie %(hs)s serverio, o ne matrix.org.",
"Failed to fetch avatar URL": "Nepavyko gauti avataro URL",
"Commands": "Komandos",
@ -526,7 +526,7 @@
"Enable inline URL previews by default": "Įjungti tiesiogines URL nuorodų peržiūras pagal numatymą",
"Enable URL previews for this room (only affects you)": "Įjungti URL nuorodų peržiūras šiame kambaryje (įtakoja tik jus)",
"Enable URL previews by default for participants in this room": "Įjungti URL nuorodų peržiūras pagal numatymą dalyviams šiame kambaryje",
"Confirm password": "Pakartokite slaptažodį",
"Confirm password": "Patvirtinkite slaptažodį",
"Demote yourself?": "Pažeminti save?",
"Demote": "Pažeminti",
"Share Link to User": "Dalintis nuoroda į naudotoją",
@ -625,8 +625,8 @@
"This homeserver has hit its Monthly Active User limit so <b>some users will not be able to log in</b>.": "Šis namų serveris pasiekė savo mėnesinį aktyvių naudotojų limitą, taigi, <b>kai kurie naudotojai negalės prisijungti</b>.",
"This homeserver has exceeded one of its resource limits so <b>some users will not be able to log in</b>.": "Šis namų serveris viršijo vieno iš savo išteklių limitą, taigi, <b>kai kurie naudotojai negalės prisijungti</b>.",
"An error ocurred whilst trying to remove the widget from the room": "Įvyko klaida, bandant pašalinti valdiklį iš kambario",
"Blacklist": "Įtraukti į juodąjį sąrašą",
"Unblacklist": "Pašalinti iš juodojo sąrašo",
"Blacklist": "Blokuoti",
"Unblacklist": "Atblokuoti",
"Verify...": "Patvirtinti...",
"Communities": "Bendruomenės",
"Home": "Pradžia",
@ -714,12 +714,12 @@
"Unable to verify email address.": "Nepavyko patvirtinti el. pašto adreso.",
"This will allow you to reset your password and receive notifications.": "Tai jums leis atstatyti savo slaptažodį ir gauti pranešimus.",
"Skip": "Praleisti",
"Username not available": "Naudotojo vardas neprieinamas",
"Username invalid: %(errMessage)s": "Neteisingas naudotojo vardas: %(errMessage)s",
"Username not available": "Vartotojo vardas negalimas",
"Username invalid: %(errMessage)s": "Neteisingas vartotojo vardas: %(errMessage)s",
"An error occurred: %(error_string)s": "Įvyko klaida: %(error_string)s",
"Checking...": "Tikrinama...",
"Username available": "Naudotojo vardas yra prieinamas",
"To get started, please pick a username!": "Norėdami pradėti, pasirinkite naudotojo vardą!",
"Username available": "Vartotojo vardas galimas",
"To get started, please pick a username!": "Norėdami pradėti, pasirinkite vartotojo vardą!",
"If you already have a Matrix account you can <a>log in</a> instead.": "Jeigu jau turite Matrix paskyrą, tuomet vietoj to, galite <a>prisijungti</a>.",
"Unable to restore backup": "Nepavyko atkurti atsarginės kopijos",
"No backup found!": "Nerasta jokios atsarginės kopijos!",
@ -755,7 +755,7 @@
"Sign In": "Prisijungti",
"Explore rooms": "Žvalgyti kambarius",
"Your Riot is misconfigured": "Jūsų Riot yra neteisingai sukonfigūruotas",
"Sign in to your Matrix account on %(serverName)s": "Prisijunkite prie savo paskyros %(serverName)s serveryje",
"Sign in to your Matrix account on %(serverName)s": "Prisijunkite prie savo Matrix paskyros %(serverName)s serveryje",
"Sign in to your Matrix account on <underlinedServerName />": "Prisijunkite prie savo paskyros <underlinedServerName /> serveryje",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Nepriklausomai nuo to ar jūs naudojate 'duonos trupinių' funkciją (avatarai virš kambarių sąrašo)",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Kur šis puslapis įtraukia identifikuojamą informaciją, kaip kambarys, vartotojas ar grupės ID, tie duomenys yra pašalinami prieš siunčiant į serverį.",
@ -900,5 +900,27 @@
"Are you sure you want to leave the room '%(roomName)s'?": "Ar tikrai norite palikti kambarį %(roomName)s?",
"%(creator)s created and configured the room.": "%(creator)s sukūrė ir sukonfigūravo kambarį.",
"Riot failed to get the public room list.": "Riot nepavyko gauti viešų kambarių sąrašą.",
"General failure": "Bendras triktis"
"General failure": "Bendras triktis",
"Messages containing my username": "Žinutės, kuriose yra mano vartotojo vardas",
"Set a new account password...": "Nustatyti naują paskyros slaptažodį...",
"If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Jei jūs pateikėte klaidą per GitHub, derinimo žurnalai (debug logs) gali padėti mums surasti problemą. Derinimo žurnaluose yra programos naudojimo duomenys, įskaitant jūsų vartotojo vardą, ID ar kitus kambarių arba grupių, kuriuose jūs lankėtės, pavadinimus ir kitų vartotojų vardus. Juose nėra žinučių.",
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (galia %(powerLevelNumber)s)",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Derinimo žurnaluose yra programos naudojimo duomenys, įskaitant jūsų vartotojo vardą, ID ar kitus kambarių arba grupių, kuriuose jūs lankėtės, pavadinimus ir kitų vartotojų vardus. Juose nėra žinučių.",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or <a>profile link</a>.": "Jei jūs negalite kažkieno surasti, paklauskite jų vartotojo vardo, pasidalinkite savo vartotojo vardu (%(userId)s) arba <a>profilio nuoroda</a>.",
"If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.": "Jei jūs negalite kažkieno surasti, paklauskite jų vartotojo vardo (pvz.: @vartotojas:serveris.lt) arba <a>pasidalinkite šiuo kambariu</a>.",
"A username can only contain lower case letters, numbers and '=_-./'": "Vartotojo vardą gali sudaryti tik mažosios raidės, skaičiai ir '=_-./'",
"The username field must not be blank.": "Vartotojo vardo laukelis negali būti tuščias.",
"Username": "Vartotojo vardas",
"Not sure of your password? <a>Set a new one</a>": "Pamiršote slaptažodį? <a>Nustatykite naują</a>",
"Enter username": "Įveskite vartotojo vardą",
"Confirm": "Patvirtinti",
"Create your Matrix account on %(serverName)s": "Sukurkite savo Matrix paskyrą %(serverName)s serveryje",
"Create your Matrix account on <underlinedServerName />": "Sukurkite savo Matrix paskyrą <underlinedServerName /> serveryje",
"Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Nustatykite el. paštą paskyros susigrąžinimui. Naudokite el. paštą ar tel. nr. norėdami pasirinktinai būti aptinkami esamų kontaktų.",
"Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Nustatykite el. paštą paskyros susigrąžinimui. Naudokite el. paštą norėdami pasirinktinai būti aptinkami esamų kontaktų.",
"Sign in instead": "Prisijungti",
"A verification email will be sent to your inbox to confirm setting your new password.": "Patvirtinimo laiškas bus išsiųstas į jūsų pašto dėžutę tam, kad patvirtintumėte naujo slaptažodžio nustatymą.",
"Set a new password": "Nustatykite naują slaptažodį",
"Create account": "Sukurti paskyrą",
"Create your account": "Sukurkite savo paskyrą"
}

View file

@ -388,7 +388,7 @@
"To continue, please enter your password.": "Voer uw wachtwoord in om verder te gaan.",
"Blacklist": "Blokkeren",
"Unblacklist": "Deblokkeren",
"I verify that the keys match": "Ik verifieer dat de sleutels overeenkomen",
"I verify that the keys match": "Ik verklaar dat de sleutels overeenkomen",
"Unable to restore session": "Sessieherstel lukt niet",
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Als u reeds een recentere versie van Riot heeft gebruikt is uw sessie mogelijk onverenigbaar met deze versie. Sluit dit venster en ga terug naar die recentere versie.",
"Unknown Address": "Onbekend adres",
@ -586,7 +586,7 @@
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s is weggegaan en weer toegetreden",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s hebben hun uitnodigingen %(count)s keer afgewezen",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s hebben hun uitnodigingen afgewezen",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s heeft de uitnodiging %(count)smaal afgewezen",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s heeft de uitnodiging %(count)s maal afgewezen",
"%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)s heeft de uitnodiging afgeslagen",
"%(severalUsers)shad their invitations withdrawn %(count)s times|other": "De uitnodigingen van %(severalUsers)s zijn %(count)s keer ingetrokken",
"%(severalUsers)shad their invitations withdrawn %(count)s times|one": "De uitnodigingen van %(severalUsers)s zijn ingetrokken",
@ -609,11 +609,11 @@
"was kicked %(count)s times|one": "is uit het gesprek gezet",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s hebben hun naam %(count)s keer gewijzigd",
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s hebben hun naam gewijzigd",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s is %(count)smaal van naam veranderd",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s is %(count)s maal van naam veranderd",
"%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s is van naam veranderd",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)s hebben hun avatar %(count)s keer gewijzigd",
"%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)s hebben hun avatar gewijzigd",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s is %(count)smaal van profielfoto veranderd",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s is %(count)s maal van profielfoto veranderd",
"%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)s is van profielfoto veranderd",
"%(items)s and %(count)s others|other": "%(items)s en %(count)s andere",
"%(items)s and %(count)s others|one": "%(items)s en één ander",
@ -1679,5 +1679,219 @@
"Verify this session": "Deze sessie verifiëren",
"Encryption upgrade available": "Er is een bijgewerkte versleuteling beschikbaar",
"You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Type <code>/help</code> om alle opdrachten te zien. Was het uw bedoeling dit als bericht te sturen?",
"Help": "Hulp"
"Help": "Hulp",
"Set up encryption": "Versleuteling instellen",
"Unverified session": "Ongeverifieerde sessie",
"This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "Dit vergt toegang tot de verstek-identiteitsserver <server /> om emailadres of telefoonummer te valideren, maar die server heeft kent geen gebruiksvoorwaarden.",
"Trust": "Vertrouw",
"Custom (%(level)s)": "Aangepast (%(level)s)",
"Error upgrading room": "Bijwerken gesprek mislukt",
"Double check that your server supports the room version chosen and try again.": "Ga nogmaals na dat de server de gekozen gespreksversie ondersteunt, en probeer dan opnieuw.",
"Verifies a user, session, and pubkey tuple": "Verifieert een combinatie van gebruiker+sessie+publieke sleutel",
"Unknown (user, session) pair:": "Onbekende combinatie gebruiker+sessie:",
"Session already verified!": "Sessie al geverifieerd!",
"WARNING: Session already verified, but keys do NOT MATCH!": "PAS OP: De sessie is al geverifieerd, maar de sleutels komen NIET OVEREEN!",
"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!": "PAS OP: Sleutelverificatie MISLUKT! De combinatie %userId)s + sessie %(deviceId)s is ondertekend met \"%(fprint)s\" - maar de opgegeven sleutel is \"%(fingerprint)s\". Wellicht worden uw berichten onderschept!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "De door u verschafte en de van %(userId)ss sessie %(deviceId)s verkregen sleutels komen overeen. De sessie is daarmee geverifieerd.",
"%(senderName)s added %(addedAddresses)s and %(count)s other addresses to this room|other": "%(senderName) heeft %(addedAddresses)s en %(count)d andere adressen aan dit gesprek toegevoegd",
"%(senderName)s removed %(removedAddresses)s and %(count)s other addresses from this room|other": "%(senderName)s heeft dit gesprek ontdaan van %(removedAddresses)s en %(count)s andere adressen",
"%(senderName)s removed %(countRemoved)s and added %(countAdded)s addresses to this room": "%(senderName)s heeft dit gesprek ontdaan van %(countRemoved)s adressen, en er %(countAdded)s toegevoegd",
"%(senderName)s placed a voice call.": "%(senderName)s probeert u te bellen.",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s poogt u te bellen, maar uw browser ondersteunt dat niet",
"%(senderName)s placed a video call.": "%(senderName)s doet een video-oproep.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s doet een video-oproep, maar uw browser ondersteunt dat niet",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s heeft de banregel voor gebruikers die met %(glob)s stroken verwijderd",
"%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s heeft de banregel voor gesprekken die met %(glob)s stroken verwijderd",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s heeft de banregel voor servers die met %(glob)s stroken verwijderd",
"%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s heeft een banregel die met %(glob)s strookt verwijderd",
"%(senderName)s updated an invalid ban rule": "%(senderName)s heeft een ongeldige banregel bijgewerkt",
"%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s heeft de regel bijgewerkt die gebruikers die met %(glob)s sporen verbant vanwege %(reason)s",
"%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s heeft de regel bijgewerkt die gesprekken die met %(glob)s sporen verbant vanwege %(reason)s",
"%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s heeft de regel bijgewerkt die servers die met %(glob)s sporen verbant vanwege %(reason)s",
"%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s heeft een banregel vanwege %(reason)s die met %(glob)s spoort bijgewerkt",
"%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s heeft geregeld dat gebruikers die met %(glob)s sporen verbannen worden vanwege %(reason)s",
"%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s heeft geregeld dat gesprekken die met %(glob)s sporen verbannen worden vanwege %(reason)s",
"%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s heeft geregeld dat servers die met %(glob)s sporen verbannen worden vanwege %(reason)s",
"%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s heeft geregeld dat alles wat met %(glob)s spoort verbannen wordt vanwege %(reason)s",
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s heeft het patroon van een banregel voor gebruikers wegens %(reason)s aangepast van %(oldGlob)s tot %(newGlob)s",
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s heeft het patroon van een banregel voor gesprekken wegens %(reason)s aangepast van %(oldGlob)s tot %(newGlob)s",
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s heeft het patroon van een banregel voor servers wegens %(reason)s aangepast van %(oldGlob)s tot %(newGlob)s",
"%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s heeft het patroon van een banregel wegens %(reason)s aangepast van %(oldGlob)s tot %(newGlob)s",
"The message you are trying to send is too large.": "Uw bericht is te lang om te verzenden.",
"a few seconds ago": "enige tellen geleden",
"about a minute ago": "een minuut of zo geleden",
"%(num)s minutes ago": "%(num)s minuten geleden",
"about an hour ago": "een uur of zo geleden",
"%(num)s hours ago": "%(num)s uren geleden",
"about a day ago": "een dag of zo geleden",
"%(num)s days ago": "%(num)s dagen geleden",
"a few seconds from now": "over een paar tellen",
"about a minute from now": "over een minuut of zo",
"%(num)s minutes from now": "over %(num)s minuten",
"about an hour from now": "over een uur of zo",
"%(num)s hours from now": "over %(num)s uur",
"about a day from now": "over een dag of zo",
"%(num)s days from now": "over %(num)s dagen",
"%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
"Try out new ways to ignore people (experimental)": "Nieuwe manieren om mensen te negeren uitproberen (nog in ontwikkeling)",
"Show a presence dot next to DMs in the room list": "Toon aanwezigheid bij tweegesprekken in de gesprekkenlijst",
"Enable cross-signing to verify per-user instead of per-session (in development)": "Gebruik gebruikersverificatie in plaats van sessieverificatie (nog in ontwikkeling)",
"Enable local event indexing and E2EE search (requires restart)": "Indexeer lokaal gebeurtenissen en maak zo E2EE-zoeken mogelijk (vergt een herstart)",
"Show info about bridges in room settings": "Toon bruginformatie in gespreksinstellingen",
"Show padlocks on invite only rooms": "Toon hangsloten op besloten gesprekken",
"Match system theme": "Pas aan aan systeemthema",
"Never send encrypted messages to unverified sessions from this session": "Zend vanaf deze sessie nooit versleutelde berichten naar ongeverifieerde sessies",
"Never send encrypted messages to unverified sessions in this room from this session": "Zend vanaf deze sessie nooit versleutelde berichten naar ongeverifieerde sessies in dit gesprek",
"Enable message search in encrypted rooms": "Sta zoeken in versleutelde gesprekken toe",
"Keep secret storage passphrase in memory for this session": "Onthoud in deze sessie het wachtwoord voor sleutelopslag",
"How fast should messages be downloaded.": "Ophaalfrequentie van berichten.",
"My Ban List": "Mijn banlijst",
"This is your list of users/servers you have blocked - don't leave the room!": "Dit is de lijst van door u geblokkeerde servers/gebruikers. Verlaat dit gesprek niet!",
"Confirm the emoji below are displayed on both devices, in the same order:": "Bevestig dat beide apparaten dezelfde emojis in dezelfde volgorde tonen:",
"Verify this device by confirming the following number appears on its screen.": "Verifieer dit apparaat door te bevestigen dat het scherm het volgende getal toont.",
"Waiting for %(displayName)s to verify…": "Wachten tot %(displayName)s geverifieerd heeft…",
"They match": "Ze komen overeen",
"They don't match": "Ze komen niet overeen",
"To be secure, do this in person or use a trusted way to communicate.": "Doe dit voor de zekerheid in persona, of via een betrouwbaar communicatiemedium.",
"Lock": "Hangslot",
"Verify yourself & others to keep your chats safe": "Verifieer jezelf en anderen om je gesprekken veilig te houden",
"Other users may not trust it": "Mogelijk wantrouwen anderen het",
"Upgrade": "Bijwerken",
"Verify": "Verifiëren",
"Later": "Later",
"Review": "Contrôle",
"Decline (%(counter)s)": "Afwijzen (%(counter)s)",
"This bridge was provisioned by <user />.": "Dank aan <user /> voor de brug.",
"This bridge is managed by <user />.": "Brug onderhouden door <user />.",
"Workspace: %(networkName)s": "Werkruimte: %(networkName)s",
"Channel: %(channelName)s": "Kanaal: %(channelName)s",
"Show less": "Minder tonen",
"Show more": "Meer tonen",
"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.": "Momenteel stelt een wachtwoordswijziging alle berichtsleutels in alle sessies opnieuw in, en maakt zo oude versleutelde berichten onleesbaar - tenzij u uw sleutels eerst wegschrijft, en na afloop weer inleest. Dit zal verbeterd worden.",
"Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, <code>@bot:*</code> would ignore all users that have the name 'bot' on any server.": "Geef hier te negeren gebruikers en servers in. Asterisken staan voor willekeurige tekenreeksen; zo leidt <code>@bot:*</code> tot het negeren van gebruikers die 'bot' heten op alle servers.",
"in memory": "in het geheugen",
"not found": "niet gevonden",
"Your homeserver does not support session management.": "Uw thuisserver ondersteunt geen sessiebeheer.",
"Unable to load session list": "Kan sessielijst niet laden",
"Delete %(count)s sessions|other": "Verwijder %(count)s sessies",
"Delete %(count)s sessions|one": "Verwijder %(count)s sessie",
"The version of Riot": "De versie van Riot",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Of u Riot op een apparaat gebruikt waarop een aanraakscherm de voornaamste invoermethode is",
"Whether you're using Riot as an installed Progressive Web App": "Of u Riot gebruikt als een geïnstalleerde Progressive-Web-App",
"Your user agent": "Uw browsers user-agent",
"The information being sent to us to help make Riot better includes:": "De informatie die naar ons wordt verstuurd om Riot te verbeteren bevat:",
"If you cancel now, you won't complete verifying the other user.": "Als u nu annuleert zul u de andere gebruiker niet verifiëren.",
"If you cancel now, you won't complete verifying your other session.": "Als u nu annuleert zal u uw andere sessie niet verifiëren.",
"If you cancel now, you won't complete your secret storage operation.": "Als u nu annuleert zal de sleutelopslag worden afgebroken.",
"Cancel entering passphrase?": "Wachtwoord invoer annuleren?",
"Show typing notifications": "Typmeldingen weergeven",
"Verify this session by completing one of the following:": "Verifieer deze sessie door een van het volgende te doen:",
"Scan this unique code": "Scan deze unieke code",
"or": "of",
"Compare unique emoji": "Vergelijk unieke emoji",
"Compare a unique set of emoji if you don't have a camera on either device": "Vergelijk een unieke lijst emoji als beide apparaten geen camera bevatten",
"Start": "Start",
"Securely cache encrypted messages locally for them to appear in search results.": "Sla versleutelde berichten beveiligd op om ze weer te geven in zoekresultaten.",
"Enable": "Inschakelen",
"Connecting to integration manager...": "Verbinding maken met de integratiebeheerder...",
"Cannot connect to integration manager": "Kan geen verbinding maken met de integratiebeheerder",
"The integration manager is offline or it cannot reach your homeserver.": "De integratiebeheerder is offline of kan uw thuisserver niet bereiken.",
"This session is backing up your keys. ": "Deze sessie maakt back-ups van uw sleutels. ",
"not stored": "niet opgeslagen",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Uw wachtwoord is gewijzigd. U zal geen pushmeldingen op uw andere sessies meer ontvangen, totdat u uzelf daarop opnieuw aanmeld",
"Ignored/Blocked": "Genegeerd/Geblokkeerd",
"Error adding ignored user/server": "Fout bij het toevoegen van een genegeerde gebruiker/server",
"Something went wrong. Please try again or view your console for hints.": "Er is iets fout gegaan. Probeer het opnieuw of bekijk de console om inzicht te krijgen.",
"Error subscribing to list": "Fout bij het abonneren op de lijst",
"Please verify the room ID or alias and try again.": "Controleer de gespreks-ID of gespreks(bij)naam en probeer het opnieuw.",
"Error removing ignored user/server": "Fout bij het verwijderen van genegeerde gebruiker/server",
"Error unsubscribing from list": "Fout bij het opzeggen van een abonnement op de lijst",
"Please try again or view your console for hints.": "Probeer het opnieuw of bekijk de console om inzicht te krijgen.",
"None": "Geen",
"You have not ignored anyone.": "U heeft niemand genegeerd.",
"You are currently ignoring:": "U negeert op dit moment:",
"You are not subscribed to any lists": "U heeft geen abonnement op een lijst",
"Unsubscribe": "Zeg abonnement op",
"View rules": "Bekijk regels",
"You are currently subscribed to:": "U heeft een abonnement op:",
"⚠ These settings are meant for advanced users.": "⚠ Deze instellingen zijn bedoeld voor gevorderde gebruikers.",
"Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Het negeren van gebruikers gaat via banlijsten. Deze bevatten regels over wie verbannen moet worden. Het nemen van een abonnement op een banlijst betekend dat je de gebruikers/servers die op de lijst staan niet meer zult zien.",
"Personal ban list": "Persoonlijke banlijst",
"Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Uw persoonlijke banlijst bevat alle gebruikers/server waar u geen berichten meer van wilt zien. Nadat u een gebruiker/server heeft genegeerd, zal een nieuwe kamer worden aangemaakt met de naam \"Mijn Banlijst\". Om de lijst actief te houden dient u de kamer niet te verlaten.",
"Server or user ID to ignore": "Server of gebruikers-ID om te negeren",
"eg: @bot:* or example.org": "bijvoorbeeld: @bot:* of example.org",
"Subscribed lists": "Abonnementen op lijsten",
"Subscribing to a ban list will cause you to join it!": "Wanneer u een abonnement neemt zal u worden toegevoegd!",
"If this isn't what you want, please use a different tool to ignore users.": "Als u dit niet wilt kunt u een andere methode gebruiken om gebruikers te negeren.",
"Room ID or alias of ban list": "Gespreks-ID of (bij)naam of banlijst",
"Subscribe": "Abonneer",
"Enable desktop notifications for this session": "Schakel desktopmeldingen in voor deze sessie",
"Enable audible notifications for this session": "Schakel meldingen met geluid in voor deze sessie",
"You should:": "U zou:",
"check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "uw browser extensies bekijken voor extensies die mogelijk de identiteitsserver blokkeren (zoals Privacy Badger)",
"contact the administrators of identity server <idserver />": "contact opnemen met de beheerders van de identiteitsserver <idserver />",
"wait and try again later": "wachten en het later weer proberen",
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Gebruik een integratiebeheerder <b>(%(serverName)s)</b> om robots, widgets en stickerpakketten te beheren.",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Gebruik een integratiebeheerder om robots, widgets en stickerpakketten te beheren.",
"Manage integrations": "Beheer integraties",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integratiebeheerders ontvangen configuratie-informatie en kunnen widgets aanpassen, uitnodigingen voor de kamer versturen en machtsniveau's namens u aanpassen.",
"Ban list rules - %(roomName)s": "Banlijst regels - %(roomName)s",
"Server rules": "Server regels",
"User rules": "Gebruiker regels",
"Show tray icon and minimize window to it on close": "Geef een icoon in de systeembalk weer en minimaliseer het venster wanneer het wordt gesloten",
"Session ID:": "Sessie-ID:",
"Session key:": "Sessiesleutel:",
"Message search": "Berichten zoeken",
"Sessions": "Sessies",
"A session's public name is visible to people you communicate with": "Een sessie's publieke naam is zichtbaar voor de mensen waarmee u communiceert",
"This room is bridging messages to the following platforms. <a>Learn more.</a>": "Dit gesprek wordt overbrugt naar de volgende platformen. <a>Lees meer</a>",
"This room isnt bridging messages to any platforms. <a>Learn more.</a>": "Dit gesprek wordt niet overbrugt naar andere platformen. <a>Lees meer.</a>",
"Bridges": "Overbruggingen",
"This user has not verified all of their sessions.": "Deze gebruiker heeft niet al haar sessies geverifieerd.",
"You have not verified this user.": "U heeft deze gebruiker niet geverifieerd.",
"You have verified this user. This user has verified all of their sessions.": "U heeft deze gebruiker geverifieerd. Deze gebruiker heeft al haar sessies geverifieerd.",
"Someone is using an unknown session": "Iemand gebruikt een onbekende sessie",
"This room is end-to-end encrypted": "Dit gesprek is van eind-tot-eind versleuteld",
"Everyone in this room is verified": "Iedereen in dit gesprek is geverifieerd",
"Some sessions for this user are not trusted": "Sommige sessies van deze gebruiker zijn niet vertrouwd",
"All sessions for this user are trusted": "Alle sessies van deze gebruiker zijn vertrouwd",
"Some sessions in this encrypted room are not trusted": "Sommige sessies in dit versleutelde gesprek zijn niet vertrouwd",
"All sessions in this encrypted room are trusted": "Alle sessies in dit versleutelde gesprek zijn vertrouwd",
"Mod": "Mod",
"rooms.": "gesprekken.",
"Recent rooms": "Actuele gesprekken",
"Direct Messages": "Tweegesprekken",
"If disabled, messages from encrypted rooms won't appear in search results.": "Dit moet aanstaan om te kunnen zoeken in versleutelde gesprekken.",
"Indexed rooms:": "Geïndexeerde gesprekken:",
"Cross-signing and secret storage are enabled.": "Cross-signing en sleutelopslag zijn ingeschakeld.",
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Uw account heeft een cross-signing identiteit in de sleutelopslag, maar deze is nog niet vertrouwd door de huidige sessie.",
"Cross-signing and secret storage are not yet set up.": "Cross-signing en sleutelopslag zijn nog niet ingesteld.",
"Bootstrap cross-signing and secret storage": "Cross-signing en sleutelopslag opzetten",
"Reset cross-signing and secret storage": "Cross-signing en sleutelopslag resetten",
"Cross-signing public keys:": "Cross-signing publieke sleutels:",
"Cross-signing private keys:": "Cross-signing privésleutels:",
"in secret storage": "in de sleutelopslag",
"Secret storage public key:": "Sleutelopslag publieke sleutel:",
"in account data": "in accountinformatie",
"Securely cache encrypted messages locally for them to appear in search results, using ": "Sla versleutelde berichten beveiligd op om ze weer te geven in de zoekresultaten, door gebruik te maken van ",
" to store messages from ": " om berichten op te slaan van ",
"Manage": "Beheren",
"Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Verbind deze sessie met de sleutelback-up voordat u uitlogt. Dit voorkomt dat u sleutels verliest die alleen op deze sessie voorkomen.",
"Connect this session to Key Backup": "Verbind deze sessie met de sleutelback-up",
"Backup has a <validity>valid</validity> signature from this user": "De back-up heeft een <validity>correcte</validity> ondertekening van deze gebruiker",
"Backup has a <validity>invalid</validity> signature from this user": "De back-up heeft een <validity>incorrecte</validity> ondertekening van deze gebruiker",
"Backup has a signature from <verify>unknown</verify> user with ID %(deviceId)s": "De back-up heeft een ondertekening van <verify>onbekende</verify> gebruiker met ID %(deviceId)s",
"Backup has a signature from <verify>unknown</verify> session with ID %(deviceId)s": "De back-up heeft een ondertekening van <verify>onbekende</verify> sessie met ID %(deviceId)s",
"Backup has a <validity>valid</validity> signature from this session": "De back-up heeft een <validity>correcte</validity> ondertekening van deze sessie",
"Backup has an <validity>invalid</validity> signature from this session": "De back-up heeft een <validity>incorrecte</validity> ondertekening van deze sessie",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> session <device></device>": "De back-up heeft een <validity>correcte</validity> ondertekening van <verify>geverifieerde</verify> sessie <device></device>",
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> session <device></device>": "De back-up heeft een <validity>correcte</validity> ondertekening van <verify>ongeverifieerde</verify> sessie <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> session <device></device>": "De back-up heeft een <validity>incorrecte</validity> ondertekening van <verify>geverifieerde</verify> sessie <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> session <device></device>": "De back-up heeft een <validity>incorrecte</validity> ondertekening van <verify>ongeverifieerde</verify> sessie <device></device>",
"Backup is not signed by any of your sessions": "De back-up is door geen van uw sessies ondertekend",
"This backup is trusted because it has been restored on this session": "Deze back-up is vertrouwd omdat het is hersteld naar deze sessie",
"Backup key stored: ": "Back-upsleutel opgeslagen: ",
"Your keys are <b>not being backed up from this session</b>.": "Uw sleutels worden <b>niet geback-upt van deze sessie</b>.",
"Clear notifications": "Notificaties wissen",
"You should <b>remove your personal data</b> from identity server <idserver /> before disconnecting. Unfortunately, identity server <idserver /> is currently offline or cannot be reached.": "U moet uw <b>persoonlijke informatie</b> van de identiteitsserver <idserver /> <b>verwijderen</b> voordat u zich ontkoppeld. Helaas kan de identiteitsserver <idserver /> op dit moment niet worden bereikt. Mogelijk is het offline."
}

View file

@ -21,7 +21,7 @@
"Curve25519 identity key": "Ключ идентификации Curve25519",
"Deactivate Account": "Закрыть аккаунт",
"Decryption error": "Ошибка расшифровки",
"Default": "Участник",
"Default": "По умолчанию",
"Deops user with given id": "Снимает полномочия оператора с пользователя с заданным ID",
"Device ID": "ID устройства",
"Displays action": "Отображение действий",
@ -924,7 +924,7 @@
"Failed to invite users to the room:": "Не удалось пригласить пользователей в комнату:",
"Upgrades a room to a new version": "Модернизирует комнату до новой версии",
"Sets the room name": "Устанавливает название комнаты",
"Forces the current outbound group session in an encrypted room to be discarded": "Принудительно отбрасывает текущий сеанс исходящей группы в зашифрованной комнате",
"Forces the current outbound group session in an encrypted room to be discarded": "Принудительно отбрасывает текущая сессия исходящей группы в зашифрованной комнате",
"%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s модернизировал эту комнату.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s добавил %(addedAddresses)s к списку адресов комнаты.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s добавил %(addedAddresses)s к списку адресов комнаты.",
@ -1576,13 +1576,13 @@
"You are still <b>sharing your personal data</b> on the identity server <idserver />.": "Вы все еще <b> делитесь своими личными данными </b> на сервере идентификации <idserver />.",
"We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Мы рекомендуем вам удалить свои адреса электронной почты и номера телефонов с сервера идентификации перед отключением.",
"Disconnect anyway": "Отключить в любом случае",
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "В настоящее время вы используете <server></server> для обнаружения и быть найденным существующими контактами, которые вы знаете. Вы можете изменить ваш сервер идентификации ниже.",
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "В настоящее время вы используете <server></server> для поиска вами ваших контактов а также вас вашими оппонентами. Вы можете изменить ваш сервер идентификации ниже.",
"If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.": "Если вы не хотите использовать <server /> для обнаружения вас и быть обнаруженным вашими существующими контактами, введите другой идентификационный сервер ниже.",
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Вы в настоящее время не используете идентификационный сервер. Чтобы обнаружить и быть обнаруженным существующими контактами, которых вы знаете, добавьте один ниже.",
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Вы в настоящее время не используете идентификационный сервер. Чтобы найти и быть найденным существующими контактами, которые вы знаете, добавьте один ниже.",
"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.": "Отключение от сервера идентификации будет означать, что другие пользователи не смогут вас обнаружить, и вы не сможете приглашать других по электронной почте или по телефону.",
"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.": "Использование сервера идентификации не обязательно. Если вы решите не использовать сервер идентификации, другие пользователи не смогут обнаружить вас, и вы не сможете пригласить других по электронной почте или телефону.",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Подтвердите условия предоставления услуг сервера идентификации (%(serverName)s), чтобы вас можно было обнаружить по адресу электронной почты или номеру телефона.",
"Discovery": "Обнаружение",
"Discovery": "Поиск",
"Deactivate account": "Деактивировать аккаунт",
"Clear cache and reload": "Очистить кэш и перезагрузить",
"Always show the window menu bar": "Всегда показывать строку меню",
@ -1607,11 +1607,11 @@
"Complete": "Выполнено",
"Revoke": "Отмена",
"Share": "Делиться",
"Discovery options will appear once you have added an email above.": "Параметры обнаружения появятся после добавления электронной почты выше.",
"Discovery options will appear once you have added an email above.": "Параметры поиска по электронной почты появятся после добавления её выше.",
"Unable to revoke sharing for phone number": "Не удалось отменить общий доступ к номеру телефона",
"Unable to share phone number": "Не удается предоставить общий доступ к номеру телефона",
"Please enter verification code sent via text.": "Пожалуйста, введите проверочный код, высланный с помощью текста.",
"Discovery options will appear once you have added a phone number above.": "Параметры обнаружения появятся после добавления вышеуказанного номера телефона.",
"Discovery options will appear once you have added a phone number above.": "Параметры поиска по номеру телефона появятся после его добавления.",
"Remove %(email)s?": "Удалить %(email)s?",
"Remove %(phone)s?": "Удалить %(phone)s?",
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "Текстовое сообщение было отправлено +%(msisdn)s. Пожалуйста, введите проверочный код, который он содержит.",
@ -1715,5 +1715,36 @@
"Error adding ignored user/server": "Ошибка добавления игнорируемого пользователя/сервера",
"Error subscribing to list": "Ошибка при подписке на список",
"Send cross-signing keys to homeserver": "Отправка ключей перекрестной подписи на домашний сервер",
"Error upgrading room": "Ошибка обновления комнаты"
"Error upgrading room": "Ошибка обновления комнаты",
"Match system theme": "Тема системы",
"Show tray icon and minimize window to it on close": "Показать иконку в панели задач и свернуть окно при закрытии",
"The version of Riot": "Версия Riot",
"Show typing notifications": "Показывать уведомления о наборе",
"Delete %(count)s sessions|other": "Удалить %(count)s сессию",
"Enable desktop notifications for this session": "Включить уведомления для рабочего стола для этой сессии",
"Enable audible notifications for this session": "Включить звуковые уведомления для этой сессии",
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Используйте Менеджер интеграциями <b>%(serverName)s</b> для управления ботами, виджетами и стикерами.",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Используйте Менеджер интеграциями для управления ботами, виджетами и стикерами.",
"Manage integrations": "Управление интеграциями",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Менеджеры интеграции получают данные конфигурации и могут изменять виджеты, отправлять приглашения в комнаты и устанавливать уровни доступа от вашего имени.",
"Sessions": "Сессии",
"Direct Messages": "Прямые сообщения",
"%(count)s sessions|other": "%(count)s сессий",
"Hide sessions": "Скрытые сессии",
"Enable 'Manage Integrations' in Settings to do this.": "Включите «Управление интеграциями» в настройках, чтобы сделать это.",
"Unknown sessions": "Неизвестные сессии",
"Help": "Помощь",
"If you cancel now, you won't complete verifying your other session.": "Если вы отмените сейчас, вы не завершите проверку вашей другой сессии.",
"Verify this session": "Проверьте эту сессию",
"Unverified session": "Непроверенная сессия",
"Verifies a user, session, and pubkey tuple": "Проверяет пользователя, сессию и публичные ключи",
"Unknown (user, session) pair:": "Неизвестная (пользователь:сессия) пара:",
"Session already verified!": "Сессия уже подтверждена!",
"Never send encrypted messages to unverified sessions from this session": "Никогда не отправляйте зашифрованные сообщения в непроверенные сессий из этой сессии",
"Never send encrypted messages to unverified sessions in this room from this session": "Никогда не отправляйте зашифрованные сообщения в непроверенные сессии в эту комнату из этой сессии",
"Your keys are <b>not being backed up from this session</b>.": "Ваши ключи <b>не резервируются с этой сессии</b>.",
"Server or user ID to ignore": "ID сервера или пользователя для игнорирования",
"Subscribed lists": "Подписанные списки",
"Subscribe": "Подписать",
"A session's public name is visible to people you communicate with": "Публичное имя сессии видны людям, с которыми вы общаетесь"
}

View file

@ -493,7 +493,7 @@
"Search failed": "Kërkimi shtoi",
"No more results": "Jo më tepër përfundime",
"Room": "Dhomë",
"Clear filter": "Pastroje filtrin",
"Clear filter": "Spastroje filtrin",
"Uploading %(filename)s and %(count)s others|other": "Po ngarkohet %(filename)s dhe %(count)s të tjera",
"Uploading %(filename)s and %(count)s others|zero": "Po ngarkohet %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Po ngarkohet %(filename)s dhe %(count)s tjetër",
@ -741,8 +741,8 @@
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)sndryshoi emrin e vet %(count)s herë",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)sndryshuan avatarët e tyre %(count)s herë",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)sndryshoi avatarin e vet %(count)s herë",
"Clear cache and resync": "Pastro fshehtinën dhe rinjëkohëso",
"Clear Storage and Sign Out": "Pastro Depon dhe Dil",
"Clear cache and resync": "Spastro fshehtinën dhe rinjëkohëso",
"Clear Storage and Sign Out": "Spastro Depon dhe Dil",
"COPY": "KOPJOJE",
"e.g. %(exampleValue)s": "p.sh., %(exampleValue)s",
"e.g. <CurrentPageURL>": "p.sh., <CurrentPageURL>",
@ -883,7 +883,7 @@
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot-i tani përdor 3 deri 5 herë më pak kujtesë, duke ngarkuar të dhëna mbi përdorues të tjerë vetëm kur duhen. Ju lutemi, prisni, teksa njëkohësojmë të dhënat me shërbyesin!",
"Put a link back to the old room at the start of the new room so people can see old messages": "Vendosni në krye të dhomës së re një lidhje për te dhoma e vjetër, që njerëzit të mund të shohin mesazhet e vjetër",
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Nëse më herët keni përdorur një version më të freskët të Riot-it, sesioni juaj mund të jetë i papërputhshëm me këtë version. Mbylleni këtë dritare dhe kthehuni te versioni më i ri.",
"Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Pastrimi i gjërave të depozituara në shfletuesin tuaj mund ta ndreqë problemin, por kjo do të sjellë nxjerrjen tuaj nga llogari dhe do ta bëjë të palexueshëm çfarëdo historiku të fshehtëzuar të bisedës.",
"Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Spastrimi i gjërave të depozituara në shfletuesin tuaj mund ta ndreqë problemin, por kjo do të sjellë nxjerrjen tuaj nga llogari dhe do ta bëjë të palexueshëm çfarëdo historiku të fshehtëzuar të bisedës.",
"If you already have a Matrix account you can <a>log in</a> instead.": "Nëse keni tashmë një llogari Matrix, mund <a>të bëni hyrjen</a>.",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Krijoni një bashkësi që bëni tok përdorues dhe dhoma! Krijoni një faqe hyrëse vetjake, që të ravijëzoni hapësirën tuaj në universin Matrix.",
"Sent messages will be stored until your connection has returned.": "Mesazhet e dërguar do të depozitohen deri sa lidhja juaj të jetë rikthyer.",
@ -1047,7 +1047,7 @@
"Custom user status messages": "Mesazhe vetjakë për gjendje përdoruesi",
"Unable to load commit detail: %(msg)s": "Sarrihet të ngarkohen hollësi depozitimi: %(msg)s",
"Set a new status...": "Caktoni një gjendje të re…",
"Clear status": "Pastroji gjendjen",
"Clear status": "Spastroji gjendjen",
"Unrecognised address": "Adresë jo e pranuar",
"User %(user_id)s may or may not exist": "Përdoruesi %(user_id)s mund të ekzistojë ose jo",
"Waiting for %(userId)s to confirm...": "Po pritet që %(userId)s të bëjë ripohimin…",
@ -1079,7 +1079,7 @@
"Verified!": "U verifikua!",
"You've successfully verified this user.": "E verifikuat me sukses këtë përdorues.",
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Mesazhet e sigurt me këtë përdorues fshehtëzohen skaj-më-skaj dhe të palexueshëm nga palë të treta.",
"Got It": "E kuptova",
"Got It": "E Mora Vesh",
"Verify this user by confirming the following number appears on their screen.": "Verifikojeni këtë përdorues duke ripohuar shfaqjen e numrit vijues në skenën e tyre.",
"Yes": "Po",
"No": "Jo",
@ -1443,7 +1443,7 @@
"Try again later, or ask a room admin to check if you have access.": "Riprovoni më vonë, ose kërkojini një përgjegjësi dhome të kontrollojë nëse keni apo jo hyrje.",
"%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.": "%(errcode)s erdhi teksa provohej të hyhej në dhomë. Nëse mendoni se po e shihni gabimisht këtë mesazh, ju lutemi, <issueLink>parashtroni një njoftim të mete</issueLink>.",
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Përmirësimi i kësaj dhome do të asgjësojë instancën e tanishme të dhomës dhe do të krijojë një dhomë të përmirësuar me të njëjtin emër.",
"This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "Kjo dhomë gjendet nën versionin <roomVersion /> e dhomës, të cilin shërbyesi e ka shënuar si <i>të paqëndrueshëm</i>.",
"This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "Kjo dhomë gjendet nën versionin <roomVersion /> e dhomës, të cilit shërbyesi Home i ka vënë shenjë si <i>i paqëndrueshëm</i>.",
"Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Su shfuqizua dot ftesa. Shërbyesi mund të jetë duke kaluar një problem të përkohshëm ose skeni leje të mjaftueshme për të shfuqizuar ftesën.",
"<reactors/><reactedWith>reacted with %(shortName)s</reactedWith>": "<reactors/><reactedWith>reagoi me %(shortName)s</reactedWith>",
"If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "Nëse ka kontekst shtesë që mund të ndihmonte në analizimin e problemit, b.f., çpo bënit në atë kohë, ID dhomash, ID përdorueusish, etj, ju lutemi, përfshijini këto gjëra këtu.",
@ -1659,7 +1659,7 @@
"check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "të kontrolloni shtojcat e shfletuesit tuaj për çfarëdo që mund të bllokojë shërbyesin e identiteteve (bie fjala, Privacy Badger)",
"contact the administrators of identity server <idserver />": "të lidheni me përgjegjësit e shërbyesit të identiteteve <idserver />",
"wait and try again later": "të prisni dhe të riprovoni më vonë",
"Clear cache and reload": "Të pastroni fshehtinën dhe të ringarkoni",
"Clear cache and reload": "Spastro fshehtinën dhe ringarko",
"Your email address hasn't been verified yet": "Adresa juaj email sështë verifikuar ende",
"Click the link in the email you received to verify and then click continue again.": "Për verifkim, klikoni lidhjen te email që morët dhe mandej vazhdoni sërish.",
"You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|one": "Ju ndan një hap nga heqja e 1 mesazhi prej %(user)s. Kjo smund të zhbëhet. Doni të vazhdohet?",
@ -1961,5 +1961,190 @@
"Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot-i po ruan lokalisht në mënyrë të sigurt në fshehtinë mesazhet që të shfaqen në përfundime kërkimi:",
"Space used:": "Hapësirë e përdorur:",
"Indexed messages:": "Mesazhe të indeksuar:",
"Number of rooms:": "Numër dhomash:"
"Number of rooms:": "Numër dhomash:",
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Në këtë dhomë ka sesione të panjohur: nëse vazhdoni pa i verifikuar ata, për dikë do të jetë e mundur të përgjojë thirrjen tuaj.",
"If you cancel now, you won't complete verifying the other user.": "Nëse e anuloni tani, sdo të plotësoni verifikimin e përdoruesit tjetër.",
"If you cancel now, you won't complete verifying your other session.": "Nëse e anuloni tani, sdo të plotësoni verifikimin e sesionit tuaj tjetër.",
"If you cancel now, you won't complete your secret storage operation.": "Nëse e anuloni tani, sdo të plotësoni veprimin tuaj për depozitë të fshehtë.",
"Cancel entering passphrase?": "Të anulohet dhënue frazëkalimi?",
"Setting up keys": "Ujdisje kyçesh",
"Unverified session": "Sesion i paverifikuar",
"Verifies a user, session, and pubkey tuple": "Verifikon një përdorues, sesion dhe një set kyçesh publikë",
"Unknown (user, session) pair:": "Çift (përdorues, sesion) i panjohur:",
"Session already verified!": "Sesion i tashmë i verifikuar!",
"WARNING: Session already verified, but keys do NOT MATCH!": "KUJDES: Sesion tashmë i verifikuar, por kyçet NUK PËRPUTHEN!",
"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!": "KUJDES: VERIFIKIMI I KYÇIT DËSHTOI! Kyçi i nënshkrimit për %(userId)s dhe sesionin %(deviceId)s është \"%(fprint)s\", që nuk përputhet me kyçin e dhënë \"%(fingerprint)s\". Kjo mund të jetë shenjë se komunikimet tuaja po përgjohen!",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Kyçi i nënshkrimit që dhatë përputhet me kyçin e nënshkrimit që morët nga sesioni i %(userId)s %(deviceId)s. Sesionit iu vu shenjë si i verifikuar.",
"Enable cross-signing to verify per-user instead of per-session (in development)": "Aktivizoni <em>cross-signing</em> për të verifikuar me bazë përdorues në vend se me bazë sesioni (në zhvillim)",
"Show padlocks on invite only rooms": "Për dhoma vetëm me ftesa shfaq dryna",
"Never send encrypted messages to unverified sessions from this session": "Mos dërgo kurrë prej këtij sesioni mesazhe të fshehtëzuar te sesione të paverifikuar",
"Never send encrypted messages to unverified sessions in this room from this session": "Mos dërgo kurrë prej këtij sesioni mesazhe të fshehtëzuar te sesione të paverifikuar në këtë dhomë",
"Keep secret storage passphrase in memory for this session": "Për këtë sesion, frazëkalimin e depozitës së fshehtë mbaje në kujtesë",
"How fast should messages be downloaded.": "Sa shpejt duhen shkarkuar mesazhet.",
"Confirm the emoji below are displayed on both devices, in the same order:": "Ripohoni që emoji-t më poshtë shfaqen në të dyja pajisjet, në të njëjtën radhë:",
"Verify this device by confirming the following number appears on its screen.": "Verifikojeni këtë pajisje duke ripohuar shfaqjen e numrit vijues në skenën e tij.",
"Waiting for %(displayName)s to verify…": "Po pritet për %(displayName)s të verifikojë…",
"They match": "Përputhen",
"They don't match": "Spërputhen",
"To be secure, do this in person or use a trusted way to communicate.": "Për të qenë i sigurt, bëjeni këtë duke qenë vetë i pranishëm ose përdorni për të komunikuar një rrugë të besuar.",
"Verify yourself & others to keep your chats safe": "Verifikoni veten & të tjerët, që ti mbani bisedat tuaja të sigurta",
"This bridge was provisioned by <user />.": "Kjo urë është dhënë nga <user />.",
"Workspace: %(networkName)s": "Hapësirë pune: %(networkName)s",
"Channel: %(channelName)s": "Kanal: %(channelName)s",
"Show less": "Shfaq më pak",
"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.": "Ndryshimi i fjalëkalimit do të sjellë zerimin e çfarëdo kyçesh fshehtëzimi skaj-më-skaj në krejt sesionet, duke e bërë të palexueshëm historikun e fshehtëzuar të bisedave, hiq rastin kur i eksportoni më parë kyçet tuaj të dhomës dhe i ri-importoni ata më pas. Në të ardhmen kjo do të përmirësohet.",
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Llogaria juaj ka një identitet <em>cross-signing</em> në depozitë të fshehtë, por sështë ende i besuar në këtë sesion.",
"in memory": "në kujtesë",
"Your homeserver does not support session management.": "Shërbyesi juaj Home nuk mbulon administrim sesionesh.",
"Unable to load session list": "Sarrihet të ngarkohet listë sesionesh",
"Delete %(count)s sessions|other": "Fshi %(count)s sesione",
"Delete %(count)s sessions|one": "Fshi %(count)s sesion",
"This session is backing up your keys. ": "Kjo sesion po bën kopjeruajtje të kyçeve tuaja. ",
"This session is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.": "Ky sesion <b>nuk po bën kopjeruajtje të kyçeve tuaja</b>, por keni një kopjeruajtje ekzistuese që mund ta përdorni për rimarrje dhe ta shtoni më tej.",
"Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Lidheni këtë sesion kopjeruajtje kyçesh, përpara se të dilni, që të shmangni humbje të çfarëdo kyçi që mund të gjendet vetëm në këtë pajisje.",
"Connect this session to Key Backup": "Lidhe këtë sesion me Kopjeruajtje Kyçesh",
"Backup has a signature from <verify>unknown</verify> session with ID %(deviceId)s": "Kopjeruajtja ka nënshkrim nga sesion <verify>i panjohur</verify> me ID %(deviceId)s.",
"Backup has a <validity>valid</validity> signature from this session": "Kopjeruajtja ka një nënshkrim të <validity>vlefshëm</validity> prej këtij sesioni",
"Backup has an <validity>invalid</validity> signature from this session": "Kopjeruajtja ka një nënshkrim të <validity>pavlefshëm</validity> prej këtij sesioni",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> session <device></device>": "Kopjeruajtja ka një nënshkrim të <validity>vlefshëm</validity> prej sesioni të <verify>verifikuar</verify> <device></device>",
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> session <device></device>": "Kopjeruajtja ka një nënshkrim të <validity>vlefshëm</validity> prej sesioni <verify>paverifikuar</verify> <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> session <device></device>": "Kopjeruajtja ka një nënshkrim të <validity>pavlefshëm</validity> prej sesioni të <verify>verifikuar</verify> <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> session <device></device>": "Kopjeruajtja ka një nënshkrim të <validity>pavlefshëm</validity> prej sesioni të <verify>paverifikuar</verify> <device></device>",
"Backup is not signed by any of your sessions": "Kopjeruajtja sështë nënshkruar nga ndonjë prej sesioneve tuaj",
"This backup is trusted because it has been restored on this session": "Kjo kopjeruajtje është e besuar, ngaqë është rikthyer në këtë sesion",
"Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Kyçi i kopjeruajtjeve u depozitua në depozitë të fshehtë, po kjo veçori sështë e aktivizuar në këtë sesion. Ju lutemi, aktivizoni në Labs <em>cross-signing</em> që të modifikoni gjendje kopjeruatjeje kyçesh.",
"Your keys are <b>not being backed up from this session</b>.": "Kyçet tuaj <b>nuk po kopjeruhen nga ky sesion</b>.",
"Enable desktop notifications for this session": "Aktivizo njoftime desktop për këtë sesion",
"Enable audible notifications for this session": "Aktivizo njoftime audio për këtë sesion",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Fjalëkalimi juaj u ndryshua me sukses. Nuk do të merrni njoftime push në sesionet tuaj të tjerë, veç në hyfshi sërish në llogarinë tuaj në to",
"Session ID:": "ID sesioni:",
"Session key:": "Kyç sesioni:",
"Sessions": "Sesione",
"A session's public name is visible to people you communicate with": "Emri publik i një sesioni është i dukshëm për persona me të cilët komunikoni",
"This room is bridging messages to the following platforms. <a>Learn more.</a>": "Kjo dhomë po kalon mesazhe përmes ure te platformat vijuese. <a>Mësoni më tepër.</a>",
"This room isnt bridging messages to any platforms. <a>Learn more.</a>": "Kjo dhomë skalon mesazhe përmes ure te ndonjë platformë. <a>Mësoni më tepër.</a>",
"Bridges": "Ura",
"This user has not verified all of their sessions.": "Ky përdorues ska verifikuar krejt sesionet e tij.",
"You have not verified this user.": "Se keni verifikuar këtë përdorues.",
"You have verified this user. This user has verified all of their sessions.": "E keni verifikuar këtë përdorues. Ky përdorues ka verifikuar krejt sesionet e veta.",
"Someone is using an unknown session": "Dikush po përdor një sesion të panjohur",
"Some sessions for this user are not trusted": "Disa sesione për këtë përdorues sjanë të besuar",
"All sessions for this user are trusted": "Krejt sesionet për këtë përdorues janë të besuar",
"Some sessions in this encrypted room are not trusted": "Disa sesione në këtë dhomë të fshehtëzuar sjanë të besuar",
"All sessions in this encrypted room are trusted": "Krejt sesionet në këtë dhomë të fshehtëzuar janë të besuar",
"Mod": "Moderator",
"Your key share request has been sent - please check your other sessions for key share requests.": "Kërkesa juaj për shkëmbim kyçesh u dërgua - ju lutemi, kontrolloni sesionet tuaj të tjerë për kërkesa shkëmbimi kyçesh.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Kërkesat për ndarje kyçesh dërgohen automatikisht te sesionet tuaj të tjerë. Nëse se pranuat ose e hodhët tej kërkesën për ndarje kyçesh në sesionet tuaj të tjerë, klikoni këtu që të rikërkoni kyçe për këtë sesion.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Nëse sesionet tuaj të tjerë nuk kanë kyçin për këtë mesazh, sdo të jeni në gjendje ta shfshehtëzoni.",
"<requestLink>Re-request encryption keys</requestLink> from your other sessions.": "<requestLink>Rikërkoni kyçe fshehtëzimi</requestLink> prej sesionesh tuaj të tjerë.",
"Encrypted by an unverified session": "Fshehtëzuar nga një sesion i paverifikuar",
"Encrypted by a deleted session": "Fshehtëzuar nga një sesion i fshirë",
"No sessions with registered encryption keys": "Ska sesion me kyçe fshehtëzimi të regjistruar",
"Waiting for %(displayName)s to accept…": "Po pritet për %(displayName)s të pranojë…",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Mesazhet tuaj janë të sigurt dhe vetëm ju dhe marrësi kanë kyçet unikë për ti shkyçur.",
"Your messages are not secure": "Mesazhet tuaj sjanë të sigurt",
"One of the following may be compromised:": "Një nga sa vijon mund të jetë komprometuar:",
"Your homeserver": "Shërbyesi juaj Home",
"The homeserver the user youre verifying is connected to": "Shërbyesi Home te i cili është lidhur përdoruesi që po verifikoni",
"Yours, or the other users internet connection": "Lidhja internet e juaja, ose e përdoruesve të tjerë",
"Yours, or the other users session": "Sesioni juaj, ose i përdoruesve të tjerë",
"%(count)s sessions|other": "%(count)s sesione",
"%(count)s sessions|one": "%(count)s sesion",
"Hide sessions": "Fshih sesione",
"Verify by emoji": "Verifikoje përmes emoji-t",
"Verify by comparing unique emoji.": "Verifikoje duke krahasuar emoji unik.",
"Ask %(displayName)s to scan your code:": "Kërkojini %(displayName)s të skanojë kodin tuaj:",
"If you can't scan the code above, verify by comparing unique emoji.": "Nëse se skanoni dot kodin më sipër, verifikojeni duke krahasuar emoji unik.",
"You've successfully verified %(displayName)s!": "E verifikuat me sukses %(displayName)s!",
"Got it": "E mora vesh",
"Verification timed out. Start verification again from their profile.": "Verifikimit i mbaroi koha. Riniseni verifikimin prej profilit të tij.",
"%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s e anuloi verifikimin. Rinisni verifkimin nga profili i tij.",
"You cancelled verification. Start verification again from their profile.": "Anuluat verifikimin. Riniseni verifikimin nga profili i tij.",
"Encryption enabled": "Fshehtëzim i aktivizuar",
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Mesazhet në këtë dhomë fshehtëzohen skaj-më-skaj. Mësoni më tepër & verifikoni këtë përdorues në profilin e tij.",
"Encryption not enabled": "Fshehtëzim jo i aktivizuar",
"The encryption used by this room isn't supported.": "Fshehtëzimi i përdorur nga kjo dhomë nuk mbulohet.",
"Clear all data in this session?": "Të pastrohen krejt të dhënat në këtë sesion?",
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Spastrimi i krejt të dhënave prej këtij sesioni është përfundimtar. Mesazhet e fshehtëzuar do të humbin, veç në qofshin kopjeruajtur kyçet e tyre.",
"Verify session": "Verifiko sesion",
"To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Që të verifikohet se ky sesion mund të besohet, ju lutemi, kontrolloni se kyçi që shihni te Rregullime Përdoruesi në atë pajisje të përputhet me kyçin më poshtë:",
"To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Që të verifikoni se këtij sesioni mund ti zihet besë, ju lutemi, lidhuni me të zotët e saj përmes ndonjë rruge tjetër (p.sh., personalisht, ose përmes një thirrjeje telefonike) dhe pyetini nëse përputhet apo jo kyçi që shohin te Rregullime të tyret të Përdoruesit për këtë pajisje me kyçin më poshtë:",
"Session name": "Emër sesioni",
"Session key": "Kyç sesioni",
"If it matches, press the verify button below. If it doesn't, then someone else is intercepting this session and you probably want to press the blacklist button instead.": "Nëse përputhet, shtypni butonin e verifikimit më poshtë. Nëse jo, atëherë dikush tjetër po e përgjon këtë sesion dhe gjasat janë që të doni të shtypni butonin e kalimit në listë bllokimesh.",
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Verifikimi i këtij përdoruesi do ti vërë shenjë sesionit të tij si të besuar dhe sesionit tuaj si të besuar për ta.",
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Që ti vihet shenjë si e besuar, verifikojeni këtë pajisje. Besimi i kësaj pajisjeje ju jep juve dhe përdoruesve të tjerë ca qetësi më tepër, kur përdoren mesazhe të fshehtëzuar skaj-më-skaj.",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Verifikimi i kësaj pajisjeje do të ti vërë shenjë si të besuar dhe përdoruesit që janë verifikuar me ju do ta besojnë këtë pajisje.",
"You added a new session '%(displayName)s', which is requesting encryption keys.": "Shtuat një sesion të ri '%(displayName)s', i cili po kërkon kyçe fshehtëzimi.",
"Your unverified session '%(displayName)s' is requesting encryption keys.": "Sesioni juaj i paverifikuar '%(displayName)s' po kërkon kyçe fshehtëzimi.",
"Loading session info...": "Po ngarkohen të dhëna sesioni…",
"New session": "Sesion i ri",
"Use this session to verify your new one, granting it access to encrypted messages:": "Përdoreni këtë sesion për të verifikuar atë tuajin të ri, duke i akorduar hyrje te mesazhe të fshehtëzuar:",
"If you didnt sign in to this session, your account may be compromised.": "Nëse sbëtë hyrjen te ky sesion, llogaria muaj mund të jetë komprometuar.",
"This wasn't me": "Ky sjam unë",
"This will allow you to return to your account after signing out, and sign in on other sessions.": "Kjo do tju lejojë të riktheheni te llogaria juaj pasi të keni bërë daljen, dhe të hyni në sesione të tjerë.",
"You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "Po kaloni në listë të bllokimesh sesione të paverifikuar; që të dërgoni mesazhe te këta sesione, duhet ti verifikoni.",
"We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Këshillojmë të përshkoni procesin e verifikimit për çdo sesion, për tu bindur se u takojnë të zotëve të ligjshëm, por, nëse parapëlqeni, mund ta dërgoni mesazhin pa verifikuar gjë.",
"Room contains unknown sessions": "Dhoma përmban sesione të panjohur",
"\"%(RoomName)s\" contains sessions that you haven't seen before.": "\"%(RoomName)s\" përmban sesione që si keni parë më parë.",
"Unknown sessions": "Sesione të panjohur",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase.": "Për verifikim sesionesh të tjerë përmes dhënies së frazëkalimit tuaj, hyni te historiku i mesazheve tuaj të sigurt dhe identiteti juaj për <em>cross-signing</em>.",
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Për verifikim sesionesh të tjerë përmes dhënies së kyçit tuaj të rimarrjes, hyni te historiku i mesazheve tuaj të sigurt dhe identiteti juaj për <em>cross-signing</em>.",
"Recovery key mismatch": "Mospërputhje kyçesh rimarrjeje",
"Incorrect recovery passphrase": "Frazëkalim rimarrjeje i pasaktë",
"Backup restored": "Kopjeruajtja u rikthye",
"Enter recovery passphrase": "Jepni frazëkalim rimarrjesh",
"Enter recovery key": "Jepni kyç rimarrjesh",
"Confirm your identity by entering your account password below.": "Ripohoni identitetin tuaj duke dhënë më poshtë fjalëkalimin e llogarisë tuaj.",
"Message not sent due to unknown sessions being present": "Mesazhi su dërgua, për shkak të pranisë së sesioneve të panjohur",
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showSessionsText>Shfaq sesione</showSessionsText>, <sendAnywayText>dërgoje sido qoftë</sendAnywayText> ose <cancelText>anuloje</cancelText>.",
"Your new session is now verified. Other users will see it as trusted.": "Sesioni juaj i ri tani është i verifikuar. Përdoruesit e tjerë do të shohin si të besuar.",
"Without completing security on this session, it wont have access to encrypted messages.": "Pa plotësuar sigurinë në këtë sesion, sdo të ketë hyrje te mesazhe të fshehtëzuar.",
"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.": "Ndryshimi i fjalëkalimit tuaj do të sjellë zerim të çfarëdo kyçesh fshehtëzimi skaj-më-skaj në krejt sesionet tuaj, duke e bërë të palexueshëm historikun e bisedave të fshehtëzuara. Ujdisni një Kopjeruajtje Kyçesh ose eksportoni kyçet e dhomës tuaj prej një tjetër sesioni, përpara se të ricaktoni fjalëkalimin tuaj.",
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jeni nxjerrë jashtë krejt sesioneve dhe nuk do të merrni më njoftime push. Që të riaktivizoni njoftimet, bëni sërish hyrjen në çdo pajisje.",
"Regain access to your account and recover encryption keys stored in this session. Without them, you wont be able to read all of your secure messages in any session.": "Rifitoni hyrjen te llogaria juaj dhe rimerrni kyçe fshehtëzimi të depozituar në këtë sesion. Pa ta, sdo të jeni në gjendje të lexoni krejt mesazhet tuaj të siguruar në çfarëdo sesion.",
"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.": "Kujdes: Të dhënat tuaja personale (përfshi kyçe fshehtëzimi) janë ende të depozituara në këtë sesion. Spastrojini, nëse keni përfunduar së përdoruri këtë sesion, ose dëshironi të bëni hyrjen në një tjetër llogari.",
"Sender session information": "Të dhëna sesioni dërguesi",
"Restore your key backup to upgrade your encryption": "Që të përmirësoni fshehtëzimin tuaj, riktheni kopjeruajtjen e kyçeve tuaj",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Përmirësojeni këtë sesion për ta lejuar të verifikojë sesione të tjerë, duke u akorduar hyrje te mesazhe të fshehtëzuar dhe duke u vënë shenjë si të besuar për përdorues të tjerë.",
"Set up encryption on this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Ujdisni fshehtëzim në këtë sesion që ta lejoni të verifikojë sesione të tjerë, duke u akorduar atyre hyrje te mesazhe të fshehtëzuar dhe duke u vënë shenjë si të besuar për përdorues të tjerë.",
"Back up my encryption keys, securing them with the same passphrase": "Kopjeruaj kyçet e mi të fshehtëzimit, duke i siguruar me të njëjtin frazëkalim",
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Mbajeni kyçin tuaj të rimarrjeve diku në një vend pak a shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh ose madje në një kasafortë.",
"Your recovery key": "Kyçi juaj i rimarrjeve",
"Copy": "Kopjoje",
"You can now verify your other devices, and other users to keep your chats safe.": "Tani mund të verifikoni pajisje tuajat të tjera dhe përdorues të tjerë, për ti mbajtur të sigurta bisedat tuaja.",
"Make a copy of your recovery key": "Bëni një kopje të kyçit tuaj të rimarrjeve",
"You're done!": "Mbaruat!",
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Pa ujdisur Rimarrje të Sigurt Mesazhesh, sdo të jeni në gjendje të riktheni historikun e mesazheve tuaj të fshehtëzuar, nëse bëni daljen ose përdorni një sesion tjetër.",
"Create key backup": "Krijo kopjeruajtje kyçesh",
"This session is encrypting history using the new recovery method.": "Ky sesion e fshehtëzon historikun duke përdorur metodë të re rimarrjesh.",
"This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Ky sesion ka pikasur se frazëkalimi dhe kyçi juaj i rimarrjeve për Mesazhe të Sigurt janë hequr.",
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Nëse këtë e keni bërë pa dashje, mund të ujdisni Mesazhe të Sigurt në këtë sesion, gjë që do të sjellë rifshehtëzimin e historikut të mesazheve të sesionit me një metodë të re rimarrjesh.",
"Indexed rooms:": "Dhoma të indeksuara:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(crawlingRooms)s nga %(totalRooms)s gjithsej",
"Message downloading sleep time(ms)": "Kohë fjetjeje shkarkimi mesazhi(ms)",
"Show typing notifications": "Shfaq njoftime shtypjeje",
"Destroy cross-signing keys?": "Të shkatërrohen kyçet <em>cross-signing</em>?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Fshirja e kyçeve <em>cross-signing</em> është e përhershme. Cilido që keni verifikuar me to, do të shohë një sinjalizim sigurie. Thuajse e sigurt që skeni pse ta bëni një gjë të tillë, veç në paçi humbur çdo pajisje prej nga mund të bëni <em>cross-sign</em>.",
"Clear cross-signing keys": "Spastro kyçe <em>cross-signing</em>",
"Verify this session by completing one of the following:": "Verifikoni këtë sesion duke plotësuar një nga sa vijon:",
"Scan this unique code": "Skanoni këtë kod unik",
"or": "ose",
"Compare unique emoji": "Krahasoni emoji unik",
"Compare a unique set of emoji if you don't have a camera on either device": "Krahasoni një grup unik emoji-sh, nëse skeni kamera në njërën nga pajisjet",
"Not Trusted": "Jo e Besuar",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) bëri hyrjen në një sesion të ri pa e verifikuar:",
"Ask this user to verify their session, or manually verify it below.": "Kërkojini këtij përdoruesi të verifikojë sesionin e vet, ose ta verifikojë më poshtë dorazi.",
"Manually Verify": "Verifikoje Dorazi",
"Verify by scanning": "Verifikoje me skanim",
"The version of Riot": "Versioni i Riot-it",
"Whether you're using Riot on a device where touch is the primary input mechanism": "Nëse po e përdorni Riot-in në një pajisje ku touch-i është mekanizmi parësor për input-e",
"Whether you're using Riot as an installed Progressive Web App": "Nëse po e përdorni Riot-in të instaluar si një Aplikacion Web Progresiv",
"Your user agent": "Agjenti juaj i përdoruesit",
"The information being sent to us to help make Riot better includes:": "Te të dhënat e dërguara te ne për të na ndihmuar ta bëjmë Riot-in më të mirë përfshihen:",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "Sesioni që po provoni të verifikoni, nuk mbulon skanim kodesh QR apo verifikim emoji-sh, çka janë ato që Riot-i mbulon. Provoni me një tjetër klient.",
"You declined": "Hodhët poshtë",
"%(name)s declined": "%(name)s hodhi poshtë",
"accepting …": "po pranohet …",
"declining …": "po hidhet poshtë …"
}

View file

@ -1496,5 +1496,59 @@
"Set up Secure Messages": "Güvenli Mesajları Ayarla",
"Space used:": "Kullanılan alan:",
"Indexed messages:": "İndekslenmiş mesajlar:",
"Number of rooms:": "Oda sayısı:"
"Number of rooms:": "Oda sayısı:",
"Enable inline URL previews by default": "Varsayılan olarak satır içi URL önizlemeleri aç",
"Waiting for %(displayName)s to verify…": "%(displayName)s ın doğrulaması için bekleniyor…",
"They match": "Eşleşiyorlar",
"They don't match": "Eşleşmiyorlar",
"Verify yourself & others to keep your chats safe": "Sohbetlerinizi güvenli tutmak için kendinizi & diğerlerini doğrulayın",
"Other users may not trust it": "Diğer kullanıcılar güvenmeyebilirler",
"Later": "Sonra",
"Review": "Gözden Geçirme",
"Workspace: %(networkName)s": "Çalışma alanı: %(networkName)s",
"Channel: %(channelName)s": "Kanal: %(channelName)s",
"Show less": "Daha az göster",
"Show more": "Daha fazla göster",
"in memory": "hafızada",
"in secret storage": "sır deposunda",
"Secret storage public key:": "Sır deposu açık anahtarı:",
"Your homeserver does not support session management.": "Anasunucunuz oturum yönetimini desteklemiyor.",
"Unable to load session list": "Oturum listesi yüklenemedi",
"Delete %(count)s sessions|other": "%(count)s oturumu sil",
"Delete %(count)s sessions|one": "%(count)s oturum sil",
"Public Name": "Açık İsim",
"rooms.": "odalar.",
"Manage": "Yönet",
"Enable": "Aç",
"The integration manager is offline or it cannot reach your homeserver.": "Entegrasyon yöneticisi çevrim dışı veya anasunucunuza erişemiyor.",
"Connect this session to Key Backup": "Anahtar Yedekleme için bu oturuma bağlanın",
"Backup is not signed by any of your sessions": "Yedek hiç bir oturumunuz tarafından imzalanmadı",
"This backup is trusted because it has been restored on this session": "Bu yedek güvenilir çünkü bu oturumda geri döndürüldü",
"Backup key stored: ": "Yedek anahtarı depolandı: ",
"Enable desktop notifications for this session": "Bu oturum için masaüstü bildirimlerini aç",
"<a>Upgrade</a> to your own domain": "Kendi etkinlik alanınızı <a>yükseltin</a>",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Botları, görsel bileşenleri ve çıkartma paketlerini yönetmek için bir entegrasyon yöneticisi kullanın.",
"Session ID:": "Oturum ID:",
"Session key:": "Oturum anahtarı:",
"Sessions": "Oturumlar",
"This user has not verified all of their sessions.": "Bu kullanıcı bütün oturumlarında doğrulanmamış.",
"You have not verified this user.": "Bu kullanıcıyı doğrulamadınız.",
"Someone is using an unknown session": "Birisi bilinmeyen bir oturum kullanıyor",
"Everyone in this room is verified": "Bu odadaki herkes doğrulanmış",
"Some sessions for this user are not trusted": "Bu kullanıcı için bazı oturumlar güvenilir değil",
"All sessions for this user are trusted": "Bu kullanıcı için tüm oturumlar güvenilir",
"The version of Riot": "Riot sürümü",
"Your user agent": "Kullanıcı ajanınız",
"If you cancel now, you won't complete verifying the other user.": "Şimdi iptal ederseniz, diğer kullanıcıyı doğrulamayı tamamlamış olmayacaksınız.",
"If you cancel now, you won't complete verifying your other session.": "Şimdi iptal ederseniz, diğer oturumu doğrulamış olmayacaksınız.",
"Setting up keys": "Anahtarları ayarla",
"Custom (%(level)s)": "Özel (%(level)s)",
"Room contains unknown sessions": "Oda bilinmeyen oturumlar içeriyor",
"Unknown sessions": "Bilinmeyen oturumlar",
"Upload %(count)s other files|other": "%(count)s diğer dosyaları yükle",
"Upload %(count)s other files|one": "%(count)s dosyayı sağla",
"A widget would like to verify your identity": "Bir görsel tasarım kimliğinizi teyit etmek istiyor",
"Remember my selection for this widget": "Bu görsel bileşen işin seçimimi hatırla",
"Deny": "Reddet",
"Recovery key mismatch": "Kurtarma anahtarı uyumsuz"
}

View file

@ -2126,5 +2126,36 @@
"You're done!": "您已經完成了!",
"Create key backup": "建立金鑰備份",
"Message downloading sleep time(ms)": "訊息下載休眠時間(毫秒)",
"of ": "的 "
"of ": "的 ",
"Indexed rooms:": "已索引的聊天室:",
"%(crawlingRooms)s out of %(totalRooms)s": "%(totalRooms)s 中的 %(crawlingRooms)s",
"If you cancel now, you won't complete verifying the other user.": "如果您現在取消,您將無法完成驗證其他使用者。",
"If you cancel now, you won't complete verifying your other session.": "如果您現在取消,您將無法完成驗證您其他的工作階段。",
"If you cancel now, you won't complete your secret storage operation.": "如果您現在取消,您將無法完成驗證您的秘密儲存空間動作。",
"Cancel entering passphrase?": "取消輸入通關密語?",
"Show typing notifications": "顯示打字通知",
"Reset cross-signing and secret storage": "重設交叉簽章與秘密儲存空間",
"Destroy cross-signing keys?": "摧毀交叉簽章金鑰?",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "永久刪除交叉簽章金鑰。任何您已驗證過的人都會看到安全性警告。除非您遺失了所有可以進行交叉簽章的裝置,否則您平常幾乎不會想要這樣做。",
"Clear cross-signing keys": "清除交叉簽章金鑰",
"Verify this session by completing one of the following:": "透過完成以下任一種動作來驗證此工作階段:",
"Scan this unique code": "掃描此獨一無二的條碼",
"or": "或",
"Compare unique emoji": "比較獨一無二的顏文字",
"Compare a unique set of emoji if you don't have a camera on either device": "如果兩個裝置上都沒有相機的話,就比較一組獨一無二的顏文字",
"Not Trusted": "未受信任",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) 登入到未驗證的新工作階段:",
"Ask this user to verify their session, or manually verify it below.": "要求此使用者驗證他們的工作階段,或在下方手動驗證。",
"Manually Verify": "手動驗證",
"Verify by scanning": "透過掃描來驗證",
"The version of Riot": "Riot 版本",
"Whether you're using Riot on a device where touch is the primary input mechanism": "您是否在以觸控為主要機制的裝置上使用 Riot",
"Whether you're using Riot as an installed Progressive Web App": "您是否使用 PWA 形式的 Riot",
"Your user agent": "您的使用者代理字串",
"The information being sent to us to help make Riot better includes:": "傳送給我們以協助改進 Riot 的資訊包含了:",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "您嘗試驗證的工作階段不支援 Riot 支援的掃描 QR code 或顏文字驗證。請用其他客戶端試試看。",
"You declined": "您拒絕了",
"%(name)s declined": "%(name)s 拒絕了",
"accepting …": "正在接受……",
"declining …": "正在拒絕……"
}

Some files were not shown because too many files have changed in this diff Show more