Merge branch 'develop' into travis/well-known
This commit is contained in:
commit
6f0f930e0a
140 changed files with 4922 additions and 683 deletions
|
@ -22,7 +22,6 @@ import _clamp from 'lodash/clamp';
|
|||
type MessageFormat = 'rich' | 'markdown';
|
||||
|
||||
class HistoryItem {
|
||||
|
||||
// We store history items in their native format to ensure history is accurate
|
||||
// and then convert them if our RTE has subsequently changed format.
|
||||
value: Value;
|
||||
|
|
|
@ -78,7 +78,6 @@ class MemberEntity extends Entity {
|
|||
}
|
||||
|
||||
class UserEntity extends Entity {
|
||||
|
||||
constructor(model, showInviteButton, inviteFn) {
|
||||
super(model);
|
||||
this.showInviteButton = Boolean(showInviteButton);
|
||||
|
|
|
@ -64,7 +64,7 @@ export function containsEmoji(str) {
|
|||
* because we want to include emoji shortnames in title text
|
||||
*/
|
||||
function unicodeToImage(str) {
|
||||
let replaceWith, unicode, alt, short, fname;
|
||||
let replaceWith; let unicode; let alt; let short; let fname;
|
||||
const mappedUnicode = emojione.mapUnicodeToShort();
|
||||
|
||||
str = str.replace(emojione.regUnicode, function(unicodeChar) {
|
||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import Matrix from "matrix-js-sdk";
|
||||
import { _t } from "./languageHandler";
|
||||
|
||||
import Promise from 'bluebird';
|
||||
import url from 'url';
|
||||
|
@ -225,19 +224,18 @@ export default class Login {
|
|||
});
|
||||
}
|
||||
|
||||
redirectToCas() {
|
||||
getSsoLoginUrl(loginType) {
|
||||
const client = this._createTemporaryClient();
|
||||
const parsedUrl = url.parse(window.location.href, true);
|
||||
|
||||
// XXX: at this point, the fragment will always be #/login, which is no
|
||||
// use to anyone. Ideally, we would get the intended fragment from
|
||||
// MatrixChat.screenAfterLogin so that you could follow #/room links etc
|
||||
// through a CAS login.
|
||||
// through an SSO login.
|
||||
parsedUrl.hash = "";
|
||||
|
||||
parsedUrl.query["homeserver"] = client.getHomeserverUrl();
|
||||
parsedUrl.query["identityServer"] = client.getIdentityServerUrl();
|
||||
const casUrl = client.getCasLoginUrl(url.format(parsedUrl));
|
||||
window.location.href = casUrl;
|
||||
return client.getSsoLoginUrl(url.format(parsedUrl), loginType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,12 @@ import Matrix from 'matrix-js-sdk';
|
|||
import utils from 'matrix-js-sdk/lib/utils';
|
||||
import EventTimeline from 'matrix-js-sdk/lib/models/event-timeline';
|
||||
import EventTimelineSet from 'matrix-js-sdk/lib/models/event-timeline-set';
|
||||
import sdk from './index';
|
||||
import createMatrixClient from './utils/createMatrixClient';
|
||||
import SettingsStore from './settings/SettingsStore';
|
||||
import MatrixActionCreators from './actions/MatrixActionCreators';
|
||||
import {phasedRollOutExpiredForUser} from "./PhasedRollOut";
|
||||
import Modal from './Modal';
|
||||
|
||||
interface MatrixClientCreds {
|
||||
homeserverUrl: string,
|
||||
|
@ -116,6 +118,14 @@ class MatrixClientPeg {
|
|||
await this.matrixClient.initCrypto();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.name === 'InvalidCryptoStoreError') {
|
||||
// The js-sdk found a crypto DB too new for it to use
|
||||
const CryptoStoreTooNewDialog =
|
||||
sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog");
|
||||
Modal.createDialog(CryptoStoreTooNewDialog, {
|
||||
host: window.location.host,
|
||||
});
|
||||
}
|
||||
// this can happen for a number of reasons, the most likely being
|
||||
// that the olm library was missing. It's not fatal.
|
||||
console.warn("Unable to initialise e2e: " + e);
|
||||
|
|
48
src/Modal.js
48
src/Modal.js
|
@ -23,6 +23,7 @@ import PropTypes from 'prop-types';
|
|||
import Analytics from './Analytics';
|
||||
import sdk from './index';
|
||||
import dis from './dispatcher';
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
|
||||
|
||||
|
@ -32,15 +33,15 @@ const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
|
|||
*/
|
||||
const AsyncWrapper = React.createClass({
|
||||
propTypes: {
|
||||
/** A function which takes a 'callback' argument which it will call
|
||||
* with the real component once it loads.
|
||||
/** A promise which resolves with the real component
|
||||
*/
|
||||
loader: PropTypes.func.isRequired,
|
||||
prom: PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
component: null,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -49,14 +50,18 @@ const AsyncWrapper = React.createClass({
|
|||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('Starting load of AsyncWrapper for modal');
|
||||
this.props.loader((e) => {
|
||||
// XXX: temporary logging to try to diagnose
|
||||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('AsyncWrapper load completed with '+e.displayName);
|
||||
this.props.prom.then((result) => {
|
||||
if (this._unmounted) {
|
||||
return;
|
||||
}
|
||||
this.setState({component: e});
|
||||
// Take the 'default' member if it's there, then we support
|
||||
// passing in just an import()ed module, since ES6 async import
|
||||
// always returns a module *namespace*.
|
||||
const component = result.default ? result.default : result;
|
||||
this.setState({component});
|
||||
}).catch((e) => {
|
||||
console.warn('AsyncWrapper promise failed', e);
|
||||
this.setState({error: e});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -64,11 +69,27 @@ const AsyncWrapper = React.createClass({
|
|||
this._unmounted = true;
|
||||
},
|
||||
|
||||
_onWrapperCancelClick: function() {
|
||||
this.props.onFinished(false);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const {loader, ...otherProps} = this.props;
|
||||
if (this.state.component) {
|
||||
const Component = this.state.component;
|
||||
return <Component {...otherProps} />;
|
||||
} else if (this.state.error) {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <BaseDialog onFinished={this.props.onFinished}
|
||||
title={_t("Error")}
|
||||
>
|
||||
{_t("Unable to load! Check your network connectivity and try again.")}
|
||||
<DialogButtons primaryButton={_t("Dismiss")}
|
||||
onPrimaryButtonClick={this._onWrapperCancelClick}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</BaseDialog>;
|
||||
} else {
|
||||
// show a spinner until the component is loaded.
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
@ -115,7 +136,7 @@ class ModalManager {
|
|||
}
|
||||
|
||||
createDialog(Element, ...rest) {
|
||||
return this.createDialogAsync((cb) => {cb(Element);}, ...rest);
|
||||
return this.createDialogAsync(new Promise(resolve => resolve(Element)), ...rest);
|
||||
}
|
||||
|
||||
createTrackedDialogAsync(analyticsAction, analyticsInfo, ...rest) {
|
||||
|
@ -133,9 +154,8 @@ class ModalManager {
|
|||
* require(['<module>'], cb);
|
||||
* }
|
||||
*
|
||||
* @param {Function} loader a function which takes a 'callback' argument,
|
||||
* which it should call with a React component which will be displayed as
|
||||
* the modal view.
|
||||
* @param {Promise} prom a promise which resolves with a React component
|
||||
* which will be displayed as the modal view.
|
||||
*
|
||||
* @param {Object} props properties to pass to the displayed
|
||||
* component. (We will also pass an 'onFinished' property.)
|
||||
|
@ -147,7 +167,7 @@ class ModalManager {
|
|||
* Also, when closed, all modals will be removed
|
||||
* from the stack.
|
||||
*/
|
||||
createDialogAsync(loader, props, className, isPriorityModal) {
|
||||
createDialogAsync(prom, props, className, isPriorityModal) {
|
||||
const self = this;
|
||||
const modal = {};
|
||||
|
||||
|
@ -178,7 +198,7 @@ class ModalManager {
|
|||
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
|
||||
// property set here so you can't close the dialog from a button click!
|
||||
modal.elem = (
|
||||
<AsyncWrapper key={modalCount} loader={loader} {...props}
|
||||
<AsyncWrapper key={modalCount} prom={prom} {...props}
|
||||
onFinished={closeDialog} />
|
||||
);
|
||||
modal.onFinished = props ? props.onFinished : null;
|
||||
|
|
|
@ -25,7 +25,6 @@ import { _t } from './languageHandler';
|
|||
* API on the homeserver in question with the new password.
|
||||
*/
|
||||
class PasswordReset {
|
||||
|
||||
/**
|
||||
* Configure the endpoints for password resetting.
|
||||
* @param {string} homeserverUrl The URL to the HS which has the account to reset.
|
||||
|
|
|
@ -23,7 +23,6 @@ const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
|
|||
const PRESENCE_STATES = ["online", "offline", "unavailable"];
|
||||
|
||||
class Presence {
|
||||
|
||||
/**
|
||||
* Start listening the user activity to evaluate his presence state.
|
||||
* Any state change will be sent to the Home Server.
|
||||
|
|
|
@ -45,7 +45,7 @@ export async function startAnyRegistrationFlow(options) {
|
|||
// caution though.
|
||||
const hasIlagFlow = flows.some((flow) => {
|
||||
return flow.stages.every((stage) => {
|
||||
return ['m.login.dummy', 'm.login.recaptcha'].includes(stage);
|
||||
return ['m.login.dummy', 'm.login.recaptcha', 'm.login.terms'].includes(stage);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ export function getDisplayAliasForRoom(room) {
|
|||
* return the other one. Otherwise, return null.
|
||||
*/
|
||||
export function getOnlyOtherMember(room, myUserId) {
|
||||
|
||||
if (room.currentState.getJoinedMemberCount() === 2) {
|
||||
return room.getJoinedMembers().filter(function(m) {
|
||||
return m.userId !== myUserId;
|
||||
|
@ -103,7 +102,7 @@ export function guessAndSetDMRoom(room, isDirect) {
|
|||
let newTarget;
|
||||
if (isDirect) {
|
||||
const guessedUserId = guessDMRoomTargetId(
|
||||
room, MatrixClientPeg.get().getUserId()
|
||||
room, MatrixClientPeg.get().getUserId(),
|
||||
);
|
||||
newTarget = guessedUserId;
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,6 @@ const SdkConfig = require('./SdkConfig');
|
|||
const MatrixClientPeg = require('./MatrixClientPeg');
|
||||
|
||||
class ScalarAuthClient {
|
||||
|
||||
constructor() {
|
||||
this.scalarToken = null;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ const DEFAULTS = {
|
|||
};
|
||||
|
||||
class SdkConfig {
|
||||
|
||||
static get() {
|
||||
return global.mxReactSdkConfig || {};
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import sdk from './index';
|
|||
import {_t, _td} from './languageHandler';
|
||||
import Modal from './Modal';
|
||||
import SettingsStore, {SettingLevel} from './settings/SettingsStore';
|
||||
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
|
||||
import * as querystring from "querystring";
|
||||
|
||||
|
||||
class Command {
|
||||
|
@ -153,11 +155,24 @@ export const CommandMap = {
|
|||
description: _td('Joins room with given alias'),
|
||||
runFn: function(roomId, args) {
|
||||
if (args) {
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
let roomAlias = matches[1];
|
||||
if (roomAlias[0] !== '#') return reject(this.getUsage());
|
||||
// Note: we support 2 versions of this command. The first is
|
||||
// the public-facing one for most users and the other is a
|
||||
// power-user edition where someone may join via permalink or
|
||||
// room ID with optional servers. Practically, this results
|
||||
// in the following variations:
|
||||
// /join #example:example.org
|
||||
// /join !example:example.org
|
||||
// /join !example:example.org altserver.com elsewhere.ca
|
||||
// /join https://matrix.to/#/!example:example.org?via=altserver.com
|
||||
// The command also supports event permalinks transparently:
|
||||
// /join https://matrix.to/#/!example:example.org/$something:example.org
|
||||
// /join https://matrix.to/#/!example:example.org/$something:example.org?via=altserver.com
|
||||
const params = args.split(' ');
|
||||
if (params.length < 1) return reject(this.getUsage());
|
||||
|
||||
const matrixToMatches = params[0].match(MATRIXTO_URL_PATTERN);
|
||||
if (params[0][0] === '#') {
|
||||
let roomAlias = params[0];
|
||||
if (!roomAlias.includes(':')) {
|
||||
roomAlias += ':' + MatrixClientPeg.get().getDomain();
|
||||
}
|
||||
|
@ -167,7 +182,65 @@ export const CommandMap = {
|
|||
room_alias: roomAlias,
|
||||
auto_join: true,
|
||||
});
|
||||
return success();
|
||||
} else if (params[0][0] === '!') {
|
||||
const roomId = params[0];
|
||||
const viaServers = params.splice(0);
|
||||
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId,
|
||||
opts: {
|
||||
// These are passed down to the js-sdk's /join call
|
||||
server_name: viaServers,
|
||||
},
|
||||
auto_join: true,
|
||||
});
|
||||
return success();
|
||||
} else if (matrixToMatches) {
|
||||
let entity = matrixToMatches[1];
|
||||
let eventId = null;
|
||||
let viaServers = [];
|
||||
|
||||
if (entity[0] !== '!' && entity[0] !== '#') return reject(this.getUsage());
|
||||
|
||||
if (entity.indexOf('?') !== -1) {
|
||||
const parts = entity.split('?');
|
||||
entity = parts[0];
|
||||
|
||||
const parsed = querystring.parse(parts[1]);
|
||||
viaServers = parsed["via"];
|
||||
if (typeof viaServers === 'string') viaServers = [viaServers];
|
||||
}
|
||||
|
||||
// We quietly support event ID permalinks too
|
||||
if (entity.indexOf('/$') !== -1) {
|
||||
const parts = entity.split("/$");
|
||||
entity = parts[0];
|
||||
eventId = `$${parts[1]}`;
|
||||
}
|
||||
|
||||
const dispatch = {
|
||||
action: 'view_room',
|
||||
auto_join: true,
|
||||
};
|
||||
|
||||
if (entity[0] === '!') dispatch["room_id"] = entity;
|
||||
else dispatch["room_alias"] = entity;
|
||||
|
||||
if (eventId) {
|
||||
dispatch["event_id"] = eventId;
|
||||
dispatch["highlighted"] = true;
|
||||
}
|
||||
|
||||
if (viaServers) {
|
||||
dispatch["opts"] = {
|
||||
// These are passed down to the js-sdk's /join call
|
||||
server_name: viaServers,
|
||||
};
|
||||
}
|
||||
|
||||
dis.dispatch(dispatch);
|
||||
return success();
|
||||
}
|
||||
}
|
||||
|
@ -492,6 +565,7 @@ export const CommandMap = {
|
|||
const aliases = {
|
||||
j: "join",
|
||||
newballsplease: "discardsession",
|
||||
goto: "join", // because it handles event permalinks magically
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ const CURRENTLY_ACTIVE_THRESHOLD_MS = 2000;
|
|||
* with the app (but at a much lower frequency than mouse move events)
|
||||
*/
|
||||
class UserActivity {
|
||||
|
||||
/**
|
||||
* Start listening to user activity
|
||||
*/
|
||||
|
|
|
@ -87,7 +87,7 @@ export default {
|
|||
device_display_name: address,
|
||||
lang: navigator.language,
|
||||
data: data,
|
||||
append: true, // We always append for email pushers since we don't want to stop other accounts notifying to the same email address
|
||||
append: true, // We always append for email pushers since we don't want to stop other accounts notifying to the same email address
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,8 +3,8 @@ const Velocity = require('velocity-vector');
|
|||
// courtesy of https://github.com/julianshapiro/velocity/issues/283
|
||||
// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
|
||||
function bounce( p ) {
|
||||
let pow2,
|
||||
bounce = 4;
|
||||
let pow2;
|
||||
let bounce = 4;
|
||||
|
||||
while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {
|
||||
// just sets pow2
|
||||
|
|
|
@ -62,6 +62,35 @@ function createAccountDataAction(matrixClient, accountDataEvent) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAccountDataAction
|
||||
* @type {Object}
|
||||
* @property {string} action 'MatrixActions.Room.accountData'.
|
||||
* @property {MatrixEvent} event the MatrixEvent that triggered the dispatch.
|
||||
* @property {string} event_type the type of the MatrixEvent, e.g. "m.direct".
|
||||
* @property {Object} event_content the content of the MatrixEvent.
|
||||
* @property {Room} room the room where the account data was changed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a MatrixActions.Room.accountData action that represents a MatrixClient `Room.accountData`
|
||||
* matrix event.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient the matrix client.
|
||||
* @param {MatrixEvent} accountDataEvent the account data event.
|
||||
* @param {Room} room the room where account data was changed
|
||||
* @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData.
|
||||
*/
|
||||
function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
|
||||
return {
|
||||
action: 'MatrixActions.Room.accountData',
|
||||
event: accountDataEvent,
|
||||
event_type: accountDataEvent.getType(),
|
||||
event_content: accountDataEvent.getContent(),
|
||||
room: room,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAction
|
||||
* @type {Object}
|
||||
|
@ -201,6 +230,7 @@ export default {
|
|||
start(matrixClient) {
|
||||
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction);
|
||||
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room', createRoomAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
|
||||
|
|
|
@ -85,7 +85,7 @@ export default class Autocompleter {
|
|||
provider
|
||||
.getCompletions(query, selection, force)
|
||||
.timeout(PROVIDER_COMPLETION_TIMEOUT)
|
||||
.reflect()
|
||||
.reflect(),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ export default class CommunityProvider extends AutocompleteProvider {
|
|||
if (command) {
|
||||
const joinedGroups = cli.getGroups().filter(({myMembership}) => myMembership === 'join');
|
||||
|
||||
const groups = (await Promise.all(joinedGroups.map(async ({groupId}) => {
|
||||
const groups = (await Promise.all(joinedGroups.map(async({groupId}) => {
|
||||
try {
|
||||
return FlairStore.getGroupProfileCached(cli, groupId);
|
||||
} catch (e) { // if FlairStore failed, fall back to just groupId
|
||||
|
|
|
@ -26,7 +26,6 @@ import { Block } from 'slate';
|
|||
*/
|
||||
|
||||
class PlainWithPillsSerializer {
|
||||
|
||||
/*
|
||||
* @param {String} options.pillFormat - either 'md', 'plain', 'id'
|
||||
*/
|
||||
|
|
|
@ -33,12 +33,12 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return({
|
||||
directoryHover : false,
|
||||
roomsHover : false,
|
||||
return ({
|
||||
directoryHover: false,
|
||||
roomsHover: false,
|
||||
homeHover: false,
|
||||
peopleHover : false,
|
||||
settingsHover : false,
|
||||
peopleHover: false,
|
||||
settingsHover: false,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -145,7 +145,7 @@ module.exports = React.createClass({
|
|||
// Get the label/tooltip to show
|
||||
getLabel: function(label, show) {
|
||||
if (show) {
|
||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -16,18 +16,18 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CompatibilityPage',
|
||||
propTypes: {
|
||||
onAccept: React.PropTypes.func
|
||||
onAccept: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onAccept: function() {} // NOP
|
||||
onAccept: function() {}, // NOP
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -36,7 +36,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
return (
|
||||
<div className="mx_CompatibilityPage">
|
||||
<div className="mx_CompatibilityPage_box">
|
||||
|
@ -69,5 +68,5 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -36,10 +36,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
phases: {
|
||||
CONFIG: "CONFIG", // We're waiting for user to configure and hit create.
|
||||
CREATING: "CREATING", // We're sending the request.
|
||||
CREATED: "CREATED", // We successfully created the room.
|
||||
ERROR: "ERROR", // There was an error while trying to create room.
|
||||
CONFIG: "CONFIG", // We're waiting for user to configure and hit create.
|
||||
CREATING: "CREATING", // We're sending the request.
|
||||
CREATED: "CREATED", // We successfully created the room.
|
||||
ERROR: "ERROR", // There was an error while trying to create room.
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
|
|
@ -746,14 +746,38 @@ export default React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_leaveGroupWarnings: function() {
|
||||
const warnings = [];
|
||||
|
||||
if (this.state.isUserPrivileged) {
|
||||
warnings.push((
|
||||
<span className="warning">
|
||||
{ " " /* Whitespace, otherwise the sentences get smashed together */ }
|
||||
{ _t("You are an administrator of this community. You will not be " +
|
||||
"able to rejoin without an invite from another administrator.") }
|
||||
</span>
|
||||
));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
},
|
||||
|
||||
|
||||
_onLeaveClick: function() {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const warnings = this._leaveGroupWarnings();
|
||||
|
||||
Modal.createTrackedDialog('Leave Group', '', QuestionDialog, {
|
||||
title: _t("Leave Community"),
|
||||
description: _t("Leave %(groupName)s?", {groupName: this.props.groupId}),
|
||||
description: (
|
||||
<span>
|
||||
{ _t("Leave %(groupName)s?", {groupName: this.props.groupId}) }
|
||||
{ warnings }
|
||||
</span>
|
||||
),
|
||||
button: _t("Leave"),
|
||||
danger: true,
|
||||
onFinished: async (confirmed) => {
|
||||
danger: this.state.isUserPrivileged,
|
||||
onFinished: async(confirmed) => {
|
||||
if (!confirmed) return;
|
||||
|
||||
this.setState({membershipBusy: true});
|
||||
|
|
|
@ -23,6 +23,8 @@ import request from 'browser-request';
|
|||
import { _t } from '../../languageHandler';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import sdk from '../../index';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import dis from '../../dispatcher';
|
||||
|
||||
class HomePage extends React.Component {
|
||||
static displayName = 'HomePage';
|
||||
|
@ -37,6 +39,10 @@ class HomePage extends React.Component {
|
|||
homePageUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||
};
|
||||
|
||||
state = {
|
||||
iframeSrc: '',
|
||||
page: '',
|
||||
|
@ -52,15 +58,14 @@ class HomePage extends React.Component {
|
|||
|
||||
if (this.props.teamToken && this.props.teamServerUrl) {
|
||||
this.setState({
|
||||
iframeSrc: `${this.props.teamServerUrl}/static/${this.props.teamToken}/home.html`
|
||||
iframeSrc: `${this.props.teamServerUrl}/static/${this.props.teamToken}/home.html`,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// we use request() to inline the homepage into the react component
|
||||
// so that it can inherit CSS and theming easily rather than mess around
|
||||
// with iframes and trying to synchronise document.stylesheets.
|
||||
|
||||
let src = this.props.homePageUrl || 'home.html';
|
||||
const src = this.props.homePageUrl || 'home.html';
|
||||
|
||||
request(
|
||||
{ method: "GET", url: src },
|
||||
|
@ -77,7 +82,7 @@ class HomePage extends React.Component {
|
|||
|
||||
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
|
||||
this.setState({ page: body });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,18 +91,55 @@ class HomePage extends React.Component {
|
|||
this._unmounted = true;
|
||||
}
|
||||
|
||||
onLoginClick() {
|
||||
dis.dispatch({ action: 'start_login' });
|
||||
}
|
||||
|
||||
onRegisterClick() {
|
||||
dis.dispatch({ action: 'start_registration' });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.iframeSrc) {
|
||||
return (
|
||||
<div className="mx_HomePage">
|
||||
<iframe src={ this.state.iframeSrc } />
|
||||
let guestWarning = "";
|
||||
if (this.context.matrixClient.isGuest()) {
|
||||
guestWarning = (
|
||||
<div className="mx_HomePage_guest_warning">
|
||||
<img src="img/warning.svg" width="24" height="23" />
|
||||
<div>
|
||||
<div>
|
||||
{ _t("You are currently using Riot anonymously as a guest.") }
|
||||
</div>
|
||||
<div>
|
||||
{ _t(
|
||||
'If you would like to create a Matrix account you can <a>register</a> now.',
|
||||
{},
|
||||
{ 'a': (sub) => <a href="#" onClick={this.onRegisterClick}>{ sub }</a> },
|
||||
) }
|
||||
</div>
|
||||
<div>
|
||||
{ _t(
|
||||
'If you already have a Matrix account you can <a>log in</a> instead.',
|
||||
{},
|
||||
{ 'a': (sub) => <a href="#" onClick={this.onLoginClick}>{ sub }</a> },
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
if (this.state.iframeSrc) {
|
||||
return (
|
||||
<div className="mx_HomePage">
|
||||
{ guestWarning }
|
||||
<iframe src={ this.state.iframeSrc } />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||
return (
|
||||
<GeminiScrollbarWrapper autoshow={true} className="mx_HomePage">
|
||||
{ guestWarning }
|
||||
<div className="mx_HomePage_body" dangerouslySetInnerHTML={{ __html: this.state.page }}>
|
||||
</div>
|
||||
</GeminiScrollbarWrapper>
|
||||
|
@ -106,4 +148,4 @@ class HomePage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = HomePage;
|
||||
module.exports = HomePage;
|
||||
|
|
|
@ -68,6 +68,11 @@ export default React.createClass({
|
|||
// If true, poll to see if the auth flow has been completed
|
||||
// out-of-band
|
||||
poll: PropTypes.bool,
|
||||
|
||||
// If true, components will be told that the 'Continue' button
|
||||
// is managed by some other party and should not be managed by
|
||||
// the component itself.
|
||||
continueIsManaged: PropTypes.bool,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -128,6 +133,12 @@ export default React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
tryContinue: function() {
|
||||
if (this.refs.stageComponent && this.refs.stageComponent.tryContinue) {
|
||||
this.refs.stageComponent.tryContinue();
|
||||
}
|
||||
},
|
||||
|
||||
_authStateUpdated: function(stageType, stageState) {
|
||||
const oldStage = this.state.authStage;
|
||||
this.setState({
|
||||
|
@ -192,6 +203,7 @@ export default React.createClass({
|
|||
fail={this._onAuthStageFailed}
|
||||
setEmailSid={this._setEmailSid}
|
||||
makeRegistrationUrl={this.props.makeRegistrationUrl}
|
||||
showContinue={!this.props.continueIsManaged}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@ import VectorConferenceHandler from '../../VectorConferenceHandler';
|
|||
import SettingsStore from '../../settings/SettingsStore';
|
||||
|
||||
|
||||
var LeftPanel = React.createClass({
|
||||
const LeftPanel = React.createClass({
|
||||
displayName: 'LeftPanel',
|
||||
|
||||
// NB. If you add props, don't forget to update
|
||||
|
@ -181,14 +181,8 @@ var LeftPanel = React.createClass({
|
|||
const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
|
||||
const CallPreview = sdk.getComponent('voip.CallPreview');
|
||||
|
||||
let topBox;
|
||||
if (this.context.matrixClient.isGuest()) {
|
||||
const LoginBox = sdk.getComponent('structures.LoginBox');
|
||||
topBox = <LoginBox collapsed={ this.props.collapsed }/>;
|
||||
} else {
|
||||
const SearchBox = sdk.getComponent('structures.SearchBox');
|
||||
topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
|
||||
}
|
||||
const SearchBox = sdk.getComponent('structures.SearchBox');
|
||||
const topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
|
||||
|
||||
const classes = classNames(
|
||||
"mx_LeftPanel",
|
||||
|
@ -220,11 +214,11 @@ var LeftPanel = React.createClass({
|
|||
collapsed={this.props.collapsed}
|
||||
searchFilter={this.state.searchFilter}
|
||||
ConferenceHandler={VectorConferenceHandler} />
|
||||
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
||||
<BottomLeftMenu collapsed={this.props.collapsed} />
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = LeftPanel;
|
||||
|
|
|
@ -64,6 +64,9 @@ const LoggedInView = React.createClass({
|
|||
|
||||
teamToken: PropTypes.string,
|
||||
|
||||
// Used by the RoomView to handle joining rooms
|
||||
viaServers: PropTypes.arrayOf(PropTypes.string),
|
||||
|
||||
// and lots and lots of other stuff.
|
||||
},
|
||||
|
||||
|
@ -186,13 +189,13 @@ const LoggedInView = React.createClass({
|
|||
_updateServerNoticeEvents: async function() {
|
||||
const roomLists = RoomListStore.getRoomLists();
|
||||
if (!roomLists['m.server_notice']) return [];
|
||||
|
||||
|
||||
const pinnedEvents = [];
|
||||
for (const room of roomLists['m.server_notice']) {
|
||||
const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", "");
|
||||
|
||||
if (!pinStateEvent || !pinStateEvent.getContent().pinned) continue;
|
||||
|
||||
|
||||
const pinnedEventIds = pinStateEvent.getContent().pinned.slice(0, MAX_PINNED_NOTICES_PER_ROOM);
|
||||
for (const eventId of pinnedEventIds) {
|
||||
const timeline = await this._matrixClient.getEventTimeline(room.getUnfilteredTimelineSet(), eventId, 0);
|
||||
|
@ -204,7 +207,7 @@ const LoggedInView = React.createClass({
|
|||
serverNoticeEvents: pinnedEvents,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
_onKeyDown: function(ev) {
|
||||
/*
|
||||
|
@ -389,6 +392,7 @@ const LoggedInView = React.createClass({
|
|||
onRegistered={this.props.onRegistered}
|
||||
thirdPartyInvite={this.props.thirdPartyInvite}
|
||||
oobData={this.props.roomOobData}
|
||||
viaServers={this.props.viaServers}
|
||||
eventPixelOffset={this.props.initialEventPixelOffset}
|
||||
key={this.props.currentRoomId || 'roomview'}
|
||||
disabled={this.props.middleDisabled}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -16,31 +17,15 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
import { _t } from '../../languageHandler';
|
||||
var sdk = require('../../index')
|
||||
var dis = require('../../dispatcher');
|
||||
var rate_limited_func = require('../../ratelimitedfunc');
|
||||
var AccessibleButton = require('../../components/views/elements/AccessibleButton');
|
||||
const dis = require('../../dispatcher');
|
||||
const AccessibleButton = require('../../components/views/elements/AccessibleButton');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'LoginBox',
|
||||
|
||||
propTypes: {
|
||||
collapsed: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
onToggleCollapse: function(show) {
|
||||
if (show) {
|
||||
dis.dispatch({
|
||||
action: 'show_left_panel',
|
||||
});
|
||||
}
|
||||
else {
|
||||
dis.dispatch({
|
||||
action: 'hide_left_panel',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onLoginClick: function() {
|
||||
|
@ -52,42 +37,21 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
|
||||
var toggleCollapse;
|
||||
if (this.props.collapsed) {
|
||||
toggleCollapse =
|
||||
<AccessibleButton className="mx_SearchBox_maximise" onClick={ this.onToggleCollapse.bind(this, true) }>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16" alt="Expand panel"/>
|
||||
const loginButton = (
|
||||
<div className="mx_LoginBox_loginButton_wrapper">
|
||||
<AccessibleButton className="mx_LoginBox_loginButton" element="button" onClick={this.onLoginClick}>
|
||||
{ _t("Login") }
|
||||
</AccessibleButton>
|
||||
}
|
||||
else {
|
||||
toggleCollapse =
|
||||
<AccessibleButton className="mx_SearchBox_minimise" onClick={ this.onToggleCollapse.bind(this, false) }>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16" alt="Collapse panel"/>
|
||||
<AccessibleButton className="mx_LoginBox_registerButton" element="button" onClick={this.onRegisterClick}>
|
||||
{ _t("Register") }
|
||||
</AccessibleButton>
|
||||
}
|
||||
|
||||
var loginButton;
|
||||
if (!this.props.collapsed) {
|
||||
loginButton = (
|
||||
<div className="mx_LoginBox_loginButton_wrapper">
|
||||
<AccessibleButton className="mx_LoginBox_loginButton" element="button" onClick={this.onLoginClick}>
|
||||
{ _t("Login") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_LoginBox_registerButton" element="button" onClick={this.onRegisterClick}>
|
||||
{ _t("Register") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return (
|
||||
<div className="mx_SearchBox mx_LoginBox">
|
||||
{ loginButton }
|
||||
{ toggleCollapse }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_LoginBox">
|
||||
{ loginButton }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -840,6 +840,7 @@ export default React.createClass({
|
|||
page_type: PageTypes.RoomView,
|
||||
thirdPartyInvite: roomInfo.third_party_invite,
|
||||
roomOobData: roomInfo.oob_data,
|
||||
viaServers: roomInfo.via_servers,
|
||||
};
|
||||
|
||||
if (roomInfo.room_alias) {
|
||||
|
@ -1034,6 +1035,7 @@ export default React.createClass({
|
|||
{ warnings }
|
||||
</span>
|
||||
),
|
||||
button: _t("Leave"),
|
||||
onFinished: (shouldLeave) => {
|
||||
if (shouldLeave) {
|
||||
const d = MatrixClientPeg.get().leave(roomId);
|
||||
|
@ -1373,6 +1375,7 @@ export default React.createClass({
|
|||
cli.on("crypto.roomKeyRequestCancellation", (req) => {
|
||||
krh.handleKeyRequestCancellation(req);
|
||||
});
|
||||
|
||||
cli.on("Room", (room) => {
|
||||
if (MatrixClientPeg.get().isCryptoEnabled()) {
|
||||
const blacklistEnabled = SettingsStore.getValueAt(
|
||||
|
@ -1406,8 +1409,8 @@ export default React.createClass({
|
|||
|
||||
// Fire the tinter right on startup to ensure the default theme is applied
|
||||
// A later sync can/will correct the tint to be the right value for the user
|
||||
const color_scheme = SettingsStore.getValue("roomColor");
|
||||
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
||||
const colorScheme = SettingsStore.getValue("roomColor");
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1488,9 +1491,21 @@ export default React.createClass({
|
|||
inviterName: params.inviter_name,
|
||||
};
|
||||
|
||||
// on our URLs there might be a ?via=matrix.org or similar to help
|
||||
// joins to the room succeed. We'll pass these through as an array
|
||||
// to other levels. If there's just one ?via= then params.via is a
|
||||
// single string. If someone does something like ?via=one.com&via=two.com
|
||||
// then params.via is an array of strings.
|
||||
let via = [];
|
||||
if (params.via) {
|
||||
if (typeof(params.via) === 'string') via = [params.via];
|
||||
else via = params.via;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
action: 'view_room',
|
||||
event_id: eventId,
|
||||
via_servers: via,
|
||||
// If an event ID is given in the URL hash, notify RoomViewStore to mark
|
||||
// it as highlighted, which will propagate to RoomView and highlight the
|
||||
// associated EventTile.
|
||||
|
|
|
@ -16,18 +16,18 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
|
||||
var MatrixClientPeg = require('../../MatrixClientPeg');
|
||||
var ContentRepo = require("matrix-js-sdk").ContentRepo;
|
||||
var Modal = require('../../Modal');
|
||||
var sdk = require('../../index');
|
||||
var dis = require('../../dispatcher');
|
||||
const MatrixClientPeg = require('../../MatrixClientPeg');
|
||||
const ContentRepo = require("matrix-js-sdk").ContentRepo;
|
||||
const Modal = require('../../Modal');
|
||||
const sdk = require('../../index');
|
||||
const dis = require('../../dispatcher');
|
||||
|
||||
var linkify = require('linkifyjs');
|
||||
var linkifyString = require('linkifyjs/string');
|
||||
var linkifyMatrix = require('../../linkify-matrix');
|
||||
var sanitizeHtml = require('sanitize-html');
|
||||
const linkify = require('linkifyjs');
|
||||
const linkifyString = require('linkifyjs/string');
|
||||
const linkifyMatrix = require('../../linkify-matrix');
|
||||
const sanitizeHtml = require('sanitize-html');
|
||||
import Promise from 'bluebird';
|
||||
|
||||
import { _t } from '../../languageHandler';
|
||||
|
@ -46,7 +46,7 @@ module.exports = React.createClass({
|
|||
getDefaultProps: function() {
|
||||
return {
|
||||
config: {},
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -58,7 +58,7 @@ module.exports = React.createClass({
|
|||
includeAll: false,
|
||||
roomServer: null,
|
||||
filterString: null,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -134,13 +134,12 @@ module.exports = React.createClass({
|
|||
opts.include_all_networks = true;
|
||||
}
|
||||
if (this.nextBatch) opts.since = this.nextBatch;
|
||||
if (my_filter_string) opts.filter = { generic_search_term: my_filter_string } ;
|
||||
if (my_filter_string) opts.filter = { generic_search_term: my_filter_string };
|
||||
return MatrixClientPeg.get().publicRooms(opts).then((data) => {
|
||||
if (
|
||||
my_filter_string != this.state.filterString ||
|
||||
my_server != this.state.roomServer ||
|
||||
my_next_batch != this.nextBatch)
|
||||
{
|
||||
my_next_batch != this.nextBatch) {
|
||||
// if the filter or server has changed since this request was sent,
|
||||
// throw away the result (don't even clear the busy flag
|
||||
// since we must still have a request in flight)
|
||||
|
@ -163,8 +162,7 @@ module.exports = React.createClass({
|
|||
if (
|
||||
my_filter_string != this.state.filterString ||
|
||||
my_server != this.state.roomServer ||
|
||||
my_next_batch != this.nextBatch)
|
||||
{
|
||||
my_next_batch != this.nextBatch) {
|
||||
// as above: we don't care about errors for old
|
||||
// requests either
|
||||
return;
|
||||
|
@ -177,10 +175,10 @@ module.exports = React.createClass({
|
|||
|
||||
this.setState({ loading: false });
|
||||
console.error("Failed to get publicRooms: %s", JSON.stringify(err));
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to get public room list', '', ErrorDialog, {
|
||||
title: _t('Failed to get public room list'),
|
||||
description: ((err && err.message) ? err.message : _t('The server may be unavailable or overloaded'))
|
||||
description: ((err && err.message) ? err.message : _t('The server may be unavailable or overloaded')),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -193,13 +191,13 @@ module.exports = React.createClass({
|
|||
* this needs SPEC-417.
|
||||
*/
|
||||
removeFromDirectory: function(room) {
|
||||
var alias = get_display_alias_for_room(room);
|
||||
var name = room.name || alias || _t('Unnamed room');
|
||||
const alias = get_display_alias_for_room(room);
|
||||
const name = room.name || alias || _t('Unnamed room');
|
||||
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
var desc;
|
||||
let desc;
|
||||
if (alias) {
|
||||
desc = _t('Delete the room alias %(alias)s and remove %(name)s from the directory?', {alias: alias, name: name});
|
||||
} else {
|
||||
|
@ -212,9 +210,9 @@ module.exports = React.createClass({
|
|||
onFinished: (should_delete) => {
|
||||
if (!should_delete) return;
|
||||
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
var modal = Modal.createDialog(Loader);
|
||||
var step = _t('remove %(name)s from the directory.', {name: name});
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const modal = Modal.createDialog(Loader);
|
||||
let step = _t('remove %(name)s from the directory.', {name: name});
|
||||
|
||||
MatrixClientPeg.get().setRoomDirectoryVisibility(room.room_id, 'private').then(() => {
|
||||
if (!alias) return;
|
||||
|
@ -229,10 +227,10 @@ module.exports = React.createClass({
|
|||
console.error("Failed to " + step + ": " + err);
|
||||
Modal.createTrackedDialog('Remove from Directory Error', '', ErrorDialog, {
|
||||
title: _t('Error'),
|
||||
description: ((err && err.message) ? err.message : _t('The server may be unavailable or overloaded'))
|
||||
description: ((err && err.message) ? err.message : _t('The server may be unavailable or overloaded')),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -347,7 +345,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
showRoom: function(room, room_alias) {
|
||||
var payload = {action: 'view_room'};
|
||||
const payload = {action: 'view_room'};
|
||||
if (room) {
|
||||
// Don't let the user view a room they won't be able to either
|
||||
// peek or join: fail earlier so they don't have to click back
|
||||
|
@ -383,16 +381,16 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getRows: function() {
|
||||
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||
|
||||
if (!this.state.publicRooms) return [];
|
||||
|
||||
var rooms = this.state.publicRooms;
|
||||
var rows = [];
|
||||
var self = this;
|
||||
var guestRead, guestJoin, perms;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var name = rooms[i].name || get_display_alias_for_room(rooms[i]) || _t('Unnamed room');
|
||||
const rooms = this.state.publicRooms;
|
||||
const rows = [];
|
||||
const self = this;
|
||||
let guestRead; let guestJoin; let perms;
|
||||
for (let i = 0; i < rooms.length; i++) {
|
||||
const name = rooms[i].name || get_display_alias_for_room(rooms[i]) || _t('Unnamed room');
|
||||
guestRead = null;
|
||||
guestJoin = null;
|
||||
|
||||
|
@ -412,7 +410,7 @@ module.exports = React.createClass({
|
|||
perms = <div className="mx_RoomDirectory_perms">{guestRead}{guestJoin}</div>;
|
||||
}
|
||||
|
||||
var topic = rooms[i].topic || '';
|
||||
let topic = rooms[i].topic || '';
|
||||
topic = linkifyString(sanitizeHtml(topic));
|
||||
|
||||
rows.push(
|
||||
|
@ -432,14 +430,14 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||
{ perms }
|
||||
<div className="mx_RoomDirectory_topic"
|
||||
onClick={ function(e) { e.stopPropagation() } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }}/>
|
||||
onClick={ function(e) { e.stopPropagation(); } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }} />
|
||||
<div className="mx_RoomDirectory_alias">{ get_display_alias_for_room(rooms[i]) }</div>
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomMemberCount">
|
||||
{ rooms[i].num_joined_members }
|
||||
</td>
|
||||
</tr>
|
||||
</tr>,
|
||||
);
|
||||
}
|
||||
return rows;
|
||||
|
@ -524,7 +522,7 @@ module.exports = React.createClass({
|
|||
onFillRequest={ this.onFillRequest }
|
||||
stickyBottom={false}
|
||||
startAtBottom={false}
|
||||
onResize={function(){}}
|
||||
onResize={function() {}}
|
||||
>
|
||||
{ scrollpanel_content }
|
||||
</ScrollPanel>;
|
||||
|
@ -577,11 +575,11 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
||||
// but works with the objects we get from the public room list
|
||||
function get_display_alias_for_room(room) {
|
||||
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
||||
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ function getUnsentMessages(room) {
|
|||
return room.getPendingEvents().filter(function(ev) {
|
||||
return ev.status === Matrix.EventStatus.NOT_SENT;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomStatusBar',
|
||||
|
@ -303,7 +303,7 @@ module.exports = React.createClass({
|
|||
const errorIsMauError = Boolean(
|
||||
this.state.syncStateData &&
|
||||
this.state.syncStateData.error &&
|
||||
this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED'
|
||||
this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED',
|
||||
);
|
||||
return this.state.syncState === "ERROR" && !errorIsMauError;
|
||||
},
|
||||
|
|
|
@ -88,6 +88,9 @@ module.exports = React.createClass({
|
|||
|
||||
// is the RightPanel collapsed?
|
||||
collapsedRhs: PropTypes.bool,
|
||||
|
||||
// Servers the RoomView can use to try and assist joins
|
||||
viaServers: PropTypes.arrayOf(PropTypes.string),
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -678,8 +681,8 @@ module.exports = React.createClass({
|
|||
if (!room) return;
|
||||
|
||||
console.log("Tinter.tint from updateTint");
|
||||
const color_scheme = SettingsStore.getValue("roomColor", room.roomId);
|
||||
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
||||
const colorScheme = SettingsStore.getValue("roomColor", room.roomId);
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
},
|
||||
|
||||
onAccountData: function(event) {
|
||||
|
@ -694,10 +697,10 @@ module.exports = React.createClass({
|
|||
if (room.roomId == this.state.roomId) {
|
||||
const type = event.getType();
|
||||
if (type === "org.matrix.room.color_scheme") {
|
||||
const color_scheme = event.getContent();
|
||||
const colorScheme = event.getContent();
|
||||
// XXX: we should validate the event
|
||||
console.log("Tinter.tint from onRoomAccountData");
|
||||
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
} else if (type === "org.matrix.room.preview_urls" || type === "im.vector.web.settings") {
|
||||
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||
this._updatePreviewUrlVisibility(room);
|
||||
|
@ -833,7 +836,7 @@ module.exports = React.createClass({
|
|||
action: 'do_after_sync_prepared',
|
||||
deferred_action: {
|
||||
action: 'join_room',
|
||||
opts: { inviteSignUrl: signUrl },
|
||||
opts: { inviteSignUrl: signUrl, viaServers: this.props.viaServers },
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -875,7 +878,7 @@ module.exports = React.createClass({
|
|||
this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
||||
dis.dispatch({
|
||||
action: 'join_room',
|
||||
opts: { inviteSignUrl: signUrl },
|
||||
opts: { inviteSignUrl: signUrl, viaServers: this.props.viaServers },
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
@ -1514,6 +1517,7 @@ module.exports = React.createClass({
|
|||
canPreview={false} error={this.state.roomLoadError}
|
||||
roomAlias={roomAlias}
|
||||
spinner={this.state.joining}
|
||||
spinnerState="joining"
|
||||
inviterName={inviterName}
|
||||
invitedEmail={invitedEmail}
|
||||
room={this.state.room}
|
||||
|
@ -1558,6 +1562,7 @@ module.exports = React.createClass({
|
|||
inviterName={inviterName}
|
||||
canPreview={false}
|
||||
spinner={this.state.joining}
|
||||
spinnerState="joining"
|
||||
room={this.state.room}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1645,6 +1650,7 @@ module.exports = React.createClass({
|
|||
onForgetClick={this.onForgetClick}
|
||||
onRejectClick={this.onRejectThreepidInviteButtonClicked}
|
||||
spinner={this.state.joining}
|
||||
spinnerState="joining"
|
||||
inviterName={inviterName}
|
||||
invitedEmail={invitedEmail}
|
||||
canPreview={this.state.canPeek}
|
||||
|
@ -1667,10 +1673,10 @@ module.exports = React.createClass({
|
|||
</AuxPanel>
|
||||
);
|
||||
|
||||
let messageComposer, searchInfo;
|
||||
let messageComposer; let searchInfo;
|
||||
const canSpeak = (
|
||||
// joined and not showing search results
|
||||
myMembership == 'join' && !this.state.searchResults
|
||||
myMembership === 'join' && !this.state.searchResults
|
||||
);
|
||||
if (canSpeak) {
|
||||
messageComposer =
|
||||
|
@ -1684,6 +1690,11 @@ module.exports = React.createClass({
|
|||
/>;
|
||||
}
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
const LoginBox = sdk.getComponent('structures.LoginBox');
|
||||
messageComposer = <LoginBox />;
|
||||
}
|
||||
|
||||
// TODO: Why aren't we storing the term/scope/count in this format
|
||||
// in this.state if this is what RoomHeader desires?
|
||||
if (this.state.searchResults) {
|
||||
|
@ -1695,7 +1706,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
if (inCall) {
|
||||
let zoomButton, voiceMuteButton, videoMuteButton;
|
||||
let zoomButton; let voiceMuteButton; let videoMuteButton;
|
||||
|
||||
if (call.type === "video") {
|
||||
zoomButton = (
|
||||
|
|
|
@ -72,7 +72,7 @@ module.exports = React.createClass({
|
|||
function() {
|
||||
this.props.onSearch(this.refs.search.value);
|
||||
},
|
||||
100
|
||||
100,
|
||||
),
|
||||
|
||||
onToggleCollapse: function(show) {
|
||||
|
@ -80,8 +80,7 @@ module.exports = React.createClass({
|
|||
dis.dispatch({
|
||||
action: 'show_left_panel',
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dis.dispatch({
|
||||
action: 'hide_left_panel',
|
||||
});
|
||||
|
@ -103,25 +102,24 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
|
||||
var collapseTabIndex = this.refs.search && this.refs.search.value !== "" ? "-1" : "0";
|
||||
const collapseTabIndex = this.refs.search && this.refs.search.value !== "" ? "-1" : "0";
|
||||
|
||||
var toggleCollapse;
|
||||
let toggleCollapse;
|
||||
if (this.props.collapsed) {
|
||||
toggleCollapse =
|
||||
<AccessibleButton className="mx_SearchBox_maximise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, true) }>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16" alt={ _t("Expand panel") }/>
|
||||
</AccessibleButton>
|
||||
}
|
||||
else {
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16" alt={ _t("Expand panel") } />
|
||||
</AccessibleButton>;
|
||||
} else {
|
||||
toggleCollapse =
|
||||
<AccessibleButton className="mx_SearchBox_minimise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, false) }>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16" alt={ _t("Collapse panel") }/>
|
||||
</AccessibleButton>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16" alt={ _t("Collapse panel") } />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
var searchControls;
|
||||
let searchControls;
|
||||
if (!this.props.collapsed) {
|
||||
searchControls = [
|
||||
this.state.searchTerm.length > 0 ?
|
||||
|
@ -148,16 +146,16 @@ module.exports = React.createClass({
|
|||
onChange={ this.onChange }
|
||||
onKeyDown={ this._onKeyDown }
|
||||
placeholder={ _t('Filter room names') }
|
||||
/>
|
||||
/>,
|
||||
];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
return (
|
||||
<div className="mx_SearchBox">
|
||||
{ searchControls }
|
||||
{ toggleCollapse }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -829,7 +829,7 @@ var TimelinePanel = React.createClass({
|
|||
// 4. Also, if pos === null, the event might not be paginated - show the unread bar
|
||||
const pos = this.getReadMarkerPosition();
|
||||
return this.state.readMarkerEventId !== null && // 1.
|
||||
this.state.readMarkerEventId !== this._getCurrentReadReceipt() && // 2.
|
||||
this.state.readMarkerEventId !== this._getCurrentReadReceipt() && // 2.
|
||||
(pos < 0 || pos === null); // 3., 4.
|
||||
},
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ const SIMPLE_SETTINGS = [
|
|||
{ id: "TagPanel.disableTagPanel" },
|
||||
{ id: "enableWidgetScreenshots" },
|
||||
{ id: "RoomSubList.showEmpty" },
|
||||
{ id: "pinMentionedRooms" },
|
||||
{ id: "pinUnreadRooms" },
|
||||
{ id: "showDeveloperTools" },
|
||||
];
|
||||
|
||||
// These settings must be defined in SettingsStore
|
||||
|
@ -586,23 +589,21 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_onExportE2eKeysClicked: function() {
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', '', (cb) => {
|
||||
require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
|
||||
cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog'));
|
||||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
});
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', '',
|
||||
import('../../async-components/views/dialogs/ExportE2eKeysDialog'),
|
||||
{
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
_onImportE2eKeysClicked: function() {
|
||||
Modal.createTrackedDialogAsync('Import E2E Keys', '', (cb) => {
|
||||
require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => {
|
||||
cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog'));
|
||||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
});
|
||||
Modal.createTrackedDialogAsync('Import E2E Keys', '',
|
||||
import('../../async-components/views/dialogs/ImportE2eKeysDialog'),
|
||||
{
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
_renderGroupSettings: function() {
|
||||
|
@ -736,6 +737,16 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let keyBackupSection;
|
||||
if (SettingsStore.isFeatureEnabled("feature_keybackup")) {
|
||||
const KeyBackupPanel = sdk.getComponent('views.settings.KeyBackupPanel');
|
||||
keyBackupSection = <div className="mx_UserSettings_section">
|
||||
<h3>{ _t("Key Backup") }</h3>
|
||||
<KeyBackupPanel />
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{ _t("Cryptography") }</h3>
|
||||
|
@ -751,6 +762,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_UserSettings_section">
|
||||
{ CRYPTO_SETTINGS.map( this._renderDeviceSetting ) }
|
||||
</div>
|
||||
{keyBackupSection}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -844,7 +856,7 @@ module.exports = React.createClass({
|
|||
SettingsStore.getLabsFeatures().forEach((featureId) => {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
const onChange = async (e) => {
|
||||
const onChange = async(e) => {
|
||||
const checked = e.target.checked;
|
||||
if (featureId === "feature_lazyloading") {
|
||||
const confirmed = await this._onLazyLoadChanging(checked);
|
||||
|
@ -1297,7 +1309,7 @@ module.exports = React.createClass({
|
|||
// If the olmVersion is not defined then either crypto is disabled, or
|
||||
// we are using a version old version of olm. We assume the former.
|
||||
let olmVersionString = "<not-enabled>";
|
||||
if (olmVersion !== undefined) {
|
||||
if (olmVersion) {
|
||||
olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,5 +53,5 @@ module.exports = React.createClass({
|
|||
</SyntaxHighlight>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -121,13 +121,12 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_onExportE2eKeysClicked: function() {
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', 'Forgot Password', (cb) => {
|
||||
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
|
||||
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
|
||||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
});
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', 'Forgot Password',
|
||||
import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
|
||||
{
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
onInputChanged: function(stateKey, ev) {
|
||||
|
|
|
@ -90,7 +90,10 @@ module.exports = React.createClass({
|
|||
// letting you do that login type
|
||||
this._stepRendererMap = {
|
||||
'm.login.password': this._renderPasswordStep,
|
||||
'm.login.cas': this._renderCasStep,
|
||||
|
||||
// CAS and SSO are the same thing, modulo the url we link to
|
||||
'm.login.cas': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("cas")),
|
||||
'm.login.sso': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("sso")),
|
||||
};
|
||||
|
||||
this._initLoginLogic();
|
||||
|
@ -201,10 +204,6 @@ module.exports = React.createClass({
|
|||
}).done();
|
||||
},
|
||||
|
||||
onCasLogin: function() {
|
||||
this._loginLogic.redirectToCas();
|
||||
},
|
||||
|
||||
_onLoginAsGuestClick: function() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
|
@ -541,10 +540,9 @@ module.exports = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
_renderCasStep: function() {
|
||||
const CasLogin = sdk.getComponent('login.CasLogin');
|
||||
_renderSsoStep: function(url) {
|
||||
return (
|
||||
<CasLogin onSubmit={this.onCasLogin} />
|
||||
<a href={url} className="mx_Login_sso_link">{ _t('Sign in with single sign-on') }</a>
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ module.exports = React.createClass({
|
|||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
|
||||
let {member, fallbackUserId, onClick, viewUserOnClick, ...otherProps} = this.props;
|
||||
let userId = member ? member.userId : fallbackUserId;
|
||||
const userId = member ? member.userId : fallbackUserId;
|
||||
|
||||
if (viewUserOnClick) {
|
||||
onClick = () => {
|
||||
|
|
|
@ -48,7 +48,7 @@ export default class GroupInviteTileContextMenu extends React.Component {
|
|||
Modal.createTrackedDialog('Reject community invite', '', QuestionDialog, {
|
||||
title: _t('Reject invitation'),
|
||||
description: _t('Are you sure you want to reject the invitation?'),
|
||||
onFinished: async (shouldLeave) => {
|
||||
onFinished: async(shouldLeave) => {
|
||||
if (!shouldLeave) return;
|
||||
|
||||
// FIXME: controller shouldn't be loading a view :(
|
||||
|
|
|
@ -31,13 +31,13 @@ export default class ChangelogDialog extends React.Component {
|
|||
componentDidMount() {
|
||||
const version = this.props.newVersion.split('-');
|
||||
const version2 = this.props.version.split('-');
|
||||
if(version == null || version2 == null) return;
|
||||
if (version == null || version2 == null) return;
|
||||
// parse versions of form: [vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]
|
||||
for(let i=0; i<REPOS.length; i++) {
|
||||
for (let i=0; i<REPOS.length; i++) {
|
||||
const oldVersion = version2[2*i];
|
||||
const newVersion = version[2*i];
|
||||
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
|
||||
if(body == null) return;
|
||||
if (body == null) return;
|
||||
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
||||
});
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export default class ChangelogDialog extends React.Component {
|
|||
{this.state[repo].map(this._elementsForCommit)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const content = (
|
||||
|
@ -83,7 +83,7 @@ export default class ChangelogDialog extends React.Component {
|
|||
button={_t("Update")}
|
||||
onFinished={this.props.onFinished}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import Unread from '../../../Unread';
|
|||
import classNames from 'classnames';
|
||||
|
||||
export default class ChatCreateOrReuseDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onFinished = this.onFinished.bind(this);
|
||||
|
|
|
@ -57,7 +57,7 @@ export default React.createClass({
|
|||
let error = null;
|
||||
if (!this.state.groupId) {
|
||||
error = _t("Community IDs cannot be empty.");
|
||||
} else if (!/^[a-z0-9=_\-\.\/]*$/.test(this.state.groupId)) {
|
||||
} else if (!/^[a-z0-9=_\-./]*$/.test(this.state.groupId)) {
|
||||
error = _t("Community IDs may only contain characters a-z, 0-9, or '=_-./'");
|
||||
}
|
||||
this.setState({
|
||||
|
|
71
src/components/views/dialogs/CryptoStoreTooNewDialog.js
Normal file
71
src/components/views/dialogs/CryptoStoreTooNewDialog.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import dis from '../../../dispatcher';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
|
||||
export default (props) => {
|
||||
const _onLogoutClicked = () => {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createTrackedDialog('Logout e2e db too new', '', QuestionDialog, {
|
||||
title: _t("Sign out"),
|
||||
description: _t(
|
||||
"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",
|
||||
),
|
||||
button: _t("Sign out"),
|
||||
focus: false,
|
||||
onFinished: (doLogout) => {
|
||||
if (doLogout) {
|
||||
dis.dispatch({action: 'logout'});
|
||||
props.onFinished();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const description =
|
||||
_t("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. ",
|
||||
{host: props.host},
|
||||
);
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return (<BaseDialog className="mx_CryptoStoreTooNewDialog"
|
||||
contentId='mx_Dialog_content'
|
||||
title={_t("Incompatible Database")}
|
||||
hasCancel={false}
|
||||
onFinished={props.onFinished}
|
||||
>
|
||||
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||
{ description }
|
||||
</div>
|
||||
<DialogButtons primaryButton={_t('Continue With Encryption Disabled')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={props.onFinished}
|
||||
>
|
||||
<button onClick={_onLogoutClicked} >
|
||||
{ _t('Sign out') }
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</BaseDialog>);
|
||||
};
|
|
@ -101,6 +101,9 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
onSubmit: function(ev) {
|
||||
if (this.refs.uiAuth) {
|
||||
this.refs.uiAuth.tryContinue();
|
||||
}
|
||||
this.setState({
|
||||
doingUIAuth: true,
|
||||
});
|
||||
|
@ -217,6 +220,8 @@ export default React.createClass({
|
|||
onAuthFinished={this._onUIAuthFinished}
|
||||
inputs={{}}
|
||||
poll={true}
|
||||
ref="uiAuth"
|
||||
continueIsManaged={true}
|
||||
/>;
|
||||
}
|
||||
const inputClasses = classnames({
|
||||
|
|
460
src/components/views/dialogs/keybackup/CreateKeyBackupDialog.js
Normal file
460
src/components/views/dialogs/keybackup/CreateKeyBackupDialog.js
Normal file
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import sdk from '../../../../index';
|
||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
||||
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import { _t, _td } from '../../../../languageHandler';
|
||||
|
||||
const PHASE_PASSPHRASE = 0;
|
||||
const PHASE_PASSPHRASE_CONFIRM = 1;
|
||||
const PHASE_SHOWKEY = 2;
|
||||
const PHASE_KEEPITSAFE = 3;
|
||||
const PHASE_BACKINGUP = 4;
|
||||
const PHASE_DONE = 5;
|
||||
const PHASE_OPTOUT_CONFIRM = 6;
|
||||
|
||||
// XXX: copied from ShareDialog: factor out into utils
|
||||
function selectText(target) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(target);
|
||||
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks the user through the process of creating an e2e key backup
|
||||
* on the server.
|
||||
*/
|
||||
export default React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase: PHASE_PASSPHRASE,
|
||||
passPhrase: '',
|
||||
passPhraseConfirm: '',
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._recoveryKeyNode = null;
|
||||
this._keyBackupInfo = null;
|
||||
},
|
||||
|
||||
_collectRecoveryKeyNode: function(n) {
|
||||
this._recoveryKeyNode = n;
|
||||
},
|
||||
|
||||
_onCopyClick: function() {
|
||||
selectText(this._recoveryKeyNode);
|
||||
const successful = document.execCommand('copy');
|
||||
if (successful) {
|
||||
this.setState({
|
||||
copied: true,
|
||||
phase: PHASE_KEEPITSAFE,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onDownloadClick: function() {
|
||||
const blob = new Blob([this._keyBackupInfo.recovery_key], {
|
||||
type: 'text/plain;charset=us-ascii',
|
||||
});
|
||||
FileSaver.saveAs(blob, 'recovery-key.txt');
|
||||
|
||||
this.setState({
|
||||
downloaded: true,
|
||||
phase: PHASE_KEEPITSAFE,
|
||||
});
|
||||
},
|
||||
|
||||
_createBackup: function() {
|
||||
this.setState({
|
||||
phase: PHASE_BACKINGUP,
|
||||
error: null,
|
||||
});
|
||||
this._createBackupPromise = MatrixClientPeg.get().createKeyBackupVersion(
|
||||
this._keyBackupInfo,
|
||||
).then((info) => {
|
||||
return MatrixClientPeg.get().backupAllGroupSessions(info.version);
|
||||
}).then(() => {
|
||||
this.setState({
|
||||
phase: PHASE_DONE,
|
||||
});
|
||||
}).catch(e => {
|
||||
console.log("Error creating key backup", e);
|
||||
this.setState({
|
||||
error: e,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_onCancel: function() {
|
||||
this.props.onFinished(false);
|
||||
},
|
||||
|
||||
_onDone: function() {
|
||||
this.props.onFinished(true);
|
||||
},
|
||||
|
||||
_onOptOutClick: function() {
|
||||
this.setState({phase: PHASE_OPTOUT_CONFIRM});
|
||||
},
|
||||
|
||||
_onSetUpClick: function() {
|
||||
this.setState({phase: PHASE_PASSPHRASE});
|
||||
},
|
||||
|
||||
_onSkipPassPhraseClick: async function() {
|
||||
this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion();
|
||||
this.setState({
|
||||
copied: false,
|
||||
phase: PHASE_SHOWKEY,
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseNextClick: function() {
|
||||
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
|
||||
},
|
||||
|
||||
_onPassPhraseKeyPress: function(e) {
|
||||
if (e.key === 'Enter' && this._passPhraseIsValid()) {
|
||||
this._onPassPhraseNextClick();
|
||||
}
|
||||
},
|
||||
|
||||
_onPassPhraseConfirmNextClick: async function() {
|
||||
this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(this.state.passPhrase);
|
||||
this.setState({
|
||||
copied: false,
|
||||
phase: PHASE_SHOWKEY,
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseConfirmKeyPress: function(e) {
|
||||
if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) {
|
||||
this._onPassPhraseConfirmNextClick();
|
||||
}
|
||||
},
|
||||
|
||||
_onSetAgainClick: function() {
|
||||
this.setState({
|
||||
passPhrase: '',
|
||||
passPhraseConfirm: '',
|
||||
phase: PHASE_PASSPHRASE,
|
||||
});
|
||||
},
|
||||
|
||||
_onKeepItSafeGotItClick: function() {
|
||||
this.setState({
|
||||
phase: PHASE_SHOWKEY,
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseChange: function(e) {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseConfirmChange: function(e) {
|
||||
this.setState({
|
||||
passPhraseConfirm: e.target.value,
|
||||
});
|
||||
},
|
||||
|
||||
_passPhraseIsValid: function() {
|
||||
return this.state.passPhrase !== '';
|
||||
},
|
||||
|
||||
_renderPhasePassPhrase: function() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
return <div>
|
||||
<p>{_t("Secure your encrypted message history with a Recovery Passphrase.")}</p>
|
||||
<p>{_t("You'll need it if you log out or lose access to this device.")}</p>
|
||||
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<input type="password"
|
||||
onChange={this._onPassPhraseChange}
|
||||
onKeyPress={this._onPassPhraseKeyPress}
|
||||
value={this.state.passPhrase}
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
placeholder={_t("Enter a passphrase...")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onPassPhraseNextClick}
|
||||
hasCancel={false}
|
||||
disabled={!this._passPhraseIsValid()}
|
||||
/>
|
||||
|
||||
<p>{_t(
|
||||
"If you don't want encrypted message history to be availble on other devices, "+
|
||||
"<button>opt out</button>.",
|
||||
{},
|
||||
{
|
||||
button: sub => <AccessibleButton
|
||||
element="span"
|
||||
className="mx_linkButton"
|
||||
onClick={this._onOptOutClick}
|
||||
>
|
||||
{sub}
|
||||
</AccessibleButton>,
|
||||
},
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"Or, if you don't want to create a Recovery Passphrase, skip this step and "+
|
||||
"<button>download a recovery key</button>.",
|
||||
{},
|
||||
{
|
||||
button: sub => <AccessibleButton
|
||||
element="span"
|
||||
className="mx_linkButton"
|
||||
onClick={this._onSkipPassPhraseClick}
|
||||
>
|
||||
{sub}
|
||||
</AccessibleButton>,
|
||||
},
|
||||
)}</p>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderPhasePassPhraseConfirm: function() {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
let passPhraseMatch = null;
|
||||
if (this.state.passPhraseConfirm.length > 0) {
|
||||
let matchText;
|
||||
if (this.state.passPhraseConfirm === this.state.passPhrase) {
|
||||
matchText = _t("That matches!");
|
||||
} else {
|
||||
matchText = _t("That doesn't match.");
|
||||
}
|
||||
passPhraseMatch = <div className="mx_CreateKeyBackupDialog_passPhraseMatch">
|
||||
<div>{matchText}</div>
|
||||
<div>
|
||||
<AccessibleButton element="span" className="mx_linkButton" onClick={this._onSetAgainClick}>
|
||||
{_t("Go back to set it again.")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t(
|
||||
"Type in your Recovery Passphrase to confirm you remember it. " +
|
||||
"If it helps, add it to your password manager or store it " +
|
||||
"somewhere safe.",
|
||||
)}</p>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
{passPhraseMatch}
|
||||
<div>
|
||||
<input type="password"
|
||||
onChange={this._onPassPhraseConfirmChange}
|
||||
onKeyPress={this._onPassPhraseConfirmKeyPress}
|
||||
value={this.state.passPhraseConfirm}
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
placeholder={_t("Repeat your passphrase...")}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onPassPhraseConfirmNextClick}
|
||||
hasCancel={false}
|
||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderPhaseShowKey: function() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t("Make a copy of this Recovery Key and keep it safe.")}</p>
|
||||
<p>{_t("As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.")}</p>
|
||||
<p className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div>{_t("Your Recovery Key")}</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
|
||||
<button onClick={this._onCopyClick}>
|
||||
{_t("Copy to clipboard")}
|
||||
</button>
|
||||
{
|
||||
// FIXME REDESIGN: buttons should be adjacent but insufficient room in current design
|
||||
}
|
||||
<br /><br />
|
||||
<button onClick={this._onDownloadClick}>
|
||||
{_t("Download")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKey">
|
||||
<code ref={this._collectRecoveryKeyNode}>{this._keyBackupInfo.recovery_key}</code>
|
||||
</div>
|
||||
</p>
|
||||
<br />
|
||||
<DialogButtons primaryButton={_t("I've made a copy")}
|
||||
onPrimaryButtonClick={this._createBackup}
|
||||
hasCancel={false}
|
||||
disabled={!this.state.copied}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderPhaseKeepItSafe: function() {
|
||||
let introText;
|
||||
if (this.state.copied) {
|
||||
introText = _t(
|
||||
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:",
|
||||
{}, {b: s => <b>{s}</b>},
|
||||
);
|
||||
} else if (this.state.downloaded) {
|
||||
introText = _t(
|
||||
"Your Recovery Key is in your <b>Downloads</b> folder.",
|
||||
{}, {b: s => <b>{s}</b>},
|
||||
);
|
||||
}
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
{introText}
|
||||
<ul>
|
||||
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, {b: s => <b>{s}</b>})}</li>
|
||||
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, {b: s => <b>{s}</b>})}</li>
|
||||
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, {b: s => <b>{s}</b>})}</li>
|
||||
</ul>
|
||||
<DialogButtons primaryButton={_t("Got it")}
|
||||
onPrimaryButtonClick={this._onKeepItSafeGotItClick}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderBusyPhase: function(text) {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
return <div>
|
||||
<p>{_t(text)}</p>
|
||||
<Spinner />
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderPhaseDone: function() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t("Backup created")}</p>
|
||||
<p>{_t("Your encryption keys are now being backed up to your Homeserver.")}</p>
|
||||
<DialogButtons primaryButton={_t('Close')}
|
||||
onPrimaryButtonClick={this._onDone}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderPhaseOptOutConfirm: function() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
{_t(
|
||||
"Without setting up Secure Message Recovery, you won't be able to restore your " +
|
||||
"encrypted message history if you log out or use another device.",
|
||||
)}
|
||||
<DialogButtons primaryButton={_t('Set up Secure Message Recovery')}
|
||||
onPrimaryButtonClick={this._onSetUpClick}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button onClick={this._onCancel}>I understand, continue without</button>
|
||||
</DialogButtons>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_titleForPhase: function(phase) {
|
||||
switch (phase) {
|
||||
case PHASE_PASSPHRASE:
|
||||
return _t('Create a Recovery Passphrase');
|
||||
case PHASE_PASSPHRASE_CONFIRM:
|
||||
return _t('Confirm Recovery Passphrase');
|
||||
case PHASE_OPTOUT_CONFIRM:
|
||||
return _t('Warning!');
|
||||
case PHASE_SHOWKEY:
|
||||
return _t('Recovery Key');
|
||||
case PHASE_KEEPITSAFE:
|
||||
return _t('Keep it safe');
|
||||
case PHASE_BACKINGUP:
|
||||
return _t('Backing up...');
|
||||
default:
|
||||
return _t("Create Key Backup");
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
||||
let content;
|
||||
if (this.state.error) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
content = <div>
|
||||
<p>{_t("Unable to create key backup")}</p>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<DialogButtons primaryButton={_t('Retry')}
|
||||
onPrimaryButtonClick={this._createBackup}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
switch (this.state.phase) {
|
||||
case PHASE_PASSPHRASE:
|
||||
content = this._renderPhasePassPhrase();
|
||||
break;
|
||||
case PHASE_PASSPHRASE_CONFIRM:
|
||||
content = this._renderPhasePassPhraseConfirm();
|
||||
break;
|
||||
case PHASE_SHOWKEY:
|
||||
content = this._renderPhaseShowKey();
|
||||
break;
|
||||
case PHASE_KEEPITSAFE:
|
||||
content = this._renderPhaseKeepItSafe();
|
||||
break;
|
||||
case PHASE_BACKINGUP:
|
||||
content = this._renderBusyPhase(_td("Backing up..."));
|
||||
break;
|
||||
case PHASE_DONE:
|
||||
content = this._renderPhaseDone();
|
||||
break;
|
||||
case PHASE_OPTOUT_CONFIRM:
|
||||
content = this._renderPhaseOptOutConfirm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_CreateKeyBackupDialog'
|
||||
onFinished={this.props.onFinished}
|
||||
title={this._titleForPhase(this.state.phase)}
|
||||
hasCancel={[PHASE_DONE].includes(this.state.phase)}
|
||||
>
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
303
src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
Normal file
303
src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import sdk from '../../../../index';
|
||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
||||
import Modal from '../../../../Modal';
|
||||
|
||||
import { _t } from '../../../../languageHandler';
|
||||
|
||||
/**
|
||||
* Dialog for restoring e2e keys from a backup and the user's recovery key
|
||||
*/
|
||||
export default React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
backupInfo: null,
|
||||
loading: false,
|
||||
loadError: null,
|
||||
restoreError: null,
|
||||
recoveryKey: "",
|
||||
recoverInfo: null,
|
||||
recoveryKeyValid: false,
|
||||
forceRecoveryKey: false,
|
||||
passPhrase: '',
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
|
||||
_onCancel: function() {
|
||||
this.props.onFinished(false);
|
||||
},
|
||||
|
||||
_onDone: function() {
|
||||
this.props.onFinished(true);
|
||||
},
|
||||
|
||||
_onUseRecoveryKeyClick: function() {
|
||||
this.setState({
|
||||
forceRecoveryKey: true,
|
||||
});
|
||||
},
|
||||
|
||||
_onResetRecoveryClick: function() {
|
||||
this.props.onFinished(false);
|
||||
const CreateKeyBackupDialog = sdk.getComponent("dialogs.keybackup.CreateKeyBackupDialog");
|
||||
Modal.createTrackedDialog('Create Key Backup', '', CreateKeyBackupDialog, {});
|
||||
},
|
||||
|
||||
_onRecoveryKeyChange: function(e) {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseNext: async function() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
});
|
||||
try {
|
||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword(
|
||||
this.state.passPhrase, undefined, undefined, this.state.backupInfo.version,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
recoverInfo,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error restoring backup", e);
|
||||
this.setState({
|
||||
loading: false,
|
||||
restoreError: e,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onRecoveryKeyNext: async function() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
});
|
||||
try {
|
||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey(
|
||||
this.state.recoveryKey, undefined, undefined, this.state.backupInfo.version,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
recoverInfo,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error restoring backup", e);
|
||||
this.setState({
|
||||
loading: false,
|
||||
restoreError: e,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onPassPhraseChange: function(e) {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
});
|
||||
},
|
||||
|
||||
_onPassPhraseKeyPress: function(e) {
|
||||
if (e.key === "Enter") {
|
||||
this._onPassPhraseNext();
|
||||
}
|
||||
},
|
||||
|
||||
_onRecoveryKeyKeyPress: function(e) {
|
||||
if (e.key === "Enter" && this.state.recoveryKeyValid) {
|
||||
this._onRecoveryKeyNext();
|
||||
}
|
||||
},
|
||||
|
||||
_loadBackupStatus: async function() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
loadError: null,
|
||||
});
|
||||
try {
|
||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||
this.setState({
|
||||
loadError: null,
|
||||
loading: false,
|
||||
backupInfo,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error loading backup status", e);
|
||||
this.setState({
|
||||
loadError: e,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
const backupHasPassphrase = (
|
||||
this.state.backupInfo &&
|
||||
this.state.backupInfo.auth_data &&
|
||||
this.state.backupInfo.auth_data.private_key_salt &&
|
||||
this.state.backupInfo.auth_data.private_key_iterations
|
||||
);
|
||||
|
||||
let content;
|
||||
let title;
|
||||
if (this.state.loading) {
|
||||
title = _t("Loading...");
|
||||
content = <Spinner />;
|
||||
} else if (this.state.loadError) {
|
||||
title = _t("Error");
|
||||
content = _t("Unable to load backup status");
|
||||
} else if (this.state.restoreError) {
|
||||
title = _t("Error");
|
||||
content = _t("Unable to restore backup");
|
||||
} else if (this.state.backupInfo === null) {
|
||||
title = _t("Error");
|
||||
content = _t("No backup found!");
|
||||
} else if (this.state.recoverInfo) {
|
||||
title = _t("Backup Restored");
|
||||
let failedToDecrypt;
|
||||
if (this.state.recoverInfo.total > this.state.recoverInfo.imported) {
|
||||
failedToDecrypt = <p>{_t(
|
||||
"Failed to decrypt %(failedCount)s sessions!",
|
||||
{failedCount: this.state.recoverInfo.total - this.state.recoverInfo.imported},
|
||||
)}</p>;
|
||||
}
|
||||
content = <div>
|
||||
<p>{_t("Restored %(sessionCount)s session keys", {sessionCount: this.state.recoverInfo.imported})}</p>
|
||||
{failedToDecrypt}
|
||||
</div>;
|
||||
} else if (backupHasPassphrase && !this.state.forceRecoveryKey) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
title = _t("Enter Recovery Passphrase");
|
||||
content = <div>
|
||||
{_t(
|
||||
"Access your secure message history and set up secure " +
|
||||
"messaging by entering your recovery passphrase.",
|
||||
)}<br />
|
||||
|
||||
<div 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')}
|
||||
onPrimaryButtonClick={this._onPassPhraseNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
focus={false}
|
||||
/>
|
||||
</div>
|
||||
{_t(
|
||||
"If you've forgotten your recovery passphrase you can "+
|
||||
"<button1>use your recovery key</button1> or " +
|
||||
"<button2>set up new recovery options</button2>"
|
||||
, {}, {
|
||||
button1: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onUseRecoveryKeyClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
button2: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
})}
|
||||
</div>;
|
||||
} else {
|
||||
title = _t("Enter Recovery Key");
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
let keyStatus;
|
||||
if (this.state.recoveryKey.length === 0) {
|
||||
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus"></div>;
|
||||
} else if (this.state.recoveryKeyValid) {
|
||||
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus">
|
||||
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
|
||||
</div>;
|
||||
} else {
|
||||
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t("Not a valid recovery key")}
|
||||
</div>;
|
||||
}
|
||||
|
||||
content = <div>
|
||||
{_t(
|
||||
"Access your secure message history and set up secure " +
|
||||
"messaging by entering your recovery key.",
|
||||
)}<br />
|
||||
|
||||
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
|
||||
<input className="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
onChange={this._onRecoveryKeyChange}
|
||||
onKeyPress={this._onRecoveryKeyKeyPress}
|
||||
value={this.state.recoveryKey}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{keyStatus}
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={!this.state.recoveryKeyValid}
|
||||
/>
|
||||
</div>
|
||||
{_t(
|
||||
"If you've forgotten your recovery passphrase you can "+
|
||||
"<button>set up new recovery options</button>"
|
||||
, {}, {
|
||||
button: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_RestoreKeyBackupDialog'
|
||||
onFinished={this.props.onFinished}
|
||||
title={title}
|
||||
>
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -153,8 +153,8 @@ export default class NetworkDropdown extends React.Component {
|
|||
|
||||
const sortedInstances = this.props.protocols[proto].instances;
|
||||
sortedInstances.sort(function(x, y) {
|
||||
const a = x.desc
|
||||
const b = y.desc
|
||||
const a = x.desc;
|
||||
const b = y.desc;
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a > b) {
|
||||
|
@ -208,7 +208,7 @@ export default class NetworkDropdown extends React.Component {
|
|||
return <div key={key} className="mx_NetworkDropdown_networkoption" onClick={click_handler}>
|
||||
{icon}
|
||||
<span className="mx_NetworkDropdown_menu_network">{name}</span>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -223,11 +223,11 @@ export default class NetworkDropdown extends React.Component {
|
|||
current_value = <input type="text" className="mx_NetworkDropdown_networkoption"
|
||||
ref={this.collectInputTextBox} onKeyUp={this.onInputKeyUp}
|
||||
placeholder="matrix.org" // 'matrix.org' as an example of an HS name
|
||||
/>
|
||||
/>;
|
||||
} else {
|
||||
const instance = instanceForInstanceId(this.props.protocols, this.state.selectedInstanceId);
|
||||
current_value = this._makeMenuOption(
|
||||
this.state.selectedServer, instance, this.state.includeAll, false
|
||||
this.state.selectedServer, instance, this.state.includeAll, false,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -318,6 +318,19 @@ export default class AppTile extends React.Component {
|
|||
}
|
||||
this.setState({deleting: true});
|
||||
|
||||
// HACK: This is a really dirty way to ensure that Jitsi cleans up
|
||||
// its hold on the webcam. Without this, the widget holds a media
|
||||
// stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351
|
||||
if (this.refs.appFrame) {
|
||||
// In practice we could just do `+= ''` to trick the browser
|
||||
// into thinking the URL changed, however I can foresee this
|
||||
// being optimized out by a browser. Instead, we'll just point
|
||||
// the iframe at a page that is reasonably safe to use in the
|
||||
// event the iframe doesn't wink away.
|
||||
// This is relative to where the Riot instance is located.
|
||||
this.refs.appFrame.src = 'about:blank';
|
||||
}
|
||||
|
||||
WidgetUtils.setRoomWidget(
|
||||
this.props.room.roomId,
|
||||
this.props.id,
|
||||
|
|
|
@ -78,7 +78,7 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
let blacklistButton = null, verifyButton = null;
|
||||
let blacklistButton = null; let verifyButton = null;
|
||||
|
||||
if (this.state.device.isBlocked()) {
|
||||
blacklistButton = (
|
||||
|
|
|
@ -43,7 +43,11 @@ module.exports = React.createClass({
|
|||
|
||||
focus: PropTypes.bool,
|
||||
|
||||
// disables the primary and cancel buttons
|
||||
disabled: PropTypes.bool,
|
||||
|
||||
// disables only the primary button
|
||||
primaryDisabled: PropTypes.bool,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -70,15 +74,15 @@ module.exports = React.createClass({
|
|||
}
|
||||
return (
|
||||
<div className="mx_Dialog_buttons">
|
||||
{ cancelButton }
|
||||
{ this.props.children }
|
||||
<button className={primaryButtonClassName}
|
||||
onClick={this.props.onPrimaryButtonClick}
|
||||
autoFocus={this.props.focus}
|
||||
disabled={this.props.disabled}
|
||||
disabled={this.props.disabled || this.props.primaryDisabled}
|
||||
>
|
||||
{ this.props.primaryButton }
|
||||
</button>
|
||||
{ this.props.children }
|
||||
{ cancelButton }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -122,7 +122,6 @@ export default class EditableTextContainer extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EditableTextContainer.propTypes = {
|
||||
|
|
|
@ -16,13 +16,13 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
const MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
|
||||
import {formatDate} from '../../../DateUtils';
|
||||
var filesize = require('filesize');
|
||||
var AccessibleButton = require('../../../components/views/elements/AccessibleButton');
|
||||
const filesize = require('filesize');
|
||||
const AccessibleButton = require('../../../components/views/elements/AccessibleButton');
|
||||
const Modal = require('../../../Modal');
|
||||
const sdk = require('../../../index');
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
@ -69,24 +69,24 @@ module.exports = React.createClass({
|
|||
Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, {
|
||||
onFinished: (proceed) => {
|
||||
if (!proceed) return;
|
||||
var self = this;
|
||||
const self = this;
|
||||
MatrixClientPeg.get().redactEvent(
|
||||
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
|
||||
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(),
|
||||
).catch(function(e) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
// display error message stating you couldn't delete this.
|
||||
var code = e.errcode || e.statusCode;
|
||||
const code = e.errcode || e.statusCode;
|
||||
Modal.createTrackedDialog('You cannot delete this image.', '', ErrorDialog, {
|
||||
title: _t('Error'),
|
||||
description: _t('You cannot delete this image. (%(code)s)', {code: code})
|
||||
description: _t('You cannot delete this image. (%(code)s)', {code: code}),
|
||||
});
|
||||
}).done();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
getName: function () {
|
||||
var name = this.props.name;
|
||||
getName: function() {
|
||||
let name = this.props.name;
|
||||
if (name && this.props.link) {
|
||||
name = <a href={ this.props.link } target="_blank" rel="noopener">{ name }</a>;
|
||||
}
|
||||
|
@ -94,7 +94,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
/*
|
||||
// In theory max-width: 80%, max-height: 80% on the CSS should work
|
||||
// but in practice, it doesn't, so do it manually:
|
||||
|
@ -123,7 +122,7 @@ module.exports = React.createClass({
|
|||
height: displayHeight
|
||||
};
|
||||
*/
|
||||
var style, res;
|
||||
let style; let res;
|
||||
|
||||
if (this.props.width && this.props.height) {
|
||||
style = {
|
||||
|
@ -133,23 +132,22 @@ module.exports = React.createClass({
|
|||
res = style.width + "x" + style.height + "px";
|
||||
}
|
||||
|
||||
var size;
|
||||
let size;
|
||||
if (this.props.fileSize) {
|
||||
size = filesize(this.props.fileSize);
|
||||
}
|
||||
|
||||
var size_res;
|
||||
let size_res;
|
||||
if (size && res) {
|
||||
size_res = size + ", " + res;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
size_res = size || res;
|
||||
}
|
||||
|
||||
var showEventMeta = !!this.props.mxEvent;
|
||||
const showEventMeta = !!this.props.mxEvent;
|
||||
|
||||
var eventMeta;
|
||||
if(showEventMeta) {
|
||||
let eventMeta;
|
||||
if (showEventMeta) {
|
||||
// Figure out the sender, defaulting to mxid
|
||||
let sender = this.props.mxEvent.getSender();
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
|
@ -163,8 +161,8 @@ module.exports = React.createClass({
|
|||
</div>);
|
||||
}
|
||||
|
||||
var eventRedact;
|
||||
if(showEventMeta) {
|
||||
let eventRedact;
|
||||
if (showEventMeta) {
|
||||
eventRedact = (<div className="mx_ImageView_button" onClick={this.onRedactClick}>
|
||||
{ _t('Remove') }
|
||||
</div>);
|
||||
|
@ -175,10 +173,10 @@ module.exports = React.createClass({
|
|||
<div className="mx_ImageView_lhs">
|
||||
</div>
|
||||
<div className="mx_ImageView_content">
|
||||
<img src={this.props.src} style={style}/>
|
||||
<img src={this.props.src} style={style} />
|
||||
<div className="mx_ImageView_labelWrapper">
|
||||
<div className="mx_ImageView_label">
|
||||
<AccessibleButton className="mx_ImageView_cancel" onClick={ this.props.onFinished }><img src="img/cancel-white.svg" width="18" height="18" alt={ _t('Close') }/></AccessibleButton>
|
||||
<AccessibleButton className="mx_ImageView_cancel" onClick={ this.props.onFinished }><img src="img/cancel-white.svg" width="18" height="18" alt={ _t('Close') } /></AccessibleButton>
|
||||
<div className="mx_ImageView_shim">
|
||||
</div>
|
||||
<div className="mx_ImageView_name">
|
||||
|
@ -187,7 +185,7 @@ module.exports = React.createClass({
|
|||
{ eventMeta }
|
||||
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } target="_blank" rel="noopener">
|
||||
<div className="mx_ImageView_download">
|
||||
{ _t('Download this file') }<br/>
|
||||
{ _t('Download this file') }<br />
|
||||
<span className="mx_ImageView_size">{ size_res }</span>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -201,5 +199,5 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -20,14 +20,14 @@ module.exports = React.createClass({
|
|||
displayName: 'InlineSpinner',
|
||||
|
||||
render: function() {
|
||||
var w = this.props.w || 16;
|
||||
var h = this.props.h || 16;
|
||||
var imgClass = this.props.imgClassName || "";
|
||||
const w = this.props.w || 16;
|
||||
const h = this.props.h || 16;
|
||||
const imgClass = this.props.imgClassName || "";
|
||||
|
||||
return (
|
||||
<div className="mx_InlineSpinner">
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -54,7 +54,6 @@ function getOrCreateContainer(containerId) {
|
|||
* bounding rect as the parent of PE.
|
||||
*/
|
||||
export default class PersistedElement extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
// Unique identifier for this PersistedElement instance
|
||||
// Any PersistedElements with the same persistKey will use
|
||||
|
|
|
@ -29,7 +29,7 @@ const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
|||
|
||||
// For URLs of matrix.to links in the timeline which have been reformatted by
|
||||
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
||||
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^\/]*)$/;
|
||||
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
||||
|
||||
const Pill = React.createClass({
|
||||
statics: {
|
||||
|
|
|
@ -16,19 +16,19 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Spinner',
|
||||
|
||||
render: function() {
|
||||
var w = this.props.w || 32;
|
||||
var h = this.props.h || 32;
|
||||
var imgClass = this.props.imgClassName || "";
|
||||
const w = this.props.w || 32;
|
||||
const h = this.props.h || 32;
|
||||
const imgClass = this.props.imgClassName || "";
|
||||
return (
|
||||
<div className="mx_Spinner">
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -20,7 +20,6 @@ import TintableSvg from './TintableSvg';
|
|||
import AccessibleButton from './AccessibleButton';
|
||||
|
||||
export default class TintableSvgButton extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_MatrixToolbar_content">
|
||||
{ _t('You are not receiving desktop notifications') } <a className="mx_MatrixToolbar_link" onClick={ this.onClick }> { _t('Enable them now') }</a>
|
||||
</div>
|
||||
<AccessibleButton className="mx_MatrixToolbar_close" onClick={ this.hideToolbar } ><img src="img/cancel.svg" width="18" height="18" alt={_t('Close')}/></AccessibleButton>
|
||||
<AccessibleButton className="mx_MatrixToolbar_close" onClick={ this.hideToolbar } ><img src="img/cancel.svg" width="18" height="18" alt={_t('Close')} /></AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -45,10 +45,10 @@ export default React.createClass({
|
|||
description: <div className="mx_MatrixToolbar_changelog">{releaseNotes}</div>,
|
||||
button: _t("Update"),
|
||||
onFinished: (update) => {
|
||||
if(update && PlatformPeg.get()) {
|
||||
if (update && PlatformPeg.get()) {
|
||||
PlatformPeg.get().installUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -58,10 +58,10 @@ export default React.createClass({
|
|||
version: this.props.version,
|
||||
newVersion: this.props.newVersion,
|
||||
onFinished: (update) => {
|
||||
if(update && PlatformPeg.get()) {
|
||||
if (update && PlatformPeg.get()) {
|
||||
PlatformPeg.get().installUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -103,5 +103,5 @@ export default React.createClass({
|
|||
{action_button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -32,14 +32,14 @@ export default React.createClass({
|
|||
getDefaultProps: function() {
|
||||
return {
|
||||
detail: '',
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getStatusText: function() {
|
||||
// we can't import the enum from riot-web as we don't want matrix-react-sdk
|
||||
// to depend on riot-web. so we grab it as a normal object via API instead.
|
||||
const updateCheckStatusEnum = PlatformPeg.get().getUpdateCheckStatusEnum();
|
||||
switch(this.props.status) {
|
||||
switch (this.props.status) {
|
||||
case updateCheckStatusEnum.ERROR:
|
||||
return _t('Error encountered (%(errorDetail)s).', { errorDetail: this.props.detail });
|
||||
case updateCheckStatusEnum.CHECKING:
|
||||
|
@ -59,7 +59,7 @@ export default React.createClass({
|
|||
const message = this.getStatusText();
|
||||
const warning = _t('Warning');
|
||||
|
||||
if (!'getUpdateCheckStatusEnum' in PlatformPeg.get()) {
|
||||
if (!('getUpdateCheckStatusEnum' in PlatformPeg.get())) {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
|
@ -83,9 +83,9 @@ export default React.createClass({
|
|||
{message}
|
||||
</div>
|
||||
<AccessibleButton className="mx_MatrixToolbar_close" onClick={this.hideToolbar}>
|
||||
<img src="img/cancel.svg" width="18" height="18" alt={_t('Close')}/>
|
||||
<img src="img/cancel.svg" width="18" height="18" alt={_t('Close')} />
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -165,7 +165,7 @@ export default React.createClass({
|
|||
return (
|
||||
<div className="mx_MemberList">
|
||||
{ inputBox }
|
||||
<GeminiScrollbarWrapper autoshow={true} className="mx_MemberList_outerWrapper">
|
||||
<GeminiScrollbarWrapper autoshow={true}>
|
||||
{ joined }
|
||||
{ invited }
|
||||
</GeminiScrollbarWrapper>
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CasLogin',
|
||||
|
||||
propTypes: {
|
||||
onSubmit: PropTypes.func, // fn()
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.props.onSubmit}>{ _t("Sign in with CAS") }</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
});
|
|
@ -222,6 +222,7 @@ export const TermsAuthEntry = React.createClass({
|
|||
stageParams: PropTypes.object.isRequired,
|
||||
errorText: PropTypes.string,
|
||||
busy: PropTypes.bool,
|
||||
showContinue: PropTypes.bool,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -275,19 +276,30 @@ export const TermsAuthEntry = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_trySubmit: function(policyId) {
|
||||
tryContinue: function() {
|
||||
this._trySubmit();
|
||||
},
|
||||
|
||||
_togglePolicy: function(policyId) {
|
||||
const newToggles = {};
|
||||
let allChecked = true;
|
||||
for (const policy of this.state.policies) {
|
||||
let checked = this.state.toggledPolicies[policy.id];
|
||||
if (policy.id === policyId) checked = !checked;
|
||||
|
||||
newToggles[policy.id] = checked;
|
||||
}
|
||||
this.setState({"toggledPolicies": newToggles});
|
||||
},
|
||||
|
||||
_trySubmit: function() {
|
||||
let allChecked = true;
|
||||
for (const policy of this.state.policies) {
|
||||
let checked = this.state.toggledPolicies[policy.id];
|
||||
allChecked = allChecked && checked;
|
||||
}
|
||||
|
||||
this.setState({"toggledPolicies": newToggles});
|
||||
if (allChecked) this.props.submitAuthDict({type: TermsAuthEntry.LOGIN_TYPE});
|
||||
else this.setState({errorText: _t("Please review and accept all of the homeserver's policies")});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -296,34 +308,42 @@ export const TermsAuthEntry = React.createClass({
|
|||
return <Loader />;
|
||||
}
|
||||
|
||||
let checkboxes = [];
|
||||
const checkboxes = [];
|
||||
let allChecked = true;
|
||||
for (const policy of this.state.policies) {
|
||||
const checked = this.state.toggledPolicies[policy.id];
|
||||
allChecked = allChecked && checked;
|
||||
|
||||
checkboxes.push(
|
||||
<label key={"policy_checkbox_" + policy.id}>
|
||||
<input type="checkbox" onClick={() => this._trySubmit(policy.id)} checked={checked} />
|
||||
<label key={"policy_checkbox_" + policy.id} className="mx_InteractiveAuthEntryComponents_termsPolicy">
|
||||
<input type="checkbox" onClick={() => this._togglePolicy(policy.id)} checked={checked} />
|
||||
<a href={policy.url} target="_blank" rel="noopener">{ policy.name }</a>
|
||||
</label>
|
||||
</label>,
|
||||
);
|
||||
}
|
||||
|
||||
let errorSection;
|
||||
if (this.props.errorText) {
|
||||
if (this.props.errorText || this.state.errorText) {
|
||||
errorSection = (
|
||||
<div className="error" role="alert">
|
||||
{ this.props.errorText }
|
||||
{ this.props.errorText || this.state.errorText }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let submitButton;
|
||||
if (this.props.showContinue !== false) {
|
||||
// XXX: button classes
|
||||
submitButton = <button className="mx_InteractiveAuthEntryComponents_termsSubmit mx_UserSettings_button"
|
||||
onClick={this._trySubmit} disabled={!allChecked}>{_t("Accept")}</button>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{_t("Please review and accept the policies of this homeserver:")}</p>
|
||||
{ checkboxes }
|
||||
{ errorSection }
|
||||
{ submitButton }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -278,6 +278,7 @@ export default class MImageBody extends React.Component {
|
|||
|
||||
let img = null;
|
||||
let placeholder = null;
|
||||
let gifLabel = null;
|
||||
|
||||
// e2e image hasn't been decrypted yet
|
||||
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
||||
|
@ -302,11 +303,14 @@ export default class MImageBody extends React.Component {
|
|||
onMouseLeave={this.onImageLeave} />;
|
||||
}
|
||||
|
||||
if (this._isGif() && !SettingsStore.getValue("autoplayGifsAndVideos") && !this.state.hover) {
|
||||
gifLabel = <p className="mx_MImageBody_gifLabel">GIF</p>;
|
||||
}
|
||||
|
||||
const thumbnail = (
|
||||
<div className="mx_MImageBody_thumbnail_container" style={{ maxHeight: maxHeight + "px" }} >
|
||||
{ /* Calculate aspect ratio, using %padding will size _container correctly */ }
|
||||
<div style={{ paddingBottom: (100 * infoHeight / infoWidth) + '%' }} />
|
||||
|
||||
{ showPlaceholder &&
|
||||
<div className="mx_MImageBody_thumbnail" style={{
|
||||
// Constrain width here so that spinner appears central to the loaded thumbnail
|
||||
|
@ -320,6 +324,7 @@ export default class MImageBody extends React.Component {
|
|||
|
||||
<div style={{display: !showPlaceholder ? undefined : 'none'}}>
|
||||
{ img }
|
||||
{ gifLabel }
|
||||
</div>
|
||||
|
||||
{ this.state.hover && this.getTooltip() }
|
||||
|
|
|
@ -103,7 +103,7 @@ module.exports = React.createClass({
|
|||
oldCanonicalAlias = this.props.canonicalAliasEvent.getContent().alias;
|
||||
}
|
||||
|
||||
let newCanonicalAlias = this.state.canonicalAlias;
|
||||
const newCanonicalAlias = this.state.canonicalAlias;
|
||||
|
||||
if (this.props.canSetCanonicalAlias && oldCanonicalAlias !== newCanonicalAlias) {
|
||||
console.log("AliasSettings: Updating canonical alias");
|
||||
|
@ -167,7 +167,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (!this.props.canonicalAlias) {
|
||||
this.setState({
|
||||
canonicalAlias: alias
|
||||
canonicalAlias: alias,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -220,8 +220,9 @@ module.exports = React.createClass({
|
|||
let canonical_alias_section;
|
||||
if (this.props.canSetCanonicalAlias) {
|
||||
let found = false;
|
||||
const canonicalValue = this.state.canonicalAlias || "";
|
||||
canonical_alias_section = (
|
||||
<select onChange={this.onCanonicalAliasChange} value={this.state.canonicalAlias}>
|
||||
<select onChange={this.onCanonicalAliasChange} value={canonicalValue}>
|
||||
<option value="" key="unset">{ _t('not specified') }</option>
|
||||
{
|
||||
Object.keys(self.state.domainToAliases).map((domain, i) => {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { _t } from '../../../languageHandler';
|
|||
import Modal from '../../../Modal';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
const GROUP_ID_REGEX = /\+\S+\:\S+/;
|
||||
const GROUP_ID_REGEX = /\+\S+:\S+/;
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RelatedGroupSettings',
|
||||
|
|
|
@ -33,7 +33,6 @@ import Autocompleter from '../../../autocomplete/Autocompleter';
|
|||
const COMPOSER_SELECTED = 0;
|
||||
|
||||
export default class Autocomplete extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
|
@ -416,11 +416,10 @@ module.exports = withMatrixClient(React.createClass({
|
|||
onCryptoClicked: function(e) {
|
||||
const event = this.props.mxEvent;
|
||||
|
||||
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '', (cb) => {
|
||||
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb);
|
||||
}, {
|
||||
event: event,
|
||||
});
|
||||
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '',
|
||||
import('../../../async-components/views/dialogs/EncryptedEventDialog'),
|
||||
{event},
|
||||
);
|
||||
},
|
||||
|
||||
onRequestKeysClick: function() {
|
||||
|
|
|
@ -107,7 +107,7 @@ module.exports = React.createClass({
|
|||
|
||||
// FIXME: do we want to factor out all image displaying between this and MImageBody - especially for lightboxing?
|
||||
let image = p["og:image"];
|
||||
let imageMaxWidth = 100, imageMaxHeight = 100;
|
||||
const imageMaxWidth = 100; const imageMaxHeight = 100;
|
||||
if (image && image.startsWith("mxc://")) {
|
||||
image = MatrixClientPeg.get().mxcUrlToHttp(image, imageMaxWidth, imageMaxHeight);
|
||||
}
|
||||
|
|
|
@ -712,7 +712,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
|
||||
if (!member || !member.membership || member.membership === 'leave') {
|
||||
const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId();
|
||||
const onInviteUserButton = async () => {
|
||||
const onInviteUserButton = async() => {
|
||||
try {
|
||||
await cli.invite(roomId, member.userId);
|
||||
} catch (err) {
|
||||
|
|
|
@ -447,7 +447,7 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<div className="mx_MemberList">
|
||||
{ inputBox }
|
||||
<GeminiScrollbarWrapper autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
|
||||
<GeminiScrollbarWrapper autoshow={true} className="mx_MemberList_joined">
|
||||
<TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAtJoined}
|
||||
createOverflowElement={this._createOverflowTileJoined}
|
||||
getChildren={this._getChildrenJoined}
|
||||
|
|
|
@ -269,7 +269,7 @@ export default class MessageComposer extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
let e2eImg, e2eTitle, e2eClass;
|
||||
let e2eImg; let e2eTitle; let e2eClass;
|
||||
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
||||
if (roomIsEncrypted) {
|
||||
// FIXME: show a /!\ if there are untrusted devices in the room...
|
||||
|
@ -429,7 +429,7 @@ export default class MessageComposer extends React.Component {
|
|||
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
|
||||
src="img/icon-text-cancel.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -67,7 +67,7 @@ const EMOJI_UNICODE_TO_SHORTNAME = mapUnicodeToShort();
|
|||
const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + asciiRegexp + ')\\s$');
|
||||
const EMOJI_REGEX = new RegExp(unicodeRegexp, 'g');
|
||||
|
||||
const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000;
|
||||
const TYPING_USER_TIMEOUT = 10000; const TYPING_SERVER_TIMEOUT = 30000;
|
||||
|
||||
const ENTITY_TYPES = {
|
||||
AT_ROOM_PILL: 'ATROOMPILL',
|
||||
|
@ -175,8 +175,8 @@ export default class MessageComposerInput extends React.Component {
|
|||
// see https://github.com/ianstormtaylor/slate/issues/762#issuecomment-304855095
|
||||
this.direction = '';
|
||||
|
||||
this.plainWithMdPills = new PlainWithPillsSerializer({ pillFormat: 'md' });
|
||||
this.plainWithIdPills = new PlainWithPillsSerializer({ pillFormat: 'id' });
|
||||
this.plainWithMdPills = new PlainWithPillsSerializer({ pillFormat: 'md' });
|
||||
this.plainWithIdPills = new PlainWithPillsSerializer({ pillFormat: 'id' });
|
||||
this.plainWithPlainPills = new PlainWithPillsSerializer({ pillFormat: 'plain' });
|
||||
|
||||
this.md = new Md({
|
||||
|
@ -544,7 +544,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
|
||||
if (editorState.startText !== null) {
|
||||
const text = editorState.startText.text;
|
||||
const currentStartOffset = editorState.startOffset;
|
||||
const currentStartOffset = editorState.selection.start.offset;
|
||||
|
||||
// Automatic replacement of plaintext emoji to Unicode emoji
|
||||
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
|
||||
|
@ -558,11 +558,11 @@ export default class MessageComposerInput extends React.Component {
|
|||
|
||||
const range = Range.create({
|
||||
anchor: {
|
||||
key: editorState.selection.startKey,
|
||||
key: editorState.startText.key,
|
||||
offset: currentStartOffset - emojiMatch[1].length - 1,
|
||||
},
|
||||
focus: {
|
||||
key: editorState.selection.startKey,
|
||||
key: editorState.startText.key,
|
||||
offset: currentStartOffset - 1,
|
||||
},
|
||||
});
|
||||
|
@ -573,29 +573,42 @@ export default class MessageComposerInput extends React.Component {
|
|||
}
|
||||
|
||||
// emojioneify any emoji
|
||||
editorState.document.getTexts().forEach(node => {
|
||||
if (node.text !== '' && HtmlUtils.containsEmoji(node.text)) {
|
||||
let match;
|
||||
while ((match = EMOJI_REGEX.exec(node.text)) !== null) {
|
||||
const range = Range.create({
|
||||
anchor: {
|
||||
key: node.key,
|
||||
offset: match.index,
|
||||
},
|
||||
focus: {
|
||||
key: node.key,
|
||||
offset: match.index + match[0].length,
|
||||
},
|
||||
});
|
||||
const inline = Inline.create({
|
||||
type: 'emoji',
|
||||
data: { emojiUnicode: match[0] },
|
||||
});
|
||||
change = change.insertInlineAtRange(range, inline);
|
||||
editorState = change.value;
|
||||
let foundEmoji;
|
||||
do {
|
||||
foundEmoji = false;
|
||||
|
||||
for (const node of editorState.document.getTexts()) {
|
||||
if (node.text !== '' && HtmlUtils.containsEmoji(node.text)) {
|
||||
let match;
|
||||
EMOJI_REGEX.lastIndex = 0;
|
||||
while ((match = EMOJI_REGEX.exec(node.text)) !== null) {
|
||||
const range = Range.create({
|
||||
anchor: {
|
||||
key: node.key,
|
||||
offset: match.index,
|
||||
},
|
||||
focus: {
|
||||
key: node.key,
|
||||
offset: match.index + match[0].length,
|
||||
},
|
||||
});
|
||||
const inline = Inline.create({
|
||||
type: 'emoji',
|
||||
data: { emojiUnicode: match[0] },
|
||||
});
|
||||
change = change.insertInlineAtRange(range, inline);
|
||||
editorState = change.value;
|
||||
|
||||
// if we replaced an emoji, start again looking for more
|
||||
// emoji in the new editor state since doing the replacement
|
||||
// will change the node structure & offsets so we can't compute
|
||||
// insertion ranges from node.key / match.index anymore.
|
||||
foundEmoji = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} while (foundEmoji);
|
||||
|
||||
// work around weird bug where inserting emoji via the macOS
|
||||
// emoji picker can leave the selection stuck in the emoji's
|
||||
|
@ -1065,7 +1078,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
|
||||
// only look for commands if the first block contains simple unformatted text
|
||||
// i.e. no pills or rich-text formatting and begins with a /.
|
||||
let cmd, commandText;
|
||||
let cmd; let commandText;
|
||||
const firstChild = editorState.document.nodes.get(0);
|
||||
const firstGrandChild = firstChild && firstChild.nodes.get(0);
|
||||
if (firstChild && firstGrandChild &&
|
||||
|
@ -1247,7 +1260,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
selectHistory = async (up) => {
|
||||
selectHistory = async(up) => {
|
||||
const delta = up ? -1 : 1;
|
||||
|
||||
// True if we are not currently selecting history, but composing a message
|
||||
|
@ -1295,7 +1308,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
return true;
|
||||
};
|
||||
|
||||
onTab = async (e) => {
|
||||
onTab = async(e) => {
|
||||
this.setState({
|
||||
someCompletions: null,
|
||||
});
|
||||
|
@ -1317,7 +1330,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow();
|
||||
};
|
||||
|
||||
onEscape = async (e) => {
|
||||
onEscape = async(e) => {
|
||||
e.preventDefault();
|
||||
if (this.autocomplete) {
|
||||
this.autocomplete.onEscape(e);
|
||||
|
@ -1336,7 +1349,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
/* If passed null, restores the original editor content from state.originalEditorState.
|
||||
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
|
||||
*/
|
||||
setDisplayedCompletion = async (displayedCompletion: ?Completion): boolean => {
|
||||
setDisplayedCompletion = async(displayedCompletion: ?Completion): boolean => {
|
||||
const activeEditorState = this.state.originalEditorState || this.state.editorState;
|
||||
|
||||
if (displayedCompletion == null) {
|
||||
|
|
|
@ -44,9 +44,13 @@ module.exports = React.createClass({
|
|||
error: PropTypes.object,
|
||||
|
||||
canPreview: PropTypes.bool,
|
||||
spinner: PropTypes.bool,
|
||||
room: PropTypes.object,
|
||||
|
||||
// When a spinner is present, a spinnerState can be specified to indicate the
|
||||
// purpose of the spinner.
|
||||
spinner: PropTypes.bool,
|
||||
spinnerState: PropTypes.oneOf(["joining"]),
|
||||
|
||||
// The alias that was used to access this room, if appropriate
|
||||
// If given, this will be how the room is referred to (eg.
|
||||
// in error messages).
|
||||
|
@ -89,11 +93,16 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
let joinBlock, previewBlock;
|
||||
let joinBlock; let previewBlock;
|
||||
|
||||
if (this.props.spinner || this.state.busy) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
let spinnerIntro = "";
|
||||
if (this.props.spinnerState === "joining") {
|
||||
spinnerIntro = _t("Joining room...");
|
||||
}
|
||||
return (<div className="mx_RoomPreviewBar">
|
||||
<p className="mx_RoomPreviewBar_spinnerIntro">{ spinnerIntro }</p>
|
||||
<Spinner />
|
||||
</div>);
|
||||
}
|
||||
|
|
|
@ -590,6 +590,11 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_openDevtools: function() {
|
||||
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
|
||||
Modal.createDialog(DevtoolsDialog, {roomId: this.props.room.roomId});
|
||||
},
|
||||
|
||||
_renderEncryptionSection: function() {
|
||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||
|
||||
|
@ -652,31 +657,31 @@ module.exports = React.createClass({
|
|||
const userLevels = powerLevels.users || {};
|
||||
|
||||
const powerLevelDescriptors = {
|
||||
users_default: {
|
||||
"users_default": {
|
||||
desc: _t('The default role for new room members is'),
|
||||
defaultValue: 0,
|
||||
},
|
||||
events_default: {
|
||||
"events_default": {
|
||||
desc: _t('To send messages, you must be a'),
|
||||
defaultValue: 0,
|
||||
},
|
||||
invite: {
|
||||
"invite": {
|
||||
desc: _t('To invite users into the room, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
state_default: {
|
||||
"state_default": {
|
||||
desc: _t('To configure the room, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
kick: {
|
||||
"kick": {
|
||||
desc: _t('To kick users, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
ban: {
|
||||
"ban": {
|
||||
desc: _t('To ban users, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
redact: {
|
||||
"redact": {
|
||||
desc: _t('To remove other users\' messages, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
|
@ -942,6 +947,11 @@ module.exports = React.createClass({
|
|||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
const devtoolsButton = SettingsStore.getValue("showDeveloperTools") ?
|
||||
(<AccessibleButton className="mx_RoomSettings_devtoolsButton" onClick={this._openDevtools}>
|
||||
{ _t("Open Devtools") }
|
||||
</AccessibleButton>) : null;
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSettings">
|
||||
|
||||
|
@ -1055,6 +1065,7 @@ module.exports = React.createClass({
|
|||
{ _t('Internal room ID: ') } <code>{ this.props.room.roomId }</code><br />
|
||||
{ _t('Room version number: ') } <code>{ this.props.room.getVersion() }</code><br />
|
||||
{ roomUpgradeButton }
|
||||
{ devtoolsButton }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -179,13 +179,12 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_onExportE2eKeysClicked: function() {
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', 'Change Password', (cb) => {
|
||||
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
|
||||
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
|
||||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
});
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', 'Change Password',
|
||||
import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
|
||||
{
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
onClickChange: function(ev) {
|
||||
|
|
240
src/components/views/settings/KeyBackupPanel.js
Normal file
240
src/components/views/settings/KeyBackupPanel.js
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
|
||||
export default class KeyBackupPanel extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._startNewBackup = this._startNewBackup.bind(this);
|
||||
this._deleteBackup = this._deleteBackup.bind(this);
|
||||
this._verifyDevice = this._verifyDevice.bind(this);
|
||||
this._onKeyBackupStatus = this._onKeyBackupStatus.bind(this);
|
||||
this._restoreBackup = this._restoreBackup.bind(this);
|
||||
|
||||
this._unmounted = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
error: null,
|
||||
backupInfo: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this._loadBackupStatus();
|
||||
|
||||
MatrixClientPeg.get().on('crypto.keyBackupStatus', this._onKeyBackupStatus);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatus);
|
||||
}
|
||||
}
|
||||
|
||||
_onKeyBackupStatus() {
|
||||
this._loadBackupStatus();
|
||||
}
|
||||
|
||||
async _loadBackupStatus() {
|
||||
this.setState({loading: true});
|
||||
try {
|
||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
|
||||
if (this._unmounted) return;
|
||||
this.setState({
|
||||
backupInfo,
|
||||
backupSigStatus,
|
||||
loading: false,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Unable to fetch key backup status", e);
|
||||
if (this._unmounted) return;
|
||||
this.setState({
|
||||
error: e,
|
||||
loading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_startNewBackup() {
|
||||
const CreateKeyBackupDialog = sdk.getComponent('dialogs.keybackup.CreateKeyBackupDialog');
|
||||
Modal.createTrackedDialog('Key Backup', 'Key Backup', CreateKeyBackupDialog, {
|
||||
onFinished: () => {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_deleteBackup() {
|
||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
||||
Modal.createTrackedDialog('Delete Backup', '', QuestionDialog, {
|
||||
title: _t('Delete Backup'),
|
||||
description: _t(
|
||||
"Delete your backed up encryption keys from the server? " +
|
||||
"You will no longer be able to use your recovery key to read encrypted message history",
|
||||
),
|
||||
button: _t('Delete backup'),
|
||||
danger: true,
|
||||
onFinished: (proceed) => {
|
||||
if (!proceed) return;
|
||||
this.setState({loading: true});
|
||||
MatrixClientPeg.get().deleteKeyBackupVersion(this.state.backupInfo.version).then(() => {
|
||||
this._loadBackupStatus();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_restoreBackup() {
|
||||
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
|
||||
Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
|
||||
});
|
||||
}
|
||||
|
||||
_verifyDevice(e) {
|
||||
const device = this.state.backupSigStatus.sigs[e.target.getAttribute('data-sigindex')].device;
|
||||
|
||||
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||
Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
|
||||
userId: MatrixClientPeg.get().credentials.userId,
|
||||
device: device,
|
||||
onFinished: () => {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<div className="error">
|
||||
{_t("Unable to load key backup status")}
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.loading) {
|
||||
return <Spinner />;
|
||||
} else if (this.state.backupInfo) {
|
||||
let clientBackupStatus;
|
||||
if (MatrixClientPeg.get().getKeyBackupEnabled()) {
|
||||
clientBackupStatus = _t("This device is uploading keys to this backup");
|
||||
} else {
|
||||
// XXX: display why and how to fix it
|
||||
clientBackupStatus = _t(
|
||||
"This device is <b>not</b> uploading keys to this backup", {},
|
||||
{b: x => <b>{x}</b>},
|
||||
);
|
||||
}
|
||||
|
||||
let backupSigStatuses = this.state.backupSigStatus.sigs.map((sig, i) => {
|
||||
const sigStatusSubstitutions = {
|
||||
validity: sub =>
|
||||
<span className={sig.valid ? 'mx_KeyBackupPanel_sigValid' : 'mx_KeyBackupPanel_sigInvalid'}>
|
||||
{sub}
|
||||
</span>,
|
||||
verify: sub =>
|
||||
<span className={sig.device.isVerified() ? 'mx_KeyBackupPanel_deviceVerified' : 'mx_KeyBackupPanel_deviceNotVerified'}>
|
||||
{sub}
|
||||
</span>,
|
||||
device: sub => <span className="mx_KeyBackupPanel_deviceName">{sig.device.getDisplayName()}</span>,
|
||||
};
|
||||
let sigStatus;
|
||||
if (sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key()) {
|
||||
sigStatus = _t(
|
||||
"Backup has a <validity>valid</validity> signature from this device",
|
||||
{}, sigStatusSubstitutions,
|
||||
);
|
||||
} else if (sig.valid && sig.device.isVerified()) {
|
||||
sigStatus = _t(
|
||||
"Backup has a <validity>valid</validity> signature from " +
|
||||
"<verify>verified</verify> device <device>x</device>",
|
||||
{}, sigStatusSubstitutions,
|
||||
);
|
||||
} else if (sig.valid && !sig.device.isVerified()) {
|
||||
sigStatus = _t(
|
||||
"Backup has a <validity>valid</validity> signature from " +
|
||||
"<verify>unverified</verify> device <device></device>",
|
||||
{}, sigStatusSubstitutions,
|
||||
);
|
||||
} else if (!sig.valid && sig.device.isVerified()) {
|
||||
sigStatus = _t(
|
||||
"Backup has an <validity>invalid</validity> signature from " +
|
||||
"<verify>verified</verify> device <device></device>",
|
||||
{}, sigStatusSubstitutions,
|
||||
);
|
||||
} else if (!sig.valid && !sig.device.isVerified()) {
|
||||
sigStatus = _t(
|
||||
"Backup has an <validity>invalid</validity> signature from " +
|
||||
"<verify>unverified</verify> device <device></device>",
|
||||
{}, sigStatusSubstitutions,
|
||||
);
|
||||
}
|
||||
|
||||
let verifyButton;
|
||||
if (!sig.device.isVerified()) {
|
||||
verifyButton = <div><br /><AccessibleButton className="mx_UserSettings_button"
|
||||
onClick={this._verifyDevice} data-sigindex={i}>
|
||||
{ _t("Verify...") }
|
||||
</AccessibleButton></div>;
|
||||
}
|
||||
|
||||
return <div key={i}>
|
||||
{sigStatus}
|
||||
{verifyButton}
|
||||
</div>;
|
||||
});
|
||||
if (this.state.backupSigStatus.sigs.length === 0) {
|
||||
backupSigStatuses = _t("Backup is not signed by any of your devices");
|
||||
}
|
||||
|
||||
return <div>
|
||||
{_t("Backup version: ")}{this.state.backupInfo.version}<br />
|
||||
{_t("Algorithm: ")}{this.state.backupInfo.algorithm}<br />
|
||||
{clientBackupStatus}<br />
|
||||
<div>{backupSigStatuses}</div><br />
|
||||
<br />
|
||||
<AccessibleButton className="mx_UserSettings_button"
|
||||
onClick={this._restoreBackup}>
|
||||
{ _t("Restore backup") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_UserSettings_button danger"
|
||||
onClick={this._deleteBackup}>
|
||||
{ _t("Delete backup") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
} else {
|
||||
return <div>
|
||||
{_t("No backup is present")}<br /><br />
|
||||
<AccessibleButton className="mx_UserSettings_button"
|
||||
onClick={this._startNewBackup}>
|
||||
{ _t("Start a new backup") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ module.exports = React.createClass({
|
|||
phases: {
|
||||
LOADING: "LOADING", // The component is loading or sending data to the hs
|
||||
DISPLAY: "DISPLAY", // The component is ready and display data
|
||||
ERROR: "ERROR", // There was an error
|
||||
ERROR: "ERROR", // There was an error
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
|
@ -86,14 +86,14 @@ module.exports = React.createClass({
|
|||
getInitialState: function() {
|
||||
return {
|
||||
phase: this.phases.LOADING,
|
||||
masterPushRule: undefined, // The master rule ('.m.rule.master')
|
||||
vectorPushRules: [], // HS default push rules displayed in Vector UI
|
||||
vectorContentRules: { // Keyword push rules displayed in Vector UI
|
||||
masterPushRule: undefined, // The master rule ('.m.rule.master')
|
||||
vectorPushRules: [], // HS default push rules displayed in Vector UI
|
||||
vectorContentRules: { // Keyword push rules displayed in Vector UI
|
||||
vectorState: PushRuleVectorState.ON,
|
||||
rules: [],
|
||||
},
|
||||
externalPushRules: [], // Push rules (except content rule) that have been defined outside Vector UI
|
||||
externalContentRules: [], // Keyword push rules that have been defined outside Vector UI
|
||||
externalPushRules: [], // Push rules (except content rule) that have been defined outside Vector UI
|
||||
externalContentRules: [], // Keyword push rules that have been defined outside Vector UI
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -290,7 +290,7 @@ module.exports = React.createClass({
|
|||
for (const i in this.state.vectorContentRules.rules) {
|
||||
const rule = this.state.vectorContentRules.rules[i];
|
||||
|
||||
let enabled, actions;
|
||||
let enabled; let actions;
|
||||
switch (newPushRuleVectorState) {
|
||||
case PushRuleVectorState.ON:
|
||||
if (rule.actions.length !== 1) {
|
||||
|
|
|
@ -1288,5 +1288,16 @@
|
|||
"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.": "Преди сте използвали Riot на %(host)s с включено постепенно зареждане на членове. В тази версия, тази настройка е изключена. Понеже локалният кеш не е съвместим при тези две настройки, Riot трябва да синхронизира акаунта Ви наново.",
|
||||
"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.": "Ако другата версия на Riot все още е отворена в друг таб, моля затворете я. Използването на Riot на един адрес във версии с постепенно и без постепенно зареждане ще причини проблеми.",
|
||||
"Incompatible local cache": "Несъвместим локален кеш",
|
||||
"Clear cache and resync": "Изчисти кеша и ресинхронизирай"
|
||||
"Clear cache and resync": "Изчисти кеша и ресинхронизирай",
|
||||
"Please accept all of the policies": "Моля, приемете всички политики",
|
||||
"Please review and accept the policies of this homeserver:": "Моля, прегледайте и приемете политиките на този сървър:",
|
||||
"Add some now": "Добави сега",
|
||||
"Pin unread rooms to the top of the room list": "Закачане на непрочетени стаи най-отгоре в списъка",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Закачане на споменаващи ме стаи най-отгоре в списъка",
|
||||
"Joining room...": "Влизане в стая...",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Вие сте администратор на тази общност. Няма да можете да се присъедините пак без покана от друг администратор.",
|
||||
"Open Devtools": "Отвори инструментите за разработчици",
|
||||
"Show developer tools": "Покажи инструментите за разработчици",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Ако искате да създадете Matrix акаунт, може да се <a>регистрирате</a> тук.",
|
||||
"You are currently using Riot anonymously as a guest.": "В момента използвате Riot анонимно, като гост."
|
||||
}
|
||||
|
|
|
@ -1289,5 +1289,16 @@
|
|||
"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.",
|
||||
"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"
|
||||
"Clear cache and resync": "Zwischenspeicher löschen und erneut synchronisieren",
|
||||
"Please accept all of the policies": "Bitte akzeptiere alle Bedingungen",
|
||||
"Please review and accept the policies of this homeserver:": "Bitte sieh dir alle Bedingungen dieses Heimservers an und akzeptiere sie:",
|
||||
"Pin unread rooms to the top of the room list": "Ungelesene Räume oben an der Raumliste anheften",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Räume mit Erwähnungen oben an der Raumliste anheften",
|
||||
"Joining room...": "Trete Raum bei...",
|
||||
"Add some now": "Füge jetzt hinzu",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Du bist ein Administrator dieser Community. Du wirst nicht erneut hinzutreten können, wenn du nicht von einem anderen Administrator eingeladen wirst.",
|
||||
"Open Devtools": "Öffne Entwickler-Werkzeuge",
|
||||
"Show developer tools": "Zeige Entwickler-Werkzeuge",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Wenn du ein Matrix-Konto erstellen möchtest, kannst du dich jetzt <a>registrieren</a>.",
|
||||
"You are currently using Riot anonymously as a guest.": "Du benutzt aktuell Riot anonym als Gast."
|
||||
}
|
||||
|
|
|
@ -43,10 +43,6 @@
|
|||
"The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads",
|
||||
"Upload Failed": "Upload Failed",
|
||||
"Failure to create room": "Failure to create room",
|
||||
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
|
||||
"Send anyway": "Send anyway",
|
||||
"Send": "Send",
|
||||
"Sun": "Sun",
|
||||
"Mon": "Mon",
|
||||
"Tue": "Tue",
|
||||
|
@ -86,7 +82,8 @@
|
|||
"Failed to invite users to community": "Failed to invite users to community",
|
||||
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
||||
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
||||
"Unnamed Room": "Unnamed Room",
|
||||
"Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.",
|
||||
"Dismiss": "Dismiss",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again",
|
||||
"Unable to enable Notifications": "Unable to enable Notifications",
|
||||
|
@ -211,6 +208,11 @@
|
|||
"%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing",
|
||||
"%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
|
||||
"Failure to create room": "Failure to create room",
|
||||
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
|
||||
"Send anyway": "Send anyway",
|
||||
"Send": "Send",
|
||||
"Unnamed Room": "Unnamed Room",
|
||||
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
|
||||
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
|
||||
"Please <a>contact your service administrator</a> to continue using the service.": "Please <a>contact your service administrator</a> to continue using the service.",
|
||||
|
@ -223,6 +225,7 @@
|
|||
"Failed to join room": "Failed to join room",
|
||||
"Message Pinning": "Message Pinning",
|
||||
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
|
||||
"Backup of encryption keys to server": "Backup of encryption keys to server",
|
||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||
"Use compact timeline layout": "Use compact timeline layout",
|
||||
"Hide removed messages": "Hide removed messages",
|
||||
|
@ -249,8 +252,11 @@
|
|||
"Enable URL previews for this room (only affects you)": "Enable URL previews for this room (only affects you)",
|
||||
"Enable URL previews by default for participants in this room": "Enable URL previews by default for participants in this room",
|
||||
"Room Colour": "Room Colour",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Pin rooms I'm mentioned in to the top of the room list",
|
||||
"Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list",
|
||||
"Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets",
|
||||
"Show empty room list headings": "Show empty room list headings",
|
||||
"Show developer tools": "Show developer tools",
|
||||
"Collecting app version information": "Collecting app version information",
|
||||
"Collecting logs": "Collecting logs",
|
||||
"Uploading report": "Uploading report",
|
||||
|
@ -304,6 +310,24 @@
|
|||
"Failed to set display name": "Failed to set display name",
|
||||
"Disable Notifications": "Disable Notifications",
|
||||
"Enable Notifications": "Enable Notifications",
|
||||
"Delete Backup": "Delete Backup",
|
||||
"Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history": "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history",
|
||||
"Delete backup": "Delete backup",
|
||||
"Unable to load key backup status": "Unable to load key backup status",
|
||||
"This device is uploading keys to this backup": "This device is uploading keys to this backup",
|
||||
"This device is <b>not</b> uploading keys to this backup": "This device is <b>not</b> uploading keys to this backup",
|
||||
"Backup has a <validity>valid</validity> signature from this device": "Backup has a <validity>valid</validity> signature from this device",
|
||||
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> device <device>x</device>": "Backup has a <validity>valid</validity> signature from <verify>verified</verify> device <device>x</device>",
|
||||
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> device <device></device>": "Backup has a <validity>valid</validity> signature from <verify>unverified</verify> device <device></device>",
|
||||
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> device <device></device>": "Backup has an <validity>invalid</validity> signature from <verify>verified</verify> device <device></device>",
|
||||
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>": "Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>",
|
||||
"Verify...": "Verify...",
|
||||
"Backup is not signed by any of your devices": "Backup is not signed by any of your devices",
|
||||
"Backup version: ": "Backup version: ",
|
||||
"Algorithm: ": "Algorithm: ",
|
||||
"Restore backup": "Restore backup",
|
||||
"No backup is present": "No backup is present",
|
||||
"Start a new backup": "Start a new backup",
|
||||
"Error saving email notification preferences": "Error saving email notification preferences",
|
||||
"An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.",
|
||||
"Keywords": "Keywords",
|
||||
|
@ -331,31 +355,6 @@
|
|||
"Off": "Off",
|
||||
"On": "On",
|
||||
"Noisy": "Noisy",
|
||||
"Invalid alias format": "Invalid alias format",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
|
||||
"Invalid address format": "Invalid address format",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address",
|
||||
"not specified": "not specified",
|
||||
"not set": "not set",
|
||||
"Remote addresses for this room:": "Remote addresses for this room:",
|
||||
"Addresses": "Addresses",
|
||||
"The main address for this room is": "The main address for this room is",
|
||||
"Local addresses for this room:": "Local addresses for this room:",
|
||||
"This room has no local addresses": "This room has no local addresses",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)",
|
||||
"Invalid community ID": "Invalid community ID",
|
||||
"'%(groupId)s' is not a valid community ID": "'%(groupId)s' is not a valid community ID",
|
||||
"Flair": "Flair",
|
||||
"Showing flair for these communities:": "Showing flair for these communities:",
|
||||
"This room is not showing flair for any communities": "This room is not showing flair for any communities",
|
||||
"New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)",
|
||||
"You have <a>enabled</a> URL previews by default.": "You have <a>enabled</a> URL previews by default.",
|
||||
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> URL previews by default.",
|
||||
"URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.",
|
||||
"URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.",
|
||||
"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 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.",
|
||||
"URL Previews": "URL Previews",
|
||||
"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.": "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.",
|
||||
"Cannot add any more widgets": "Cannot add any more widgets",
|
||||
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
||||
"Add a widget": "Add a widget",
|
||||
|
@ -419,6 +418,7 @@
|
|||
"Make Moderator": "Make Moderator",
|
||||
"Admin Tools": "Admin Tools",
|
||||
"Level:": "Level:",
|
||||
"Close": "Close",
|
||||
"and %(count)s others...|other": "and %(count)s others...",
|
||||
"and %(count)s others...|one": "and one other...",
|
||||
"Invited": "Invited",
|
||||
|
@ -460,11 +460,11 @@
|
|||
"At this time it is not possible to reply with an emote.": "At this time it is not possible to reply with an emote.",
|
||||
"Markdown is disabled": "Markdown is disabled",
|
||||
"Markdown is enabled": "Markdown is enabled",
|
||||
"Unpin Message": "Unpin Message",
|
||||
"Jump to message": "Jump to message",
|
||||
"No pinned messages.": "No pinned messages.",
|
||||
"Loading...": "Loading...",
|
||||
"Pinned Messages": "Pinned Messages",
|
||||
"Unpin Message": "Unpin Message",
|
||||
"Jump to message": "Jump to message",
|
||||
"%(duration)ss": "%(duration)ss",
|
||||
"%(duration)sm": "%(duration)sm",
|
||||
"%(duration)sh": "%(duration)sh",
|
||||
|
@ -512,6 +512,7 @@
|
|||
"You have no historical rooms": "You have no historical rooms",
|
||||
"Historical": "Historical",
|
||||
"System Alerts": "System Alerts",
|
||||
"Joining room...": "Joining room...",
|
||||
"Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.",
|
||||
"This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:",
|
||||
"You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.",
|
||||
|
@ -574,6 +575,7 @@
|
|||
"Click here to fix": "Click here to fix",
|
||||
"To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, you must be a",
|
||||
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
|
||||
"Open Devtools": "Open Devtools",
|
||||
"Who can access this room?": "Who can access this room?",
|
||||
"Only people who have been invited": "Only people who have been invited",
|
||||
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
|
||||
|
@ -598,13 +600,37 @@
|
|||
"All Rooms": "All Rooms",
|
||||
"Cancel": "Cancel",
|
||||
"You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
|
||||
"Add a stickerpack": "Add a stickerpack",
|
||||
"Add some now": "Add some now",
|
||||
"Stickerpack": "Stickerpack",
|
||||
"Hide Stickers": "Hide Stickers",
|
||||
"Show Stickers": "Show Stickers",
|
||||
"Scroll to unread messages": "Scroll to unread messages",
|
||||
"Jump to first unread message.": "Jump to first unread message.",
|
||||
"Close": "Close",
|
||||
"Invalid alias format": "Invalid alias format",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
|
||||
"Invalid address format": "Invalid address format",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address",
|
||||
"not specified": "not specified",
|
||||
"not set": "not set",
|
||||
"Remote addresses for this room:": "Remote addresses for this room:",
|
||||
"Addresses": "Addresses",
|
||||
"The main address for this room is": "The main address for this room is",
|
||||
"Local addresses for this room:": "Local addresses for this room:",
|
||||
"This room has no local addresses": "This room has no local addresses",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)",
|
||||
"Invalid community ID": "Invalid community ID",
|
||||
"'%(groupId)s' is not a valid community ID": "'%(groupId)s' is not a valid community ID",
|
||||
"Flair": "Flair",
|
||||
"Showing flair for these communities:": "Showing flair for these communities:",
|
||||
"This room is not showing flair for any communities": "This room is not showing flair for any communities",
|
||||
"New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)",
|
||||
"You have <a>enabled</a> URL previews by default.": "You have <a>enabled</a> URL previews by default.",
|
||||
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> URL previews by default.",
|
||||
"URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.",
|
||||
"URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.",
|
||||
"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 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.",
|
||||
"URL Previews": "URL Previews",
|
||||
"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.": "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.",
|
||||
"Sunday": "Sunday",
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
|
@ -635,15 +661,13 @@
|
|||
"Message removed": "Message removed",
|
||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
|
||||
"Sign in with CAS": "Sign in with CAS",
|
||||
"Custom Server Options": "Custom Server Options",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.",
|
||||
"This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.",
|
||||
"You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.",
|
||||
"Dismiss": "Dismiss",
|
||||
"To continue, please enter your password.": "To continue, please enter your password.",
|
||||
"Password:": "Password:",
|
||||
"Please accept all of the policies": "Please accept all of the policies",
|
||||
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
|
||||
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
|
||||
"An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s",
|
||||
"Please check your email to continue registration.": "Please check your email to continue registration.",
|
||||
|
@ -735,7 +759,6 @@
|
|||
"Unblacklist": "Unblacklist",
|
||||
"Blacklist": "Blacklist",
|
||||
"Unverify": "Unverify",
|
||||
"Verify...": "Verify...",
|
||||
"No results": "No results",
|
||||
"Delete": "Delete",
|
||||
"Communities": "Communities",
|
||||
|
@ -847,6 +870,11 @@
|
|||
"Advanced options": "Advanced options",
|
||||
"Block users on other matrix homeservers from joining this room": "Block users on other matrix homeservers from joining this room",
|
||||
"This setting cannot be changed later!": "This setting cannot be changed later!",
|
||||
"Sign out": "Sign out",
|
||||
"To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "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",
|
||||
"You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ",
|
||||
"Incompatible Database": "Incompatible Database",
|
||||
"Continue With Encryption Disabled": "Continue With Encryption Disabled",
|
||||
"Failed to indicate account erasure": "Failed to indicate account erasure",
|
||||
"Unknown error": "Unknown error",
|
||||
"Incorrect password": "Incorrect password",
|
||||
|
@ -886,6 +914,10 @@
|
|||
"Ignore request": "Ignore request",
|
||||
"Loading device info...": "Loading device info...",
|
||||
"Encryption key request": "Encryption key request",
|
||||
"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.": "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.",
|
||||
"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.": "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.",
|
||||
"Incompatible local cache": "Incompatible local cache",
|
||||
"Clear cache and resync": "Clear cache and resync",
|
||||
"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 now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!",
|
||||
"Updating Riot": "Updating Riot",
|
||||
"Failed to upgrade room": "Failed to upgrade room",
|
||||
|
@ -897,7 +929,6 @@
|
|||
"Update any local room aliases to point to the new room": "Update any local room aliases to point to the new room",
|
||||
"Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room",
|
||||
"Put a link back to the old room at the start of the new room so people can see old messages": "Put a link back to the old room at the start of the new room so people can see old messages",
|
||||
"Sign out": "Sign out",
|
||||
"Log out and remove encryption keys?": "Log out and remove encryption keys?",
|
||||
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
|
||||
"Send Logs": "Send Logs",
|
||||
|
@ -942,6 +973,55 @@
|
|||
"Room contains unknown devices": "Room contains unknown devices",
|
||||
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
|
||||
"Unknown devices": "Unknown devices",
|
||||
"Secure your encrypted message history with a Recovery Passphrase.": "Secure your encrypted message history with a Recovery Passphrase.",
|
||||
"You'll need it if you log out or lose access to this device.": "You'll need it if you log out or lose access to this device.",
|
||||
"Enter a passphrase...": "Enter a passphrase...",
|
||||
"Next": "Next",
|
||||
"If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.": "If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.",
|
||||
"Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.": "Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.",
|
||||
"That matches!": "That matches!",
|
||||
"That doesn't match.": "That doesn't match.",
|
||||
"Go back to set it again.": "Go back to set it again.",
|
||||
"Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.": "Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.",
|
||||
"Repeat your passphrase...": "Repeat your passphrase...",
|
||||
"Make a copy of this Recovery Key and keep it safe.": "Make a copy of this Recovery Key and keep it safe.",
|
||||
"As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.",
|
||||
"Your Recovery Key": "Your Recovery Key",
|
||||
"Copy to clipboard": "Copy to clipboard",
|
||||
"Download": "Download",
|
||||
"I've made a copy": "I've made a copy",
|
||||
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:": "Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:",
|
||||
"Your Recovery Key is in your <b>Downloads</b> folder.": "Your Recovery Key is in your <b>Downloads</b> folder.",
|
||||
"<b>Print it</b> and store it somewhere safe": "<b>Print it</b> and store it somewhere safe",
|
||||
"<b>Save it</b> on a USB key or backup drive": "<b>Save it</b> on a USB key or backup drive",
|
||||
"<b>Copy it</b> to your personal cloud storage": "<b>Copy it</b> to your personal cloud storage",
|
||||
"Got it": "Got it",
|
||||
"Backup created": "Backup created",
|
||||
"Your encryption keys are now being backed up to your Homeserver.": "Your encryption keys are now being backed up to your Homeserver.",
|
||||
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.",
|
||||
"Set up Secure Message Recovery": "Set up Secure Message Recovery",
|
||||
"Create a Recovery Passphrase": "Create a Recovery Passphrase",
|
||||
"Confirm Recovery Passphrase": "Confirm Recovery Passphrase",
|
||||
"Recovery Key": "Recovery Key",
|
||||
"Keep it safe": "Keep it safe",
|
||||
"Backing up...": "Backing up...",
|
||||
"Create Key Backup": "Create Key Backup",
|
||||
"Unable to create key backup": "Unable to create key backup",
|
||||
"Retry": "Retry",
|
||||
"Unable to load backup status": "Unable to load backup status",
|
||||
"Unable to restore backup": "Unable to restore backup",
|
||||
"No backup found!": "No backup found!",
|
||||
"Backup Restored": "Backup Restored",
|
||||
"Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!",
|
||||
"Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys",
|
||||
"Enter Recovery Passphrase": "Enter Recovery Passphrase",
|
||||
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Access your secure message history and set up secure messaging by entering your recovery passphrase.",
|
||||
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>",
|
||||
"Enter Recovery Key": "Enter 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 set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
|
||||
"If you've forgotten your recovery passphrase you can <button>set up new recovery options</button>": "If you've forgotten your recovery passphrase you can <button>set up new recovery options</button>",
|
||||
"Private Chat": "Private Chat",
|
||||
"Public Chat": "Public Chat",
|
||||
"Custom": "Custom",
|
||||
|
@ -986,7 +1066,7 @@
|
|||
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
|
||||
"You must join the room to see its files": "You must join the room to see its files",
|
||||
"There are no visible files in this room": "There are no visible files in this room",
|
||||
"<h1>HTML for your community's page</h1>\r\n<p>\r\n Use the long description to introduce new members to the community, or distribute\r\n some important <a href=\"foo\">links</a>\r\n</p>\r\n<p>\r\n You can even use 'img' tags\r\n</p>\r\n": "<h1>HTML for your community's page</h1>\r\n<p>\r\n Use the long description to introduce new members to the community, or distribute\r\n some important <a href=\"foo\">links</a>\r\n</p>\r\n<p>\r\n You can even use 'img' tags\r\n</p>\r\n",
|
||||
"<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n": "<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n",
|
||||
"Add rooms to the community summary": "Add rooms to the community summary",
|
||||
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
|
||||
"Add to summary": "Add to summary",
|
||||
|
@ -1004,6 +1084,7 @@
|
|||
"Failed to update community": "Failed to update community",
|
||||
"Unable to accept invite": "Unable to accept invite",
|
||||
"Unable to join community": "Unable to join community",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.",
|
||||
"Leave Community": "Leave Community",
|
||||
"Leave %(groupName)s?": "Leave %(groupName)s?",
|
||||
"Unable to leave community": "Unable to leave community",
|
||||
|
@ -1027,6 +1108,8 @@
|
|||
"This Home server does not support communities": "This Home server does not support communities",
|
||||
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
||||
"Couldn't load home page": "Couldn't load home page",
|
||||
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.",
|
||||
"Login": "Login",
|
||||
"Failed to reject invitation": "Failed to reject invitation",
|
||||
"This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.",
|
||||
|
@ -1135,6 +1218,7 @@
|
|||
"Autocomplete Delay (ms):": "Autocomplete Delay (ms):",
|
||||
"<not supported>": "<not supported>",
|
||||
"Import E2E room keys": "Import E2E room keys",
|
||||
"Key Backup": "Key Backup",
|
||||
"Cryptography": "Cryptography",
|
||||
"Device ID:": "Device ID:",
|
||||
"Device key:": "Device key:",
|
||||
|
@ -1211,6 +1295,7 @@
|
|||
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",
|
||||
"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.": "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.",
|
||||
"Sign in with single sign-on": "Sign in with single sign-on",
|
||||
"Try the app first": "Try the app first",
|
||||
"Sign in to get started": "Sign in to get started",
|
||||
"Failed to fetch avatar URL": "Failed to fetch avatar URL",
|
||||
|
@ -1265,10 +1350,5 @@
|
|||
"Import": "Import",
|
||||
"Failed to set direct chat tag": "Failed to set direct chat tag",
|
||||
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
||||
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
|
||||
"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.": "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.",
|
||||
"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.": "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.",
|
||||
"Incompatible local cache": "Incompatible local cache",
|
||||
"Clear cache and resync": "Clear cache and resync",
|
||||
"Add some now": "Add some now"
|
||||
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"
|
||||
}
|
||||
|
|
|
@ -567,7 +567,7 @@
|
|||
"Active call (%(roomName)s)": "Active call (%(roomName)s)",
|
||||
"Accept": "Accept",
|
||||
"Add": "Add",
|
||||
"Admin Tools": "Admin tools",
|
||||
"Admin Tools": "Admin Tools",
|
||||
"Alias (optional)": "Alias (optional)",
|
||||
"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.": "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.",
|
||||
"<a>Click here</a> to join the discussion!": "<a>Click here</a> to join the discussion!",
|
||||
|
@ -644,7 +644,9 @@
|
|||
"Username not available": "Username not available",
|
||||
"Something went wrong!": "Something went wrong!",
|
||||
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.",
|
||||
"If you already have a Matrix account you can <a>log in</a> instead.": "If you already have a Matrix account you can <a>log in</a> instead.",
|
||||
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
|
||||
"Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions",
|
||||
"Not a valid Riot keyfile": "Not a valid Riot keyfile",
|
||||
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
|
||||
|
@ -828,5 +830,55 @@
|
|||
"Collapse panel": "Collapse panel",
|
||||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
"There are advanced notifications which are not shown here": "There are advanced notifications which are not shown here"
|
||||
"There are advanced notifications which are not shown here": "There are advanced notifications which are not shown here",
|
||||
"The platform you're on": "The platform you're on",
|
||||
"The version of Riot.im": "The version of Riot.im",
|
||||
"Whether or not you're logged in (we don't record your user name)": "Whether or not you're logged in (we don't record your user name)",
|
||||
"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",
|
||||
"Your homeserver's URL": "Your homeserver's URL",
|
||||
"Your identity server's URL": "Your identity server's URL",
|
||||
"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 device resolution": "Your device resolution",
|
||||
"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:",
|
||||
"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.",
|
||||
"Call Failed": "Call Failed",
|
||||
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.",
|
||||
"Review Devices": "Review Devices",
|
||||
"Call Anyway": "Call Anyway",
|
||||
"Answer Anyway": "Answer Anyway",
|
||||
"Call": "Call",
|
||||
"Answer": "Answer",
|
||||
"A conference call could not be started because the intgrations server is not available": "A conference call could not be started because the integrations server is not available",
|
||||
"Call in Progress": "Call in Progress",
|
||||
"A call is currently being placed!": "A call is currently being placed!",
|
||||
"A call is already in progress!": "A call is already in progress!",
|
||||
"Permission Required": "Permission Required",
|
||||
"You do not have permission to start a conference call in this room": "You do not have permission to start a conference call in this room",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
||||
"Who would you like to add to this community?": "Who would you like to add to this community?",
|
||||
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
|
||||
"Invite new community members": "Invite new community members",
|
||||
"Name or matrix ID": "Name or matrix ID",
|
||||
"Invite to Community": "Invite to Community",
|
||||
"Which rooms would you like to add to this community?": "Which rooms would you like to add to this community?",
|
||||
"Show these rooms to non-members on the community page and room list?": "Show these rooms to non-members on the community page and room list?",
|
||||
"Add rooms to the community": "Add rooms to the community",
|
||||
"Room name or alias": "Room name or alias",
|
||||
"Add to community": "Add to community",
|
||||
"Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:",
|
||||
"Failed to invite users to community": "Failed to invite users to community",
|
||||
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
||||
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
||||
"Registration Required": "Registration Required",
|
||||
"You need to register to do this. Would you like to register now?": "You need to register to do this. Would you like to register now?",
|
||||
"Restricted": "Restricted",
|
||||
"Missing roomId.": "Missing roomId.",
|
||||
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
|
||||
"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",
|
||||
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s changed their display name to %(displayName)s."
|
||||
}
|
||||
|
|
|
@ -251,7 +251,7 @@
|
|||
"Encrypted by a verified device": "Ĉifrita de kontrolita aparato",
|
||||
"Encrypted by an unverified device": "Ĉifrita de nekontrolita aparato",
|
||||
"Unencrypted message": "Neĉifrita mesaĝo",
|
||||
"Please select the destination room for this message": "Bonvolu elekti celan ĉambron por ĉi tiu mesaĝo",
|
||||
"Please select the destination room for this message": "Bonvolu elekti celan babilejon por tiu mesaĝo",
|
||||
"Blacklisted": "Senpova legi ĉifritajn mesaĝojn",
|
||||
"Verified": "Kontrolita",
|
||||
"Unverified": "Nekontrolita",
|
||||
|
@ -292,19 +292,19 @@
|
|||
"and %(count)s others...|other": "kaj %(count)s aliaj…",
|
||||
"and %(count)s others...|one": "kaj unu alia…",
|
||||
"Invited": "Invititaj",
|
||||
"Filter room members": "Filtri ĉambranojn",
|
||||
"Filter room members": "Filtri babilejanojn",
|
||||
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (potenco je %(powerLevelNumber)s)",
|
||||
"Attachment": "Kunsendaĵo",
|
||||
"Upload Files": "Alŝuti dosierojn",
|
||||
"Are you sure you want to upload the following files?": "Ĉu vi certe volas alŝuti la jenajn dosierojn?",
|
||||
"Encrypted room": "Ĉifrita ĉambro",
|
||||
"Unencrypted room": "Neĉifrita ĉambro",
|
||||
"Encrypted room": "Ĉifrita babilejo",
|
||||
"Unencrypted room": "Neĉifrita babilejo",
|
||||
"Hangup": "Fini vokon",
|
||||
"Voice call": "Voĉvoko",
|
||||
"Video call": "Vidvoko",
|
||||
"Upload file": "Alŝuti dosieron",
|
||||
"Show Text Formatting Toolbar": "Montri tekstaranĝan breton",
|
||||
"You do not have permission to post to this room": "Mankas al vi permeso afiŝi en la ĉambro",
|
||||
"You do not have permission to post to this room": "Mankas al vi permeso afiŝi en tiu babilejo",
|
||||
"Turn Markdown on": "Ŝalti Marksubon",
|
||||
"Turn Markdown off": "Malŝalti Marksubon",
|
||||
"Hide Text Formatting Toolbar": "Kaŝi tekstaranĝan breton",
|
||||
|
@ -339,7 +339,7 @@
|
|||
"Offline": "Eksterreta",
|
||||
"Unknown": "Nekonata",
|
||||
"Seen by %(userName)s at %(dateTime)s": "Vidita de %(userName)s je %(dateTime)s",
|
||||
"Unnamed room": "Sennoma ĉambro",
|
||||
"Unnamed room": "Sennoma babilejo",
|
||||
"World readable": "Legebla de ĉiuj",
|
||||
"Guests can join": "Gastoj povas aliĝi",
|
||||
"No rooms to show": "Neniuj ĉambroj montreblas",
|
||||
|
@ -347,11 +347,11 @@
|
|||
"Save": "Konservi",
|
||||
"(~%(count)s results)|other": "(~%(count)s rezultoj)",
|
||||
"(~%(count)s results)|one": "(~%(count)s rezulto)",
|
||||
"Join Room": "Aliĝi al ĉambro",
|
||||
"Join Room": "Aliĝi al Babilejo",
|
||||
"Upload avatar": "Alŝuti profilbildon",
|
||||
"Remove avatar": "Forigi profilbildon",
|
||||
"Settings": "Agordoj",
|
||||
"Forget room": "Forgesi ĉambron",
|
||||
"Forget room": "Forgesi babilejon",
|
||||
"Search": "Serĉi",
|
||||
"Show panel": "Montri panelon",
|
||||
"Drop here to favourite": "Demetu tien ĉi por ŝati",
|
||||
|
@ -360,7 +360,7 @@
|
|||
"Drop here to demote": "Demeti tien ĉi por malpligravigi",
|
||||
"Drop here to tag %(section)s": "Demeti tien ĉi por marki %(section)s",
|
||||
"Press <StartChatButton> to start a chat with someone": "Premu <StartChatButton> por komenci babilon kun iu",
|
||||
"You're not in any rooms yet! Press <CreateRoomButton> to make a room or <RoomDirectoryButton> to browse the directory": "Vi ankoraŭ estas en neniu ĉambro! Premu <CreateRoomButton> por krei ĉambron aŭ <RoomDirectoryButton> por esplori la ĉambrujon",
|
||||
"You're not in any rooms yet! Press <CreateRoomButton> to make a room or <RoomDirectoryButton> to browse the directory": "Vi ankoraŭ estas en neniuj Babilejoj! Premu <CreateRoomButton> por krei Babilejon aŭ <RoomDirectoryButton> por esplori la Babilejujon",
|
||||
"Community Invites": "Komunumaj invitoj",
|
||||
"Invites": "Invitoj",
|
||||
"Favourites": "Ŝatataj",
|
||||
|
@ -371,19 +371,19 @@
|
|||
"Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Ne certigeblas, ke la adreso, kien ĉi tiu invito sendiĝis, kongruas kun tiu rilata al via konto.",
|
||||
"This invitation was sent to an email address which is not associated with this account:": "Ĉi tiu invito sendiĝis al retpoŝtadreso, kiu ne rilatas al ĉi tiu konto:",
|
||||
"You may wish to login with a different account, or add this email to this account.": "Vi povas saluti per alia konto, aŭ aldoni ĉi tiun retpoŝtadreson al tiu ĉi konto.",
|
||||
"You have been invited to join this room by %(inviterName)s": "%(inviterName)s vin invitis al ĉi tiu ĉambro",
|
||||
"You have been invited to join this room by %(inviterName)s": "%(inviterName)s vin invitis al ĉi tiu babilejo",
|
||||
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Ĉu vi volas <acceptText>akcepti</acceptText> aŭ <declineText>rifuzi</declineText> ĉi tiun inviton?",
|
||||
"Reason: %(reasonText)s": "Kialo: %(reasonText)s",
|
||||
"Rejoin": "Realiĝi",
|
||||
"You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s vin forpelis de %(roomName)s.",
|
||||
"You have been kicked from this room by %(userName)s.": "%(userName)s vin forpelis de tiu ĉi ĉambro.",
|
||||
"You have been kicked from this room by %(userName)s.": "%(userName)s vin forpelis de tiu babilejo.",
|
||||
"You have been banned from %(roomName)s by %(userName)s.": "%(userName)s vi forbaris de %(roomName)s.",
|
||||
"You have been banned from this room by %(userName)s.": "%(userName)s vin forbaris de tiu ĉi ĉambro.",
|
||||
"This room": "Ĉi tiu ĉambro",
|
||||
"You have been banned from this room by %(userName)s.": "%(userName)s vin forbaris de tiu babilejo.",
|
||||
"This room": "Ĉi tiu babilejo",
|
||||
"%(roomName)s does not exist.": "%(roomName)s ne ekzistas.",
|
||||
"%(roomName)s is not accessible at this time.": "%(roomName)s ne estas atingebla nun.",
|
||||
"You are trying to access %(roomName)s.": "Vi provas atingi %(roomName)s.",
|
||||
"You are trying to access a room.": "Vi provas atingi ĉambron.",
|
||||
"You are trying to access a room.": "Vi provas aliri babilejon.",
|
||||
"<a>Click here</a> to join the discussion!": "<a>Klaku ĉi tie</a> por aliĝi al la diskuto!",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Tio ĉi estas antaŭrigardo al la ĉambro. Ĉambraj interagoj estas malŝaltitaj",
|
||||
"To change the room's avatar, you must be a": "Por ŝanĝi la ĉambran profilbildon, vi devas esti",
|
||||
|
@ -419,7 +419,7 @@
|
|||
"To link to a room it must have <a>an address</a>.": "Por esti ligebla, ĉambro devas havi <a>adreson</a>.",
|
||||
"Guests cannot join this room even if explicitly invited.": "Gastoj ne povas aliĝi ĉi tiun ĉambron eĉ kun malimplica invito.",
|
||||
"Click here to fix": "Klaku ĉi tie por riparo",
|
||||
"Who can access this room?": "Kiu povas atingi ĉi tiun ĉambron?",
|
||||
"Who can access this room?": "Kiu povas aliri ĉi tiun ĉambron?",
|
||||
"Only people who have been invited": "Nur invititaj uzantoj",
|
||||
"Anyone who knows the room's link, apart from guests": "Iu ajn kun la ligilo, krom gastoj",
|
||||
"Anyone who knows the room's link, including guests": "Iu ajn kun la ligilo, inkluzive gastojn",
|
||||
|
@ -453,17 +453,17 @@
|
|||
"not set": "neagordita",
|
||||
"Remote addresses for this room:": "Foraj adresoj de ĉi tiu ĉambro:",
|
||||
"Addresses": "Adresoj",
|
||||
"The main address for this room is": "La ĉefadreso por ĉi tiu ĉambro estas",
|
||||
"Local addresses for this room:": "Lokaj adresoj por ĉi tiu ĉambro:",
|
||||
"This room has no local addresses": "Ĉi tiu ĉambro ne havas lokajn adresojn",
|
||||
"The main address for this room is": "La ĉefadreso por ĉi tiu babilejo estas",
|
||||
"Local addresses for this room:": "Lokaj adresoj por ĉi tiu babilejo:",
|
||||
"This room has no local addresses": "Ĉi tiu babilejo ne havas lokajn adresojn",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "Nova adreso (ekz-e #io:%(localDomain)s)",
|
||||
"Invalid community ID": "Malvalida komunuma identigaĵo",
|
||||
"'%(groupId)s' is not a valid community ID": "‹%(groupId)s› ne estas valida komunuma identigaĵo",
|
||||
"New community ID (e.g. +foo:%(localDomain)s)": "Nova komunuma identigaĵo (ekz-e +io:%(localDomain)s)",
|
||||
"You have <a>enabled</a> URL previews by default.": "Vi <a>ŝaltis</a> implicitajn antaŭrigardojn al retpaĝoj.",
|
||||
"You have <a>disabled</a> URL previews by default.": "Vi <a>malŝaltis</a> implicitajn antaŭrigardojn al retpaĝoj.",
|
||||
"URL previews are enabled by default for participants in this room.": "Antaŭrigardoj al retpaĝoj estas implicite ŝaltitaj por ĉambranoj ĉi tie.",
|
||||
"URL previews are disabled by default for participants in this room.": "Antaŭrigardoj al retpaĝoj estas implicite malŝaltitaj por ĉambranoj ĉi tie.",
|
||||
"URL previews are enabled by default for participants in this room.": "Antaŭrigardoj de URL-oj estas implicite ŝaltitaj por anoj de tiu ĉi babilejo.",
|
||||
"URL previews are disabled by default for participants in this room.": "Antaŭrigardoj de URL-oj estas implicite malŝaltitaj por anoj de tiu ĉi babilejo.",
|
||||
"URL Previews": "Antaŭrigardoj al retpaĝoj",
|
||||
"Error decrypting audio": "Eraro malĉifrante sonon",
|
||||
"Error decrypting attachment": "Eraro malĉifrante kunsendaĵon",
|
||||
|
@ -1110,5 +1110,19 @@
|
|||
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "<requestLink>Redemandi ĉifroŝlosilojn</requestLink> el viaj aliaj aparatoj.",
|
||||
"Encrypting": "Ĉifranta",
|
||||
"Encrypted, not sent": "Ĉifrita, ne sendita",
|
||||
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Se viaj aliaj aparatoj ne havas la ŝlosilon por ĉi tiu mesaĝo, vi ne povos malĉifri ĝin."
|
||||
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Se viaj aliaj aparatoj ne havas la ŝlosilon por ĉi tiu mesaĝo, vi ne povos malĉifri ĝin.",
|
||||
"Permission Required": "Necesas permeso",
|
||||
"Registration Required": "Necesas registriĝo",
|
||||
"You need to register to do this. Would you like to register now?": "Por fari ĉi tion, vi bezonas registriĝi. Ĉu vi volas registriĝi nun?",
|
||||
"Missing roomId.": "Mankas identigilo de la ĉambro.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s aldonis %(addedAddresses)s kiel adresojn por la ĉambro.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s aldonis %(addedAddresses)s kiel adreson por la ĉambro.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s forigis %(removedAddresses)s kiel adresojn por la ĉambro.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s forigis %(removedAddresses)s kiel adreson por la ĉambro.",
|
||||
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s aldonis %(addedAddresses)s kaj forigis %(removedAddresses)s kiel adresojn por la ĉambro.",
|
||||
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s agordis la ĉefan adreson por la ĉambro al %(address)s.",
|
||||
"%(senderName)s removed the main address for this room.": "%(senderName)s forigis la ĉefan adreson de la ĉambro.",
|
||||
"Please <a>contact your service administrator</a> to continue using the service.": "Bonvolu <a>kontakti administranton de la servo</a> por daŭre uzadi la servon.",
|
||||
"Pin unread rooms to the top of the room list": "Fiksi nelegitajn ĉambrojn supre de la listo",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Fiksi ĉambrojn kun mencioj de mia nomo supre de la listo"
|
||||
}
|
||||
|
|
|
@ -1285,5 +1285,20 @@
|
|||
"<h1>HTML for your community's page</h1>\r\n<p>\r\n Use the long description to introduce new members to the community, or distribute\r\n some important <a href=\"foo\">links</a>\r\n</p>\r\n<p>\r\n You can even use 'img' tags\r\n</p>\r\n": "<h1>Zure komunitatearen orriaren HTMLa</h1>\n<p>\n Erabili deskripzio luzea kide berriek komunitatea ezagutu dezaten, edo eman ezagutzera <a href=\"foo\">esteka</a> garrantzitsuak\n</p>\n<p>\n 'img' etiketak erabili ditzakezu ere\n</p>\n",
|
||||
"Submit Debug Logs": "Bidali arazketa egunkariak",
|
||||
"An email address is required to register on this homeserver.": "e-mail helbide bat behar da hasiera-zerbitzari honetan izena emateko.",
|
||||
"A phone number is required to register on this homeserver.": "telefono zenbaki bat behar da hasiera-zerbitzari honetan izena emateko."
|
||||
"A phone number is required to register on this homeserver.": "telefono zenbaki bat behar da hasiera-zerbitzari honetan izena emateko.",
|
||||
"Please accept all of the policies": "Onartu mesedez politika guztiak",
|
||||
"Please review and accept the policies of this homeserver:": "Irakurri eta onartu hasiera zerbitzari honen politikak:",
|
||||
"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.": "Aurretik Riot erabili duzu %(host)s zerbitzarian kideen karga alferra gaituta zenuela. Bertsio honetan karga alferra desgaituta dago. Katxe lokala bi ezarpen hauen artean bateragarria ez denez, Riotek zure kontua berriro sinkronizatu behar du.",
|
||||
"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.": "Rioten beste bertsioa oraindik beste fitxat batean irekita badago, itxi ezazu zerbitzari bera aldi berean karga alferra gaituta eta desgaituta erabiltzeak arazoak sor ditzakeelako.",
|
||||
"Incompatible local cache": "Katxe lokal bateraezina",
|
||||
"Clear cache and resync": "Garbitu katxea eta sinkronizatu berriro",
|
||||
"Add some now": "Gehitu batzuk orain",
|
||||
"Joining room...": "Gelara elkartzen...",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Komunitate honen administratzailea zara. Ezin izango duzu berriro elkartu ez bazaitu beste administratzaile batek gonbidatzen.",
|
||||
"Open Devtools": "Ireki garapen tresnak",
|
||||
"Show developer tools": "Erakutsi garapen tresnak",
|
||||
"Pin unread rooms to the top of the room list": "Finkatu irakurri gabeko gelak gelen zerrendaren goialdean",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Finkatu aipatu nauten gelak gelen zerrendaren goialdean",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Matrix kontu bat sortu nahi baduzu, <a>izena eman</a> dezakezu.",
|
||||
"You are currently using Riot anonymously as a guest.": "Riot anonimoki gonbidatu gisa erabiltzen ari zara."
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
"Devices": "Appareils",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne pourront pas encore déchiffrer l'historique de messages d'avant leur arrivée sur le salon",
|
||||
"Failed to join room": "Échec de l’inscription au salon",
|
||||
"Failed to kick": "Échec de l'exclusion",
|
||||
"Failed to kick": "Échec de l'expulsion",
|
||||
"Failed to leave room": "Échec du départ du salon",
|
||||
"Failed to load timeline position": "Échec du chargement de la position dans l'historique",
|
||||
"Failed to mute user": "Échec de la mise en sourdine de l'utilisateur",
|
||||
|
@ -157,9 +157,9 @@
|
|||
"Join Room": "Rejoindre le salon",
|
||||
"%(targetName)s joined the room.": "%(targetName)s a rejoint le salon.",
|
||||
"Joins room with given alias": "Rejoint le salon avec l'alias renseigné",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a exclu %(targetName)s.",
|
||||
"Kick": "Exclure",
|
||||
"Kicks user with given id": "Exclut l'utilisateur à partir de son identifiant",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a expulsé %(targetName)s.",
|
||||
"Kick": "Expulser",
|
||||
"Kicks user with given id": "Expulse l'utilisateur à partir de son identifiant",
|
||||
"Labs": "Laboratoire",
|
||||
"Leave room": "Quitter le salon",
|
||||
"%(targetName)s left the room.": "%(targetName)s a quitté le salon.",
|
||||
|
@ -592,7 +592,7 @@
|
|||
"Verified": "Vérifié",
|
||||
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Souhaitez-vous <acceptText>accepter</acceptText> ou <declineText>refuser</declineText> cette invitation ?",
|
||||
"You have been banned from %(roomName)s by %(userName)s.": "Vous avez été banni(e) de %(roomName)s par %(userName)s.",
|
||||
"You have been kicked from %(roomName)s by %(userName)s.": "Vous avez été exclu de %(roomName)s par %(userName)s.",
|
||||
"You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s vous a expulsé de %(roomName)s.",
|
||||
"You may wish to login with a different account, or add this email to this account.": "Vous souhaiteriez peut-être vous identifier avec un autre compte, ou ajouter cette e-mail à votre compte.",
|
||||
"Your home server does not support device management.": "Votre serveur d'accueil ne prend pas en charge la gestion d'appareils.",
|
||||
"(~%(count)s results)|one": "(~%(count)s résultat)",
|
||||
|
@ -637,7 +637,7 @@
|
|||
"Define the power level of a user": "Définir le rang d'un utilisateur",
|
||||
"Edit": "Modifier",
|
||||
"Enable automatic language detection for syntax highlighting": "Activer la détection automatique de la langue pour la correction orthographique",
|
||||
"Hide join/leave messages (invites/kicks/bans unaffected)": "Masquer les messages d'arrivée/départ (n'affecte pas les invitations/exclusions/bannissements)",
|
||||
"Hide join/leave messages (invites/kicks/bans unaffected)": "Masquer les messages d'arrivée/départ (n'affecte pas les invitations/expulsions/bannissements)",
|
||||
"Revoke widget access": "Révoquer les accès du widget",
|
||||
"Sets the room topic": "Défini le sujet du salon",
|
||||
"To get started, please pick a username!": "Pour commencer, choisissez un nom d'utilisateur !",
|
||||
|
@ -719,7 +719,7 @@
|
|||
"Guests can join": "Les invités peuvent rejoindre le salon",
|
||||
"To invite users into the room, you must be a": "Pour inviter des utilisateurs dans le salon, vous devez être un(e)",
|
||||
"To configure the room, you must be a": "Pour configurer le salon, vous devez être un(e)",
|
||||
"To kick users, you must be a": "Pour exclure des utilisateurs, vous devez être un(e)",
|
||||
"To kick users, you must be a": "Pour expulser des utilisateurs, vous devez être",
|
||||
"To ban users, you must be a": "Pour bannir des utilisateurs, vous devez être un(e)",
|
||||
"To remove other users' messages, you must be a": "Pour supprimer les messages d'autres utilisateurs, vous devez être un(e)",
|
||||
"To send events of type <eventType/>, you must be a": "Pour envoyer des évènements du type <eventType/>, vous devez être un",
|
||||
|
@ -730,14 +730,14 @@
|
|||
"%(senderName)s sent a video": "%(senderName)s a envoyé une vidéo",
|
||||
"%(senderName)s uploaded a file": "%(senderName)s a transféré un fichier",
|
||||
"Disinvite this user?": "Désinviter l'utilisateur ?",
|
||||
"Kick this user?": "Exclure cet utilisateur ?",
|
||||
"Kick this user?": "Expulser cet utilisateur ?",
|
||||
"Unban this user?": "Révoquer le bannissement de cet utilisateur ?",
|
||||
"Ban this user?": "Bannir cet utilisateur ?",
|
||||
"Drop here to favourite": "Déposer ici pour mettre en favori",
|
||||
"Drop here to tag direct chat": "Déposer ici pour marquer comme conversation directe",
|
||||
"Drop here to restore": "Déposer ici pour restaurer",
|
||||
"Drop here to demote": "Déposer ici pour rétrograder",
|
||||
"You have been kicked from this room by %(userName)s.": "Vous avez été exclu de ce salon par %(userName)s.",
|
||||
"You have been kicked from this room by %(userName)s.": "%(userName)s vous a expulsé de ce salon.",
|
||||
"You have been banned from this room by %(userName)s.": "Vous avez été banni de ce salon par %(userName)s.",
|
||||
"You are trying to access a room.": "Vous essayez d'accéder à un salon.",
|
||||
"Members only (since the point in time of selecting this option)": "Seulement les membres (depuis la sélection de cette option)",
|
||||
|
@ -798,10 +798,10 @@
|
|||
"were unbanned %(count)s times|one": "ont vu leur bannissement révoqué",
|
||||
"was unbanned %(count)s times|other": "a vu son bannissement révoqué %(count)s fois",
|
||||
"was unbanned %(count)s times|one": "a vu son bannissement révoqué",
|
||||
"were kicked %(count)s times|other": "ont été exclus %(count)s fois",
|
||||
"were kicked %(count)s times|one": "ont été exclus",
|
||||
"was kicked %(count)s times|other": "a été exclu %(count)s fois",
|
||||
"was kicked %(count)s times|one": "a été exclu",
|
||||
"were kicked %(count)s times|other": "ont été expulsés %(count)s fois",
|
||||
"were kicked %(count)s times|one": "ont été expulsés",
|
||||
"was kicked %(count)s times|other": "a été expulsé %(count)s fois",
|
||||
"was kicked %(count)s times|one": "a été expulsé",
|
||||
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s ont changé de nom %(count)s fois",
|
||||
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s ont changé de nom",
|
||||
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s a changé de nom %(count)s fois",
|
||||
|
@ -1291,5 +1291,16 @@
|
|||
"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.": "Vous avez utilisé auparavant Riot sur %(host)s avec le chargement différé activé. Dans cette version le chargement différé est désactivé. Comme le cache local n'est pas compatible entre ces deux réglages, Riot doit resynchroniser votre compte.",
|
||||
"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.": "Si l'autre version de Riot est encore ouverte dans un autre onglet, merci de le fermer car l'utilisation de Riot sur le même hôte avec le chargement différé activé et désactivé à la fois causera des problèmes.",
|
||||
"Incompatible local cache": "Cache local incompatible",
|
||||
"Clear cache and resync": "Vider le cache et resynchroniser"
|
||||
"Clear cache and resync": "Vider le cache et resynchroniser",
|
||||
"Please accept all of the policies": "Veuillez accepter toutes les politiques",
|
||||
"Please review and accept the policies of this homeserver:": "Veuillez lire et accepter les politiques de ce serveur d'accueil :",
|
||||
"Add some now": "En ajouter maintenant",
|
||||
"Joining room...": "Adhésion au salon…",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Vous êtes administrateur de cette communauté. Vous ne pourrez pas revenir sans une invitation d'un autre administrateur.",
|
||||
"Open Devtools": "Ouvrir les outils développeur",
|
||||
"Show developer tools": "Afficher les outils de développeur",
|
||||
"Pin unread rooms to the top of the room list": "Épingler les salons non lus en haut de la liste des salons",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Épingler les salons où l'on me mentionne en haut de la liste des salons",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Si vous souhaitez créer un compte Matrix, vous pouvez <a>vous inscrire</a> maintenant.",
|
||||
"You are currently using Riot anonymously as a guest.": "Vous utilisez Riot de façon anonyme en tant qu'invité."
|
||||
}
|
||||
|
|
|
@ -1291,5 +1291,16 @@
|
|||
"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.": "Előzőleg a szoba tagság késleltetett betöltésének engedélyével itt használtad a Riotot: %(host)s. Ebben a verzióban viszont a késleltetett betöltés nem engedélyezett. Mivel a két gyorsítótár nem kompatibilis egymással így Riotnak újra kell szinkronizálnia a fiókot.",
|
||||
"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.": "Ha a másik Riot verzió fut még egy másik fülön, kérlek zárd be, mivel ha ugyanott használod a Riotot bekapcsolt késleltetett betöltéssel és kikapcsolva is akkor problémák adódhatnak.",
|
||||
"Incompatible local cache": "A helyi gyorsítótár nem kompatibilis ezzel a verzióval",
|
||||
"Clear cache and resync": "Gyorsítótár törlése és újraszinkronizálás"
|
||||
"Clear cache and resync": "Gyorsítótár törlése és újraszinkronizálás",
|
||||
"Please accept all of the policies": "Kérlek fogadd el a felhasználói feltételeket",
|
||||
"Please review and accept the policies of this homeserver:": "Kérlek nézd át és fogadd el a Matrix szerver felhasználói feltételeit:",
|
||||
"Add some now": "Adj hozzá párat",
|
||||
"Joining room...": "Belépés a szobába..",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Te vagy ennek a közösségnek az adminisztrátora. Egy másik adminisztrátortól kapott meghívó nélkül nem tudsz majd újra csatlakozni.",
|
||||
"Open Devtools": "Fejlesztői eszközök megnyitása",
|
||||
"Show developer tools": "Fejlesztői eszközök megjelenítése",
|
||||
"Pin unread rooms to the top of the room list": "Nem olvasott üzeneteket tartalmazó szobák a szobalista elejére",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Megemlítéseket tartalmazó szobák a szobalista elejére",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Ha létre szeretnél hozni egy Matrix fiókot most <a>regisztrálhatsz</a>.",
|
||||
"You are currently using Riot anonymously as a guest.": "A Riotot ismeretlen vendégként használod."
|
||||
}
|
||||
|
|
|
@ -1288,5 +1288,14 @@
|
|||
"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.": "Hai usato Riot precedentemente su %(host)s con il caricamento lento dei membri attivato. In questa versione il caricamento lento è disattivato. Dato che la cache locale non è compatibile tra queste due impostazioni, Riot deve risincronizzare il tuo account.",
|
||||
"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.": "Se l'altra versione di Riot è ancora aperta in un'altra scheda, chiudila perchè usare Riot nello stesso host con il caricamento lento sia attivato che disattivato può causare errori.",
|
||||
"Incompatible local cache": "Cache locale non compatibile",
|
||||
"Clear cache and resync": "Svuota cache e risincronizza"
|
||||
"Clear cache and resync": "Svuota cache e risincronizza",
|
||||
"Please accept all of the policies": "Si prega di accettare tutte le condizioni",
|
||||
"Please review and accept the policies of this homeserver:": "Consulta ed accetta le condizioni di questo homeserver:",
|
||||
"Pin unread rooms to the top of the room list": "Fissa le stanze non lette in cima all'elenco stanze",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Fissa le stanze dove sono menzionato in cima all'elenco stanze",
|
||||
"Joining room...": "Ingresso nella stanza...",
|
||||
"Add some now": "Aggiungine ora",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Sei un amministratore di questa comunità. Non potrai rientrare senza un invito da parte di un altro amministratore.",
|
||||
"Open Devtools": "Apri Devtools",
|
||||
"Show developer tools": "Mostra strumenti sviluppatore"
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -872,5 +872,16 @@
|
|||
"You have no historical rooms": "Jūs neturite istorinių kambarių",
|
||||
"Historical": "Istoriniai",
|
||||
"Every page you use in the app": "Kiekvienas puslapis, kurį naudoji programoje",
|
||||
"Call Timeout": "Skambučio laikas baigėsi"
|
||||
"Call Timeout": "Skambučio laikas baigėsi",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s kaip šio kambario adresus pridėjo %(addedAddresses)s.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s kaip šio kambario adresą pridėjo %(addedAddresses)s.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s kaip šio kambario adresus pašalino %(removedAddresses)s.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s kaip šio kambario adresą pašalino %(removedAddresses)s.",
|
||||
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s kaip šio kambario adresus pridėjo %(addedAddresses)s ir pašalino %(removedAddresses)s.",
|
||||
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s nustatė pagrindinį šio kambario adresą į %(address)s.",
|
||||
"%(senderName)s removed the main address for this room.": "%(senderName)s pašalino pagrindinį šio kambario adresą.",
|
||||
"Disinvite": "Atšaukti pakvietimą",
|
||||
"Disinvite this user?": "Atšaukti pakvietimą šiam naudotojui?",
|
||||
"Unknown for %(duration)s": "Nežinoma jau %(duration)s",
|
||||
"(warning: cannot be disabled again!)": "(įspėjimas: nebeįmanoma bus išjungti!)"
|
||||
}
|
||||
|
|
|
@ -143,11 +143,11 @@
|
|||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Nie można nawiązać połączenia z serwerem przy użyciu HTTP podczas korzystania z HTTPS dla bieżącej strony. Użyj HTTPS lub <a>włącz niebezpieczne skrypty</a>.",
|
||||
"Can't load user settings": "Nie można załadować ustawień użytkownika",
|
||||
"Cannot add any more widgets": "Nie można dodać już więcej widżetów",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s zmienił swoje zdjęcie profilowe.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s zmienił poziom mocy %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s zmienił nazwę pokoju na %(roomName)s.",
|
||||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s usunął nazwę pokoju.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s zmienił temat na \"%(topic)s\".",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s zmienił(a) swoje zdjęcie profilowe.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s zmienił(a) poziom mocy %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s zmienił(a) nazwę pokoju na %(roomName)s.",
|
||||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s usunął(-ęła) nazwę pokoju.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s zmienił(a) temat na \"%(topic)s\".",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Zmiany w dostępie do historii będą dotyczyć tylko przyszłych wiadomości w tym pokoju",
|
||||
"Changes your display nickname": "Zmień swój pseudonim",
|
||||
"Changes colour scheme of current room": "Zmień schemat kolorystyczny bieżącego pokoju",
|
||||
|
@ -370,8 +370,8 @@
|
|||
"Rejoin": "Dołącz ponownie",
|
||||
"Remote addresses for this room:": "Adresy zdalne dla tego pokoju:",
|
||||
"Remove Contact Information?": "Usunąć dane kontaktowe?",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s usunął swoją nazwę ekranową (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s usunął swoje zdjęcie profilowe.",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s usunął(-ęła) swoją wyświetlaną nazwę (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s usunął(-ęła) swoje zdjęcie profilowe.",
|
||||
"Remove %(threePid)s?": "Usunąć %(threePid)s?",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s zażądał grupowego połączenia głosowego VoIP.",
|
||||
"Results from DuckDuckGo": "Wyniki z DuckDuckGo",
|
||||
|
@ -548,8 +548,8 @@
|
|||
"code": "kod",
|
||||
"quote": "cytat",
|
||||
"Create": "Utwórz",
|
||||
"Online": "Dostępny",
|
||||
"Offline": "Niedostępny",
|
||||
"Online": "Dostępny(-a)",
|
||||
"Offline": "Niedostępny(-a)",
|
||||
"Add an Integration": "Dodaj integrację",
|
||||
"Token incorrect": "Niepoprawny token",
|
||||
"To link to a room it must have <a>an address</a>.": "Aby móc stworzyć link do pokoju musi on mieć swój <a>adres</a>.",
|
||||
|
@ -629,9 +629,9 @@
|
|||
" (unsupported)": " (niewspierany)",
|
||||
"Idle": "Bezczynny",
|
||||
"Check for update": "Sprawdź aktualizacje",
|
||||
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s zmienił awatar pokoju na <img/>",
|
||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s usunął awatar pokoju.",
|
||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s zmienił awatar %(roomName)s",
|
||||
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s zmienił(a) awatar pokoju na <img/>",
|
||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s usunął(-ęła) awatar pokoju.",
|
||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s zmienił(a) awatar %(roomName)s",
|
||||
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "To będzie twoja nazwa konta na <span></span> serwerze domowym; możesz też wybrać <a>inny serwer</a>.",
|
||||
"If you already have a Matrix account you can <a>log in</a> instead.": "Jeśli już posiadasz konto Matrix możesz się <a>zalogować</a>.",
|
||||
"Not a valid Riot keyfile": "Niepoprawny plik klucza Riot",
|
||||
|
@ -640,7 +640,7 @@
|
|||
"Do you want to set an email address?": "Czy chcesz ustawić adres e-mail?",
|
||||
"To return to your account in future you need to set a password": "By móc powrócić do swojego konta w przyszłości musisz ustawić hasło",
|
||||
"Share without verifying": "Udostępnij bez weryfikacji",
|
||||
"You added a new device '%(displayName)s', which is requesting encryption keys.": "Dodałeś nowe urządzenie '%(displayName)s', które żąda kluczy szyfrujących.",
|
||||
"You added a new device '%(displayName)s', which is requesting encryption keys.": "Dodałeś(-aś) nowe urządzenie '%(displayName)s', które żąda kluczy szyfrujących.",
|
||||
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Twoje niezweryfikowane urządzenie '%(displayName)s' żąda kluczy szyfrujących.",
|
||||
"Encryption key request": "Żądanie klucza szyfrującego",
|
||||
"Autocomplete Delay (ms):": "Opóźnienie autouzupełniania (ms):",
|
||||
|
@ -700,8 +700,8 @@
|
|||
"Ignored user": "Użytkownik ignorowany",
|
||||
"You are now ignoring %(userId)s": "Ignorujesz teraz %(userId)s",
|
||||
"You are no longer ignoring %(userId)s": "Nie ignorujesz już %(userId)s",
|
||||
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s zmienił swoją wyświetlaną nazwę na %(displayName)s.",
|
||||
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s zmienił przypiętą wiadomość dla tego pokoju.",
|
||||
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s zmienił(a) swoją wyświetlaną nazwę na %(displayName)s.",
|
||||
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s zmienił(a) przypiętą wiadomość dla tego pokoju.",
|
||||
"Message Pinning": "Przypinanie wiadomości",
|
||||
"%(names)s and %(count)s others are typing|other": "%(names)s oraz %(count)s innych piszą",
|
||||
"%(names)s and %(count)s others are typing|one": "%(names)s oraz jedna inna osoba piszą",
|
||||
|
@ -775,7 +775,7 @@
|
|||
"Failed to get protocol list from Home Server": "Nie można pobrać listy protokołów z serwera domowego",
|
||||
"You are not receiving desktop notifications": "Nie otrzymujesz powiadomień na pulpit",
|
||||
"Friday": "Piątek",
|
||||
"Update": "Uaktualnienie",
|
||||
"Update": "Zaktualizuj",
|
||||
"What's New": "Co nowego",
|
||||
"Add an email address above to configure email notifications": "Dodaj adres e-mail powyżej, aby skonfigurować powiadomienia e-mailowe",
|
||||
"Expand panel": "Rozwiń panel",
|
||||
|
@ -1146,5 +1146,66 @@
|
|||
"was invited %(count)s times|one": "został zaproszony",
|
||||
"was banned %(count)s times|one": "został zablokowany",
|
||||
"was kicked %(count)s times|one": "został wyrzucony",
|
||||
"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": "Niezależnie od tego, czy używasz trybu Richtext edytora tekstu w formacie RTF",
|
||||
"Call in Progress": "Łączenie w toku",
|
||||
"Permission Required": "Wymagane Pozwolenie",
|
||||
"Registration Required": "Wymagana Rejestracja",
|
||||
"You need to register to do this. Would you like to register now?": "Musisz się zarejestrować, aby to zrobić. Czy chcesz się teraz zarejestrować?",
|
||||
"underlined": "podkreślenie",
|
||||
"deleted": "przekreślenie",
|
||||
"numbered-list": "lista numerowana",
|
||||
"bulleted-list": "wykropkowana lista",
|
||||
"block-quote": "blok cytowany",
|
||||
"A conference call could not be started because the intgrations server is not available": "Połączenie grupowe nie może zostać rozpoczęte, ponieważ serwer jest niedostępny",
|
||||
"A call is currently being placed!": "W tej chwili trwa rozmowa!",
|
||||
"A call is already in progress!": "W tej chwili trwa połączenie!",
|
||||
"You do not have permission to start a conference call in this room": "Nie posiadasz permisji do rozpoczęcia rozmowy grupowej w tym pokoju",
|
||||
"Unignored user": "Nieignorowany użytkownik",
|
||||
"Forces the current outbound group session in an encrypted room to be discarded": "Wymusza odrzucenie bieżącej sesji grupy wychodzącej w zaszyfrowanym pokoju",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s dodał(a) %(addedAddresses)s jako adres tego pokoju.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s dodał(a) %(addedAddresses)s jako adres tego pokoju.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s usunął(-ęła) %(removedAddresses)s jako adres tego pokoju.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s usunął(-ęła) %(removedAddresses)s jako adres tego pokoju.",
|
||||
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s dodał %(addedAddresses)s i %(removedAddresses)s usunął adresy z tego pokoju.",
|
||||
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s ustawił(a) główny adres dla tego pokoju na %(address)s.",
|
||||
"%(senderName)s removed the main address for this room.": "%(senderName)s usunął(-ęła) główny adres tego pokoju.",
|
||||
"This homeserver has hit its Monthly Active User limit.": "Ten serwer osiągnął miesięczny limit aktywnego użytkownika.",
|
||||
"This homeserver has exceeded one of its resource limits.": "Ten serwer przekroczył jeden z limitów.",
|
||||
"Please <a>contact your service administrator</a> to continue using the service.": "Proszę, <a>skontaktuj się z administratorem</a> aby korzystać dalej z funkcji.",
|
||||
"Unable to connect to Homeserver. Retrying...": "Nie można się połączyć z serwerem. Ponawanianie...",
|
||||
"Sorry, your homeserver is too old to participate in this room.": "Przepraszamy, twój serwer jest zbyt stary by wziąć udział w tym pokoju.",
|
||||
"Please contact your homeserver administrator.": "Proszę o kontakt z administratorem serwera.",
|
||||
"Increase performance by only loading room members on first view": "Zwiększ wydajność, ładując tylko członków pokoju w pierwszym widoku",
|
||||
"Enable widget screenshots on supported widgets": "Włącz widżety zrzutów ekranów na obsługiwanych widżetach",
|
||||
"Show empty room list headings": "Pokaż nagłówki z pustym pokojem",
|
||||
"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.": "W zaszyfrowanych pokojach, takich jak ten, podgląd adresów URL jest domyślnie wyłączony, aby upewnić się, że serwer (w którym generowane są podglądy) nie może zbierać informacji o linkach widocznych w tym pokoju.",
|
||||
"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.": "Gdy ktoś umieści URL w wiadomości, można wyświetlić podgląd adresu URL, aby podać więcej informacji o tym łączu, takich jak tytuł, opis i obraz ze strony internetowej.",
|
||||
"This event could not be displayed": "Ten event nie może zostać wyświetlony",
|
||||
"This room has been replaced and is no longer active.": "Ten pokój został zamieniony i nie jest już aktywny.",
|
||||
"The conversation continues here.": "Konwersacja jest kontynuowana tutaj.",
|
||||
"System Alerts": "Alerty systemowe",
|
||||
"You don't currently have any stickerpacks enabled": "Nie masz obecnie włączonych żadnych pakietów naklejek",
|
||||
"Stickerpack": "Pakiet naklejek",
|
||||
"This room is a continuation of another conversation.": "Ten pokój jest kontynuacją innej rozmowy.",
|
||||
"Click here to see older messages.": "Kliknij tutaj, aby zobaczyć starsze wiadomości.",
|
||||
"This homeserver has hit its Monthly Active User limit so <b>some users will not be able to log in</b>.": "Ten serwer osiągnął miesięczny limit aktywnych użytkowników, więc <b>niektórzy użytkownicy nie będą mogli się zalogować</b>.",
|
||||
"This homeserver has exceeded one of its resource limits so <b>some users will not be able to log in</b>.": "Ten serwer przekroczył jeden z limitów, więc <b>niektórzy użytkownicy nie będą mogli się zalogować</b>.",
|
||||
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s dołączyli(-ły) %(count)s razy",
|
||||
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)s dołączył(a) %(count)s razy",
|
||||
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s dołączył(a)",
|
||||
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s wyszli(-ły)",
|
||||
"Internal room ID: ": "Wewnętrzny identyfikator pokoju ",
|
||||
"were invited %(count)s times|one": "zostali(-ły) zaproszeni(-one)",
|
||||
"Show developer tools": "Pokaż narzędzia deweloperskie",
|
||||
"An email address is required to register on this homeserver.": "Adres e-mail jest wymagany do rejestracji na tym serwerze domowym.",
|
||||
"A phone number is required to register on this homeserver.": "Numer telefonu jest wymagany do rejestracji na tym serwerze domowym.",
|
||||
"Updating Riot": "Aktualizowanie Riot",
|
||||
"Submit Debug Logs": "Wyślij dzienniki błędów",
|
||||
"Please <a>contact your service administrator</a> to continue using this service.": "Proszę, <a>skontaktuj się z administratorem</a> aby korzystać dalej z funkcji.",
|
||||
"Only room administrators will see this warning": "Tylko administratorzy pokojów widzą to ostrzeżenie",
|
||||
"Open Devtools": "Otwórz narzędzia deweloperskie",
|
||||
"Clear cache and resync": "Wyczyść pamięć podręczną i zsynchronizuj ponownie",
|
||||
"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 używa teraz 3-5x mniej pamięci, ładując informacje o innych użytkownikach tylko wtedy, gdy jest to konieczne. Poczekaj, aż ponownie zsynchronizujemy się z serwerem!",
|
||||
"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.": "Jeśli inna wersja Riot jest nadal otwarta w innej zakładce, proszę zamknij ją, ponieważ używanie Riot na tym samym komputerze z włączonym i wyłączonym jednocześnie leniwym ładowaniem będzie powodować problemy.",
|
||||
"And %(count)s more...|other": "I %(count)s więcej…"
|
||||
}
|
||||
|
|
1
src/i18n/strings/ro.json
Normal file
1
src/i18n/strings/ro.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -1234,5 +1234,7 @@
|
|||
"Put a link back to the old room at the start of the new room so people can see old messages": "Разместим ссылку на старую комнату, чтобы люди могли видеть старые сообщения",
|
||||
"Please <a>contact your service administrator</a> to continue using this service.": "Пожалуйста, <a>обратитесь к вашему администратору</a>, чтобы продолжить использовать этот сервис.",
|
||||
"Increase performance by only loading room members on first view": "Увеличьте производительность, загрузив только список участников комнаты",
|
||||
"Lazy loading members not supported": "Задержка загрузки элементов не поддерживается"
|
||||
"Lazy loading members not supported": "Задержка загрузки элементов не поддерживается",
|
||||
"Registration Required": "Требуется регистрация",
|
||||
"You need to register to do this. Would you like to register now?": "Необходимо зарегистрироваться. Хотите зарегистрироваться?"
|
||||
}
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
{
|
||||
"This email address is already in use": "Kjo adresë e-mail-i tashmë është në përdorim",
|
||||
"This phone number is already in use": "Ky numër telefoni tashmë është në përdorim",
|
||||
"This email address is already in use": "Kjo adresë email është tashmë në përdorim",
|
||||
"This phone number is already in use": "Ky numër telefoni është tashmë në përdorim",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Vërtetimi i adresës e-mail i pasukseshëm: Sigurohu që ke klikuar lidhjen në e-mail",
|
||||
"The platform you're on": "Platforma që jë duke përdorur",
|
||||
"The platform you're on": "Platforma ku gjendeni",
|
||||
"The version of Riot.im": "Versioni i Riot.im-it",
|
||||
"Whether or not you're logged in (we don't record your user name)": "A je i lajmëruar apo jo (ne nuk do të inçizojmë emrin përdorues tëndë)",
|
||||
"Your language of choice": "Gjuha jote e zgjedhur",
|
||||
"Which officially provided instance you are using, if any": "Cilën instancë zyrtarisht të ofruar je duke përdorur, në rast që je",
|
||||
"Your language of choice": "Gjuha juaj e zgjedhur",
|
||||
"Which officially provided instance you are using, if any": "Cilën instancë të furnizuar zyrtarish po përdorni, në pastë",
|
||||
"Whether or not you're using the Richtext mode of the Rich Text Editor": "A je duke e përdorur mënyrën e tekstit të pasuruar të redaktionuesit të tekstit të pasuruar apo jo",
|
||||
"Your homeserver's URL": "URL-ja e server-it shtëpiak tëndë",
|
||||
"Your identity server's URL": "URL-ja e server-it identiteti tëndë",
|
||||
"Your homeserver's URL": "URL e Shërbyesit tuaj Home",
|
||||
"Your identity server's URL": "URL e shërbyesit tuaj të identiteteve",
|
||||
"Analytics": "Analiza",
|
||||
"The information being sent to us to help make Riot.im better includes:": "Informacionet që dërgohen për t'i ndihmuar Riot.im-it të përmirësohet përmbajnë:",
|
||||
"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 kjo faqe pëmban informacione që mund të të identifikojnë, sikur një dhomë, përdorues apo identifikatues grupi, këto të dhëna do të mënjanohen para se t‘i dërgohën një server-it.",
|
||||
"Call Failed": "Thirrja nuk mundej të realizohet",
|
||||
"Call Failed": "Thirrja Dështoi",
|
||||
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Pajisje të panjohura ndodhen në këtë dhomë: nësë vazhdon pa i vërtetuar, është e mundshme që dikush të jua përgjon thirrjen.",
|
||||
"Review Devices": "Rishiko pajisjet",
|
||||
"Call Anyway": "Thirr prapëseprapë",
|
||||
"Answer Anyway": "Prano prapëseprapë",
|
||||
"Call": "Thirr",
|
||||
"Answer": "Prano",
|
||||
"Call Anyway": "Thirre Sido Qoftë",
|
||||
"Answer Anyway": "Përgjigju Sido Qoftë",
|
||||
"Call": "Thirrje",
|
||||
"Answer": "Përgjigje",
|
||||
"Call Timeout": "Skadim kohe thirrjeje",
|
||||
"The remote side failed to pick up": "Ana e kundërt nuk arriti të pranojë",
|
||||
"The remote side failed to pick up": "Ana e largët dështoi të përgjigjet",
|
||||
"Unable to capture screen": "Ekrani nuk mundi të inçizohej",
|
||||
"Existing Call": "Thirrje aktuale",
|
||||
"You are already in a call.": "Tashmë je në thirrje.",
|
||||
"You are already in a call.": "Jeni tashmë në një thirrje.",
|
||||
"VoIP is unsupported": "VoIP nuk mbulohet",
|
||||
"You cannot place VoIP calls in this browser.": "Thirrjet me VoIP nuk mbulohen nga ky kërkues uebi.",
|
||||
"You cannot place a call with yourself.": "Nuk mund të realizosh thirrje me vetveten.",
|
||||
"You cannot place a call with yourself.": "S’mund të bëni thirrje me vetveten.",
|
||||
"Conference calls are not supported in this client": "Thirrjet konference nuk mbulohen nga ky klienti",
|
||||
"Conference calls are not supported in encrypted rooms": "Thirrjet konference nuk mbulohen në dhoma të shifruara",
|
||||
"Warning!": "Paralajmërim!",
|
||||
"Warning!": "Sinjalizim!",
|
||||
"Conference calling is in development and may not be reliable.": "Thirrja konference ende është në zhvillim dhe mund të jetë e paqëndrueshme.",
|
||||
"Failed to set up conference call": "Thirrja konference nuk mundi të realizohej",
|
||||
"Conference call failed.": "Thirrja konference dështoi.",
|
||||
"The file '%(fileName)s' failed to upload": "Fajli '%(fileName)s' nuk mundi të mbartet",
|
||||
"The file '%(fileName)s' failed to upload": "Dështoi ngarkimi i kartelës '%(fileName)s'",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "Fajli '%(fileName)s' tejkalon kufirin madhësie për mbartje e këtij server-i shtëpiak",
|
||||
"Upload Failed": "Mbartja dështoi",
|
||||
"Upload Failed": "Ngarkimi Dështoi",
|
||||
"Failure to create room": "Dhoma nuk mundi të krijohet",
|
||||
"Server may be unavailable, overloaded, or you hit a bug.": "Server-i është i padisponueshëm, i ngarkuar tej mase, apo ka një gabim.",
|
||||
"Send anyway": "Dërgo prapëseprapë",
|
||||
"Send anyway": "Dërgoje sido qoftë",
|
||||
"Send": "Dërgoje",
|
||||
"Sun": "Die",
|
||||
"Mon": "Hën",
|
||||
|
@ -51,15 +51,15 @@
|
|||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s %(time)s",
|
||||
"Who would you like to add to this community?": "Kë kishe dashur të shtosh në këtë komunitet?",
|
||||
"Who would you like to add to this community?": "Kë do të donit të shtonit te kjo bashkësi?",
|
||||
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Paralajmërim: se cili që e shton në një komunitet do t‘i doket se cilit që e di identifikatuesin e komunitetit",
|
||||
"Invite new community members": "Fto anëtar të ri komuniteti",
|
||||
"Name or matrix ID": "Emri apo identifikatuesi matrix-i",
|
||||
"Invite new community members": "Ftoni anëtarë të rinj bashkësie",
|
||||
"Name or matrix ID": "Emër ose ID matrix-i",
|
||||
"Invite to Community": "Fto në komunitet",
|
||||
"Which rooms would you like to add to this community?": "Cilët dhoma kishe dashur t‘i shtosh në këtë komunitet?",
|
||||
"Show these rooms to non-members on the community page and room list?": "A t‘i duken dhomat joanëtarëvë ne faqën komuniteti si dhe listën dhome?",
|
||||
"Add rooms to the community": "Shto dhoma komunitetit",
|
||||
"Add to community": "Shto në komunitet",
|
||||
"Add rooms to the community": "Shtoni dhoma te bashkësia",
|
||||
"Add to community": "Shtoje te kjo bashkësi",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Shk",
|
||||
"Mar": "Mar",
|
||||
|
@ -77,42 +77,42 @@
|
|||
"Failed to invite users to community": "Përdoruesit nuk mundën të ftohën",
|
||||
"Failed to invite users to %(groupId)s": "Nuk mundën të ftohën përdoruesit në %(groupId)s",
|
||||
"Failed to add the following rooms to %(groupId)s:": "Nuk mundën të shtohen dhomat vijuese në %(groupId)s:",
|
||||
"Unnamed Room": "Dhomë paemër",
|
||||
"Unnamed Room": "Dhomë e Paemërtuar",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot nuk ka lejim të të dergojë lajmërime - të lutem kontrollo rregullimet e kërkuesit ueb tëndë",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot-it nuk i është dhënë leje të dërgojë lajmërime - të lutëm përpjeku serish",
|
||||
"Unable to enable Notifications": "Lajmërimet nuk mundën të lëshohen",
|
||||
"This email address was not found": "Kjo adresë e-mail-i nuk është gjetur",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Adresa e-mail-i yt nuk duket së është lidhur më një indentifikatues matrix në këtë server shtëpiak.",
|
||||
"Default": "Standardi",
|
||||
"Restricted": "Kufizuar",
|
||||
"This email address was not found": "Kjo adresë email s’u gjet",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Adresa juaj email s’duket të jetë e përshoqëruar me një ID Matrix në këtë shërbyes Home.",
|
||||
"Default": "Parazgjedhje",
|
||||
"Restricted": "E kufizuar",
|
||||
"Moderator": "Moderator",
|
||||
"Admin": "Administrator",
|
||||
"Start a chat": "Fillo bisedë",
|
||||
"Who would you like to communicate with?": "Me kë kishe dashur të bisedosh?",
|
||||
"Email, name or matrix ID": "E-mail-i, emri apo identifikatuesi matrix",
|
||||
"Start Chat": "Fillo bisedë",
|
||||
"Invite new room members": "Fto anëtar dhome të rinjë",
|
||||
"Who would you like to add to this room?": "Kë kishe dashur të shtosh në këtë dhomë?",
|
||||
"Send Invites": "Dërgo ftesa",
|
||||
"Admin": "Përgjegjës",
|
||||
"Start a chat": "Nisni një fjalosje",
|
||||
"Who would you like to communicate with?": "Me kë do të donit të komunikonit?",
|
||||
"Email, name or matrix ID": "Email, emër ose ID matrix",
|
||||
"Start Chat": "Filloni Fjalosje",
|
||||
"Invite new room members": "Ftoni anëtarë të rinj dhome",
|
||||
"Who would you like to add to this room?": "Kë do të donit të shtonit te kjo dhomë?",
|
||||
"Send Invites": "Dërgoni Ftesa",
|
||||
"Failed to invite user": "Përdoruesi nuk mundi të ftohej",
|
||||
"Operation failed": "Veprimi dështoi",
|
||||
"Failed to invite": "Nuk mundi të ftohet",
|
||||
"Failed to invite the following users to the %(roomName)s room:": "Përdoruesit vijuesë nuk mundën të ftohen në dhomën %(roomName)s:",
|
||||
"You need to be logged in.": "Duhesh të jesh i lajmëruar.",
|
||||
"You need to be able to invite users to do that.": "Duhesh të kesh aftësinë të ftosh përdorues për të bërë këtë.",
|
||||
"You need to be logged in.": "Lypset të jeni i futur në llogarinë tuaj.",
|
||||
"You need to be able to invite users to do that.": "Që ta bëni këtë, lypset të jeni në gjendje të ftoni përdorues.",
|
||||
"Unable to create widget.": "Widget-i nuk mundi të krijohet.",
|
||||
"Failed to send request.": "Lutja nuk mundi të dërgohej.",
|
||||
"This room is not recognised.": "Kjo dhomë nuk është e njohur.",
|
||||
"This room is not recognised.": "Kjo dhomë s’është e pranuar.",
|
||||
"Power level must be positive integer.": "Niveli fuqie duhet të jetë numër i plotë pozitiv.",
|
||||
"You are not in this room.": "Ti nuk je në këtë dhomë.",
|
||||
"You do not have permission to do that in this room.": "Nuk ke leje të bësh këtë në këtë dhomë.",
|
||||
"Room %(roomId)s not visible": "Dhoma %(roomId)s e padukshme",
|
||||
"Usage": "Përdorimi",
|
||||
"You are not in this room.": "S’gjendeni në këtë dhomë.",
|
||||
"You do not have permission to do that in this room.": "S’keni leje për ta bërë këtë në këtë dhomë.",
|
||||
"Room %(roomId)s not visible": "Dhoma %(roomId)s s’është e dukshme",
|
||||
"Usage": "Përdorim",
|
||||
"/ddg is not a command": "/ddg s'është komandë",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Për të përdorur, thjesht prit derisa të mbushën rezultatat vetëplotësuese dhe pastaj shfletoji.",
|
||||
"Unrecognised room alias:": "Emri i dhomës të panjohur:",
|
||||
"Ignored user": "Përdoruesë të shpërfillur",
|
||||
"You are now ignoring %(userId)s": "Tash %(userId)s shpërfillet prej teje",
|
||||
"Unrecognised room alias:": "Alias dhome jo i pranuar:",
|
||||
"Ignored user": "Përdorues i shpërfillur",
|
||||
"You are now ignoring %(userId)s": "Tani po e shpërfillni %(userId)s",
|
||||
"Unignored user": "Përdorues jo më i shpërfillur",
|
||||
"Fetching third party location failed": "Dështoi prurja e vendndodhjes së palës së tretë",
|
||||
"A new version of Riot is available.": "Ka gati një version të ri Riot-it.",
|
||||
|
@ -269,7 +269,7 @@
|
|||
"Enable audible notifications in web client": "Aktivizoni njoftime audio te klienti web",
|
||||
"Register": "Regjistrohuni",
|
||||
"Off": "Off",
|
||||
"Edit": "Përpunoni",
|
||||
"Edit": "Përpuno",
|
||||
"Riot does not know how to join a room on this network": "Riot-i nuk di si të hyjë në një dhomë në këtë rrjet",
|
||||
"Mentions only": "Vetëm @përmendje",
|
||||
"remove %(name)s from the directory.": "hiqe %(name)s prej drejtorie.",
|
||||
|
@ -287,7 +287,7 @@
|
|||
"%(count)s Members|one": "%(count)s Anëtar",
|
||||
"Developer Tools": "Mjete Zhvilluesi",
|
||||
"View Source": "Shihini Burimin",
|
||||
"Custom Server Options": "Mundësi Vetjake Shërbyesi",
|
||||
"Custom Server Options": "Mundësi Shërbyesi Vetjak",
|
||||
"Event Content": "Lëndë Akti",
|
||||
"Rooms": "Dhoma",
|
||||
"#example": "#shembull",
|
||||
|
@ -295,5 +295,429 @@
|
|||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Me shfletuesin tuaj të tanishëm, pamja dhe ndjesitë nga aplikacioni mund të jenë plotësisht të pasakta, dhe disa nga ose krejt veçoritë të mos funksionojnë. Nëse doni ta provoni sido qoftë, mund të vazhdoni, por mos u ankoni për çfarëdo problemesh që mund të hasni!",
|
||||
"Checking for an update...": "Po kontrollohet për një përditësim…",
|
||||
"There are advanced notifications which are not shown here": "Ka njoftime të thelluara që nuk shfaqen këtu",
|
||||
"Show empty room list headings": "Shfaqi emrat e listave të zbrazëta dhomash"
|
||||
"Show empty room list headings": "Shfaqi emrat e listave të zbrazëta dhomash",
|
||||
"PM": "PM",
|
||||
"AM": "AM",
|
||||
"Room name or alias": "Emër dhome ose alias",
|
||||
"Unknown (user, device) pair:": "Çift (përdorues, pajisje) i panjohur:",
|
||||
"Device already verified!": "Pajisjeje tashmë e verifikuar!",
|
||||
"Verified key": "Kyç i verifikuar",
|
||||
"Unrecognised command:": "Urdhër jo i pranuar: ",
|
||||
"Reason": "Arsye",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s kërkoi një konferencë VoIP.",
|
||||
"VoIP conference started.": "Konferenca VoIP filloi.",
|
||||
"VoIP conference finished.": "Konferenca VoIP përfundoi.",
|
||||
"Someone": "Dikush",
|
||||
"(no answer)": "(s’ka përgjigje)",
|
||||
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës, prej çastit kur janë ftuar.",
|
||||
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës, prej çastit kur morën pjesë.",
|
||||
"%(senderName)s made future room history visible to all room members.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës.",
|
||||
"%(senderName)s made future room history visible to anyone.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për këdo.",
|
||||
"%(displayName)s is typing": "%(displayName)s po shtyp",
|
||||
"Use compact timeline layout": "Përdorni skemë grafike kompakte për rrjedhën kohore",
|
||||
"Always show message timestamps": "Shfaq përherë vula kohore për mesazhet",
|
||||
"Room Colour": "Ngjyrë Dhome",
|
||||
"unknown caller": "thirrës i panjohur",
|
||||
"Decline": "Hidhe poshtë",
|
||||
"Accept": "Pranoje",
|
||||
"Incorrect verification code": "Kod verifikimi i pasaktë",
|
||||
"Enter Code": "Jepni Kod",
|
||||
"Submit": "Parashtroje",
|
||||
"Phone": "Telefon",
|
||||
"Add phone number": "Shtoni numër telefoni",
|
||||
"Add": "Shtojeni",
|
||||
"Upload new:": "Ngarko të re:",
|
||||
"No display name": "S’ka emër shfaqjeje",
|
||||
"New passwords don't match": "Fjalëkalimet e reja s’përputhen",
|
||||
"Passwords can't be empty": "Fjalëkalimet s’mund të jenë të zbrazët",
|
||||
"Export E2E room keys": "Eksporto kyçe dhome E2E",
|
||||
"Do you want to set an email address?": "Doni të caktoni një adresë email?",
|
||||
"Current password": "Fjalëkalimi i tanishëm",
|
||||
"Password": "Fjalëkalim",
|
||||
"New Password": "Fjalëkalim i Ri",
|
||||
"Confirm password": "Ripohoni frazëkalimin",
|
||||
"Change Password": "Ndryshoni Fjalëkalimin",
|
||||
"Authentication": "Mirëfilltësim",
|
||||
"Delete %(count)s devices|one": "Fshije pajisjen",
|
||||
"Device ID": "ID Pajisjeje",
|
||||
"Device Name": "Emër Pajisjeje",
|
||||
"Last seen": "Parë së fundi më",
|
||||
"Select devices": "Përzgjidhni pajisje",
|
||||
"Disable Notifications": "Çaktivizo Njoftimet",
|
||||
"Enable Notifications": "Aktivizo Njoftimet",
|
||||
"Invalid alias format": "Format i pavlefshëm aliasesh",
|
||||
"Invalid address format": "Format i pavlefshëm adresash",
|
||||
"not specified": "e papërcaktuar",
|
||||
"not set": "s’është caktuar",
|
||||
"Remote addresses for this room:": "Adresa të largëta për këtë dhomë:",
|
||||
"Addresses": "Adresa",
|
||||
"The main address for this room is": "Adresa kryesore për këtë dhomë është",
|
||||
"Local addresses for this room:": "Adresa vendore për këtë dhomë:",
|
||||
"This room has no local addresses": "Kjo dhomë s’ka adresë vendore",
|
||||
"Invalid community ID": "ID bashkësie e pavlefshme",
|
||||
"URL Previews": "Paraparje URL-sh",
|
||||
"Add a widget": "Shtoni një widget",
|
||||
"Drop File Here": "Hidheni Kartelën Këtu",
|
||||
"Drop file here to upload": "Hidheni kartelën këtu që të ngarkohet",
|
||||
" (unsupported)": " (e pambuluar)",
|
||||
"%(senderName)s sent an image": "%(senderName)s dërgoi një figurë",
|
||||
"%(senderName)s sent a video": "%(senderName)s dërgoi një video",
|
||||
"Options": "Mundësi",
|
||||
"Key request sent.": "Kërkesa për kyç u dërgua.",
|
||||
"Encrypted by a verified device": "Fshehtëzuar nga një pajisje e verifikuar",
|
||||
"Encrypted by an unverified device": "Fshehtëzuar nga një pajisje e paverifikuar",
|
||||
"Unencrypted message": "Mesazh i pafshehtëzuar",
|
||||
"Please select the destination room for this message": "Ju lutemi, përzgjidhni dhomën vendmbërritje për këtë mesazh",
|
||||
"Blacklisted": "Në Listë të Zezë",
|
||||
"Verified": "E verifikuar",
|
||||
"Unverified": "I paverifikuar",
|
||||
"device id: ": "ID pajisjeje: ",
|
||||
"Kick": "Përzëre",
|
||||
"Kick this user?": "Të përzihet ky përdorues?",
|
||||
"Unban": "Hiqja dëbimin",
|
||||
"Ban": "Dëboje",
|
||||
"Ban this user?": "Të dëbohet ky përdorues?",
|
||||
"Are you sure?": "Jeni i sigurt?",
|
||||
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "S’do të jeni në gjendje ta zhbëni këtë ndryshim, ngaqë po e promovoni përdoruesin të ketë të njëjtën shkallë pushteti si ju vetë.",
|
||||
"No devices with registered encryption keys": "S’ka pajisje me kyçe fshehtëzimi të regjistruar",
|
||||
"Devices": "Pajisje",
|
||||
"Unignore": "Shpërfille",
|
||||
"Ignore": "Shpërfille",
|
||||
"Mention": "Përmendje",
|
||||
"Invite": "Ftoje",
|
||||
"User Options": "Mundësi Përdoruesi",
|
||||
"Direct chats": "Fjalosje të drejtpërdrejta",
|
||||
"Make Moderator": "Kaloje Moderator",
|
||||
"Admin Tools": "Mjete Përgjegjësi",
|
||||
"Level:": "Nivel:",
|
||||
"and %(count)s others...|other": "dhe %{count} të tjerë…",
|
||||
"and %(count)s others...|one": "dhe një tjetër…",
|
||||
"Filter room members": "Filtroni anëtarë dhome",
|
||||
"Attachment": "Bashkëngjitje",
|
||||
"Upload Files": "Ngarkoni Kartela",
|
||||
"Are you sure you want to upload the following files?": "Jeni i sigurt se doni të ngarkohen kartelat vijuese?",
|
||||
"Encrypted room": "Dhomë e fshehtëzuar",
|
||||
"Unencrypted room": "Dhomë e pafshehtëzuar",
|
||||
"Voice call": "Thirrje audio",
|
||||
"Video call": "Thirrje video",
|
||||
"Upload file": "Ngarkoni kartelë",
|
||||
"Show Text Formatting Toolbar": "Shfaq Panel Formatimi Tekstesh",
|
||||
"Send an encrypted reply…": "Dërgoni një përgjigje të fshehtëzuar…",
|
||||
"Send a reply (unencrypted)…": "Dërgoni një përgjigje (të pafshehtëzuar)…",
|
||||
"Send an encrypted message…": "Dërgoni një mesazh të fshehtëzuar…",
|
||||
"Send a message (unencrypted)…": "Dërgoni një mesazh (të pafshehtëzuar)…",
|
||||
"You do not have permission to post to this room": "S’keni leje të postoni në këtë dhomë",
|
||||
"Server error": "Gabim shërbyesi",
|
||||
"Command error": "Gabim urdhri",
|
||||
"bold": "të trasha",
|
||||
"italic": "të pjerrta",
|
||||
"Markdown is disabled": "Markdown është i çaktivizuar",
|
||||
"Markdown is enabled": "Markdown është i aktivizuar",
|
||||
"Loading...": "Po ngarkohet…",
|
||||
"Pinned Messages": "Mesazhe të Fiksuar",
|
||||
"Jump to message": "Kalo te mesazhi",
|
||||
"%(duration)ss": "%(duration)ss",
|
||||
"%(duration)sm": "%(duration)sm",
|
||||
"%(duration)sh": "%(duration)sh",
|
||||
"%(duration)sd": "%(duration)sd",
|
||||
"Online for %(duration)s": "Në linjë për %(duration)s",
|
||||
"Idle for %(duration)s": "I plogësht për %(duration)s",
|
||||
"Offline for %(duration)s": "Jo në linjë për %(duration)s",
|
||||
"Idle": "I plogësht",
|
||||
"Offline": "Jo në linjë",
|
||||
"Unknown": "I panjohur",
|
||||
"Seen by %(userName)s at %(dateTime)s": "Parë nga %(userName)s më %(dateTime)s",
|
||||
"Save": "Ruaje",
|
||||
"Join Room": "Hyni në dhomë",
|
||||
"Upload avatar": "Ngarkoni avatar",
|
||||
"Remove avatar": "Hiqe avatarin",
|
||||
"Settings": "Rregullime",
|
||||
"Forget room": "Harroje dhomën",
|
||||
"Show panel": "Shfaq panel",
|
||||
"Community Invites": "Ftesa Bashkësie",
|
||||
"Invites": "Ftesa",
|
||||
"Favourites": "Të parapëlqyer",
|
||||
"People": "Persona",
|
||||
"Low priority": "Me përparësi të ulët",
|
||||
"You may wish to login with a different account, or add this email to this account.": "Mund të doni të hyni me një llogari tjetër, ose ta shtoni këtë email te kjo llogari.",
|
||||
"You have been invited to join this room by %(inviterName)s": "Jeni ftuar të merrni pjesë në këtë dhomë nga %(inviterName)s",
|
||||
"Reason: %(reasonText)s": "Arsye: %(reasonText)s",
|
||||
"Rejoin": "Rifutuni",
|
||||
"You have been kicked from %(roomName)s by %(userName)s.": "Jeni përzënë prej %(roomName)s nga %(userName)s.",
|
||||
"You have been kicked from this room by %(userName)s.": "Jeni përzënë nga kjo dhomë prej %(userName)s.",
|
||||
"You have been banned from %(roomName)s by %(userName)s.": "Jeni dëbuar prej %(roomName)s ngas %(userName)s.",
|
||||
"You have been banned from this room by %(userName)s.": "Jeni dëbuar prej kësaj dhome nga %(userName)s.",
|
||||
"This room": "Këtë dhomë",
|
||||
"%(roomName)s does not exist.": "%(roomName)s s’ekziston.",
|
||||
"You are trying to access a room.": "Po provoni të hyni në një dhomë.",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Kjo është një paraparje e kësaj dhome. Ndërveprimet në dhomë janë çaktivizuar",
|
||||
"Banned by %(displayName)s": "Dëbuar nga %(displayName)s",
|
||||
"Privacy warning": "Sinjalizim privatësie",
|
||||
"The visibility of existing history will be unchanged": "Dukshmëria e historikut ekzistues nuk do të ndryshohet.",
|
||||
"You should not yet trust it to secure data": "S’duhet t’i zini ende besë për sigurim të dhënash.",
|
||||
"Enable encryption": "Aktivizoni fshehtëzim",
|
||||
"Encryption is enabled in this room": "Në këtë dhomë është i aktivizuar fshehtëzimi",
|
||||
"Encryption is not enabled in this room": "Në këtë dhomë s’është i aktivizuar fshehtëzimi",
|
||||
"The default role for new room members is": "Roli parazgjedhje për përdorues të ri në dhomë është",
|
||||
"Privileged Users": "Përdorues të Privilegjuar",
|
||||
"Banned users": "Përdorues të dëbuar",
|
||||
"Leave room": "Dilni nga dhomë",
|
||||
"Tagged as: ": "Etiketuar me:",
|
||||
"Click here to fix": "Klikoni këtu për ta ndrequr",
|
||||
"Who can access this room?": "Kush mund të hyjë në këtë dhomë?",
|
||||
"Only people who have been invited": "Vetëm persona që janë ftuar",
|
||||
"Anyone who knows the room's link, apart from guests": "Cilido që di lidhjen e dhomës, hiq vizitorët",
|
||||
"Anyone who knows the room's link, including guests": "Cilido që di lidhjen e dhomës, përfshi vizitorë",
|
||||
"Who can read history?": "Kush mund të lexojë historikun?",
|
||||
"Anyone": "Cilido",
|
||||
"Permissions": "Leje",
|
||||
"Advanced": "Të mëtejshme",
|
||||
"Add a topic": "Shtoni një temë",
|
||||
"Decrypt %(text)s": "Shfshehtëzoje %(text)s",
|
||||
"Copied!": "U kopjua!",
|
||||
"Add an Integration": "Shtoni një Integrim",
|
||||
"This Home Server would like to make sure you are not a robot": "Ky Shërbyes Home do të donte të sigurohej se s’jeni robot",
|
||||
"Sign in with CAS": "Hyni me CAS",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Mund t’i përdorni mundësitë e shërbyesit vetjak për të hyrë në shërbyes të tjerë Matrix, duke dhënë URL-në e një tjetër shërbyesi Home.",
|
||||
"Password:": "Fjalëkalim:",
|
||||
"Please check your email to continue registration.": "Ju lutemi, që të vazhdojë regjistrimi, kontrolloni email-in tuaj.",
|
||||
"Please enter the code it contains:": "Ju lutemi, jepni kodin që përmbahet:",
|
||||
"Code": "Kod",
|
||||
"Start authentication": "Fillo mirëfilltësim",
|
||||
"Username on %(hs)s": "Emër përdoruesi në %(hs)s",
|
||||
"User name": "Emër përdoruesi",
|
||||
"Mobile phone number": "Numër telefoni celular",
|
||||
"Forgot your password?": "Harruat fjalëkalimin tuaj?",
|
||||
"Sign in with": "Hyni me",
|
||||
"Email address": "Adresë email",
|
||||
"Sign in": "Hyni",
|
||||
"Email address (optional)": "Adresë email (opsionale)",
|
||||
"You are registering with %(SelectedTeamName)s": "Po regjistroheni te %(SelectedTeamName)s",
|
||||
"Mobile phone number (optional)": "Numër telefoni celular (opsionale)",
|
||||
"Default server": "Shërbyes parazgjedhje",
|
||||
"Custom server": "Shërbyes vetjak",
|
||||
"Home server URL": "URL shërbyesi Home",
|
||||
"Identity server URL": "URL shërbyesi identitetesh",
|
||||
"What does this mean?": "Ç’do të thotë kjo?",
|
||||
"Filter community members": "Filtroni anëtarë bashkësie",
|
||||
"Something went wrong!": "Diçka shkoi ters!",
|
||||
"Visibility in Room List": "Dukshmëri në Listë Dhomash",
|
||||
"Visible to everyone": "I dukshëm për këdo",
|
||||
"Only visible to community members": "E dukshme vetëm për anëtarë të bashkësisë",
|
||||
"Filter community rooms": "Filtroni dhoma bashkësie",
|
||||
"You're not currently a member of any communities.": "Hëpërhë, s’jeni anëtar i ndonjë bashkësie.",
|
||||
"Unknown Address": "Adresë e Panjohur",
|
||||
"Allow": "Lejoje",
|
||||
"Revoke widget access": "Shfuqizo hyrje widget",
|
||||
"Create new room": "Krijoni dhomë të re",
|
||||
"Unblacklist": "Hiqe nga listë e zezë",
|
||||
"Blacklist": "Listë e zezë",
|
||||
"Unverify": "Hiqi verifikimin",
|
||||
"Verify...": "Verifikoni…",
|
||||
"No results": "S’ka përfundime",
|
||||
"Delete": "Fshije",
|
||||
"Communities": "Bashkësi",
|
||||
"Home": "Kreu",
|
||||
"Integrations Error": "Gabim Integrimesh",
|
||||
"Could not connect to the integration server": "S’u lidh dot te shërbyesi i integrimt",
|
||||
"Manage Integrations": "Administroni Integrime",
|
||||
"were invited %(count)s times|one": "janë ftuar",
|
||||
"was invited %(count)s times|other": "është ftuar %(count)s herë",
|
||||
"was invited %(count)s times|one": "është ftuar",
|
||||
"were banned %(count)s times|one": "janë dëbuar",
|
||||
"was banned %(count)s times|other": "është dëbuar %(count)s herë",
|
||||
"was banned %(count)s times|one": "është dëbuar",
|
||||
"were unbanned %(count)s times|one": "u është hequr dëbimi",
|
||||
"was unbanned %(count)s times|other": "i është hequr dëbimi %(count)s herë",
|
||||
"was unbanned %(count)s times|one": "i është hequr dëbimi",
|
||||
"were kicked %(count)s times|one": "janë përzënë",
|
||||
"was kicked %(count)s times|other": "është përzënë %(count)s herë",
|
||||
"was kicked %(count)s times|one": "është përzënë",
|
||||
"%(items)s and %(count)s others|other": "%(items)s dhe %(count)s të tjerë",
|
||||
"%(items)s and %(count)s others|one": "%(items)s dhe një tjetër",
|
||||
"%(items)s and %(lastItem)s": "%(items)s dhe %(lastItem)s",
|
||||
"collapse": "tkurre",
|
||||
"expand": "zgjeroje",
|
||||
"Custom level": "Nivel vetjak",
|
||||
"<a>In reply to</a> <pill>": "<a>Në Përgjigje të</a> <pill>",
|
||||
"Room directory": "Drejtori Dhome",
|
||||
"Start chat": "Filloni fjalosje",
|
||||
"Add User": "Shtoni Përdorues",
|
||||
"Matrix ID": "ID Matrix",
|
||||
"Matrix Room ID": "ID Matrix dhome",
|
||||
"email address": "adresë email",
|
||||
"You have entered an invalid address.": "Keni dhënë adresë email të pavlefshme.",
|
||||
"Start new chat": "Filloni fjalosje të re",
|
||||
"Confirm Removal": "Ripohoni Heqjen",
|
||||
"Create Community": "Krijoje Bashkësinë",
|
||||
"Community Name": "Emër Bashkësie",
|
||||
"Example": "Shembull",
|
||||
"Community ID": "ID Bashkësie",
|
||||
"example": "shembull",
|
||||
"Create": "Krijoje",
|
||||
"Create Room": "Krijoje Dhomën",
|
||||
"Room name (optional)": "Emër dhome (në daçi)",
|
||||
"Advanced options": "Mundësi të mëtejshme",
|
||||
"Unknown error": "Gabim i panjohur",
|
||||
"Incorrect password": "Fjalëkalim i pasaktë",
|
||||
"Deactivate Account": "Çaktivizoje Llogarinë",
|
||||
"Device name": "Emër pajisjeje",
|
||||
"Device key": "Kyç pajisjeje",
|
||||
"Verify device": "Verifiko pajisjen",
|
||||
"An error has occurred.": "Ndodhi një gabim.",
|
||||
"You added a new device '%(displayName)s', which is requesting encryption keys.": "Shtuat një pajisje të re '%(displayName)s', e cila po kërkon kyçe fshehtëzimi.",
|
||||
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Pajisja juaj e paverifikuar '%(displayName)s' po kërkon kyçe fshehtëzimi.",
|
||||
"Start verification": "Fillo verifikimin",
|
||||
"Share without verifying": "Ndajeni me të tjerë pa e verifikuar",
|
||||
"Ignore request": "Shpërfille kërkesën",
|
||||
"Loading device info...": "Po ngarkohen të dhëna pajisjeje…",
|
||||
"Encryption key request": "Kërkesë kyçi fshehtëzimesh",
|
||||
"Invalid Email Address": "Adresë Email e Pavlefshme",
|
||||
"This doesn't appear to be a valid email address": "Kjo s’duket se është adresë email e vlefshme",
|
||||
"Verification Pending": "Verifikim Në Pritje të Miratimit",
|
||||
"Skip": "Anashkaloje",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Emrat e përdoruesve mund të përmbajnë vetëm shkronja, numra, pika, vija ndarëse dhe nënvija",
|
||||
"Username not available": "Emri i përdoruesit s’është i lirë",
|
||||
"Username invalid: %(errMessage)s": "Emër përdoruesi i pavlefshëm: %(errMessage)s",
|
||||
"Username available": "Emri i përdoruesit është i lirë",
|
||||
"Room contains unknown devices": "Dhoma përmban pajisje të panjohura",
|
||||
"Unknown devices": "Pajisje të panjohura",
|
||||
"Private Chat": "Fjalosje Private",
|
||||
"Public Chat": "Fjalosje Publike",
|
||||
"Custom": "Vetjak",
|
||||
"Alias (optional)": "Alias (opsional)",
|
||||
"Name": "Emër",
|
||||
"Topic": "Temë",
|
||||
"Make this room private": "Kaloje këtë dhomë si private",
|
||||
"Encrypt room": "Fshehtëzoje dhomën",
|
||||
"You must <a>register</a> to use this functionality": "Që të përdorni këtë funksion, duhet të <a>regjistroheni</a>",
|
||||
"Add rooms to the community summary": "Shtoni dhoma te përmbledhja mbi bashkësinë",
|
||||
"Add to summary": "Shtoje te përmbledhja",
|
||||
"Add a Room": "Shtoni një Dhomë",
|
||||
"Add users to the community summary": "Shtoni përdorues te përmbledhja mbi bashkësinë",
|
||||
"Who would you like to add to this summary?": "Kë do të donit të shtonit te kjo përmbledhje?",
|
||||
"Add a User": "Shtoni një Përdorues",
|
||||
"Leave Community": "Braktiseni Bashkësinë",
|
||||
"Leave %(groupName)s?": "Të braktiset {groupName}?",
|
||||
"Community Settings": "Rregullime Bashkësie",
|
||||
"%(inviter)s has invited you to join this community": "%s ju ftoi të bëheni pjesë e kësaj bashkësie",
|
||||
"You are an administrator of this community": "Jeni një përgjegjës i kësaj bashkësie",
|
||||
"You are a member of this community": "Jeni anëtar i këtij ekipi",
|
||||
"Long Description (HTML)": "Përshkrim i Gjatë (HTML)",
|
||||
"Description": "Përshkrim",
|
||||
"Reject invitation": "Hidheni tej ftesën",
|
||||
"Are you sure you want to reject the invitation?": "Jeni i sigurt se doni të hidhet tej kjo ftesë?",
|
||||
"Are you sure you want to leave the room '%(roomName)s'?": "Jeni i sigurt se doni të dilni nga dhoma '%(roomName)s'?",
|
||||
"Signed Out": "I dalë",
|
||||
"Old cryptography data detected": "U pikasën të dhëna kriptografie të vjetër",
|
||||
"Logout": "Dalje",
|
||||
"Your Communities": "Bashkësitë Tuaja",
|
||||
"Create a new community": "Krijoni një bashkësi të re",
|
||||
"You have no visible notifications": "S’keni njoftime të dukshme.",
|
||||
"%(count)s of your messages have not been sent.|other": "Disa nga mesazhet tuaj s’janë dërguar.",
|
||||
"%(count)s of your messages have not been sent.|one": "Mesazhi juaj s’u dërgua.",
|
||||
"%(count)s new messages|other": "%(count)s mesazhe të rinj",
|
||||
"%(count)s new messages|one": "%(count)s mesazh i ri",
|
||||
"Active call": "Thirrje aktive",
|
||||
"You seem to be in a call, are you sure you want to quit?": "Duket se jeni në një thirrje, jeni i sigurt se doni të dilet?",
|
||||
"Search failed": "Kërkimi shtoi",
|
||||
"No more results": "Jo më tepër përfundime",
|
||||
"Room": "Dhomë",
|
||||
"Clear filter": "Pastroje 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",
|
||||
"Light theme": "Temë e çelët",
|
||||
"Dark theme": "Temë e errët",
|
||||
"Status.im theme": "Temë Status.im",
|
||||
"Can't load user settings": "S’ngarkohen dot rregullime përdoruesi",
|
||||
"Server may be unavailable or overloaded": "Shërbyesi mund të jetë i pakapshëm ose i mbingarkuar",
|
||||
"Sign out": "Dilni",
|
||||
"Success": "Sukses",
|
||||
"Remove %(threePid)s?": "Të hiqet %(threePid)s?",
|
||||
"Refer a friend to Riot:": "Tregojini një shoku për Riot-in:",
|
||||
"Interface Language": "Gjuhë Ndërfaqeje",
|
||||
"User Interface": "Ndërfaqe Përdoruesi",
|
||||
"Cryptography": "Kriptografi",
|
||||
"Device ID:": "ID Pajisjeje:",
|
||||
"Device key:": "Kyç pajisjeje:",
|
||||
"Ignored Users": "Përdorues të Shpërfillur",
|
||||
"Riot collects anonymous analytics to allow us to improve the application.": "Riot-i grumbullon të dhëna analitike anonime që të na lejojë ta përmirësojmë aplikacionin.",
|
||||
"These are experimental features that may break in unexpected ways": "Këto janë veçori eksperimentale që mund të ngecin në rrugë të papritura",
|
||||
"Use with caution": "Përdoreni me masë",
|
||||
"Deactivate my account": "Çaktivizoje llogarinë time",
|
||||
"Clear Cache": "Pastroje Fshehtinën",
|
||||
"Updates": "Përditësime",
|
||||
"Check for update": "Kontrollo për përditësime",
|
||||
"Bulk Options": "Veprime Masive",
|
||||
"No media permissions": "S’ka leje mediash",
|
||||
"No Microphones detected": "S’u pikasën Mikrofona",
|
||||
"No Webcams detected": "S’u pikasën kamera",
|
||||
"Default Device": "Pajisje Parazgjedhje",
|
||||
"Microphone": "Mikrofon",
|
||||
"Camera": "Kamerë",
|
||||
"VoIP": "VOIP",
|
||||
"Email": "Email",
|
||||
"Add email address": "Shtoni adresë email",
|
||||
"Profile": "Profil",
|
||||
"Display name": "Emër në ekran",
|
||||
"Account": "Llogari",
|
||||
"Logged in as:": "I futur si:",
|
||||
"Access Token:": "Token Hyrjesh:",
|
||||
"Identity Server is": "Shërbyes Identitetesh është",
|
||||
"riot-web version:": "Version riot-web:",
|
||||
"olm version:": "version olm:",
|
||||
"The email address linked to your account must be entered.": "Duhet dhënë adresa email e lidhur me llogarinë tuaj.",
|
||||
"Your password has been reset": "Fjalëkalimi juaj u ricaktua",
|
||||
"Return to login screen": "Kthehuni te skena e hyrjeve",
|
||||
"New password": "Fjalëkalim i ri",
|
||||
"Confirm your new password": "Ripohoni fjalëkalimin tuaj të ri",
|
||||
"Send Reset Email": "Dërgo Email Ricaktimi",
|
||||
"Create an account": "Krijoni një llogari",
|
||||
"Incorrect username and/or password.": "Emër përdoruesi dhe/ose fjalëkalim i pasaktë.",
|
||||
"The phone number entered looks invalid": "Numri i telefonit që u dha duket i pavlefshëm",
|
||||
"Sign in to get started": "Që t’ia filloni, bëni hyrjen",
|
||||
"Set a display name:": "Caktoni emër ekrani",
|
||||
"Upload an avatar:": "Ngarkoni një avatar:",
|
||||
"This server does not support authentication with a phone number.": "Ky shërbyes nuk mbulon mirëfilltësim me një numër telefoni.",
|
||||
"Missing password.": "Mungon fjalëkalimi.",
|
||||
"Passwords don't match.": "Fjalëkalimet s’përputhen.",
|
||||
"This doesn't look like a valid email address.": "Kjo s’duket si adresë email e vlefshme.",
|
||||
"This doesn't look like a valid phone number.": "Ky s’duket si numër telefoni i vlefshëm.",
|
||||
"You need to enter a user name.": "Lypset të jepni një emër përdoruesi.",
|
||||
"I already have an account": "Kam tashmë një llogari",
|
||||
"Bans user with given id": "Dëbon përdoruesin me ID-në e dhënë",
|
||||
"Unbans user with given id": "I heq dëbimin përdoruesit me ID-në e dhënë",
|
||||
"Sets the room topic": "Caktoni temë dhome",
|
||||
"Kicks user with given id": "Përzë përdoruesin me ID-në e dhënë",
|
||||
"Opens the Developer Tools dialog": "Hap dialogun Mjete Zhvilluesi",
|
||||
"Commands": "Urdhra",
|
||||
"Results from DuckDuckGo": "Përfundimet vijnë nga DuckDuckGo",
|
||||
"Notify the whole room": "Njofto krejt dhomën",
|
||||
"Room Notification": "Njoftim Dhome",
|
||||
"Users": "Përdorues",
|
||||
"unknown device": "pajisje e panjohur",
|
||||
"NOT verified": "JO e verifikua",
|
||||
"verified": "e verifikuar",
|
||||
"Verification": "Verifikim",
|
||||
"User ID": "ID përdoruesi",
|
||||
"Curve25519 identity key": "Kyç identiteti Curve25519",
|
||||
"none": "asnjë",
|
||||
"Algorithm": "Algoritëm",
|
||||
"unencrypted": "të pafshehtëzuara",
|
||||
"Decryption error": "Gabim shfshehtëzimi",
|
||||
"Session ID": "ID sesioni",
|
||||
"End-to-end encryption information": "Të dhëna fshehtëzimi skaj-më-skaj",
|
||||
"Event information": "Të dhëna akti",
|
||||
"Sender device information": "Të dhëna pajisjeje dërguesi",
|
||||
"Passphrases must match": "Frazëkalimet duhet të përputhen",
|
||||
"Passphrase must not be empty": "Frazëkalimi s’mund të jetë i zbrazët",
|
||||
"Export room keys": "Eksporto kyçe dhome",
|
||||
"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.": "Ky proces ju lejon të eksportoni te një kartelë vendore kyçet për mesazhe që keni marrë në dhoma të fshehtëzuara. Mandej do të jeni në gjendje ta importoni kartelën te një tjetër klient Matrix në të ardhmen, që kështu ai klient të jetë në gjendje t’i fshehtëzojë këto mesazhe.",
|
||||
"Enter passphrase": "Jepni frazëkalimin",
|
||||
"Confirm passphrase": "Ripohoni frazëkalimin",
|
||||
"Export": "Eksporto",
|
||||
"Import room keys": "Importo kyçe dhome",
|
||||
"This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Ky proces ju lejon të importoni kyçe fshehtëzimi që keni eksportuar më parë nga një tjetër klient Matrix. Mandej do të jeni në gjendje të shfshehtëzoni çfarëdo mesazhesh që mund të shfshehtëzojë ai klient tjetër.",
|
||||
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Kartela e eksportit është e mbrojtur me një frazëkalim. Që të shfshehtëzoni kartelën, duhet ta jepni frazëkalimin këtu."
|
||||
}
|
||||
|
|
|
@ -1267,5 +1267,28 @@
|
|||
"You need to register to do this. Would you like to register now?": "Du måste registrera dig för att göra detta. Vill du registrera dig nu?",
|
||||
"Forces the current outbound group session in an encrypted room to be discarded": "Tvingar den aktuella utgående gruppsessionen i ett krypterat rum att överges",
|
||||
"Unable to connect to Homeserver. Retrying...": "Det gick inte att ansluta till hemserver. Försöker igen ...",
|
||||
"Unable to query for supported registration methods": "Det gick inte att hämta stödda registreringsmetoder"
|
||||
"Unable to query for supported registration methods": "Det gick inte att hämta stödda registreringsmetoder",
|
||||
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s satte huvudadressen för detta rum till %(address)s.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s lade till %(addedAddresses)s som adresser för detta rum.",
|
||||
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s lade till %(addedAddresses)s som adress för detta rum.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s tog bort %(removedAddresses)s som adresser för detta rum.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s tog bort %(removedAddresses)s som adress för detta rum.",
|
||||
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s lade till %(addedAddresses)s och tog bort %(removedAddresses)s som adresser för detta rum.",
|
||||
"%(senderName)s removed the main address for this room.": "%(senderName)s tog bort huvudadressen för detta rum.",
|
||||
"Pin unread rooms to the top of the room list": "Nåla fast olästa rum längst upp i rumslistan",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Nåla fast rum jag nämns i längst upp i rumslistan",
|
||||
"Joining room...": "Går med i rum...",
|
||||
"Add some now": "Lägg till några nu",
|
||||
"Please review and accept the policies of this homeserver:": "Granska och acceptera policyn för denna hemserver:",
|
||||
"Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.": "Innan du skickar in loggar måste du <a>skapa en GitHub-bugg</a> för att beskriva problemet.",
|
||||
"What GitHub issue are these logs for?": "Vilken GitHub-bugg är dessa loggar för?",
|
||||
"Updating Riot": "Uppdaterar Riot",
|
||||
"Submit Debug Logs": "Skicka felsökningsloggar",
|
||||
"An email address is required to register on this homeserver.": "En epostadress krävs för att registrera sig på denna hemserver.",
|
||||
"A phone number is required to register on this homeserver.": "Ett telefonnummer krävs för att registrera sig på denna hemserver.",
|
||||
"Open Devtools": "Öppna Devtools",
|
||||
"Show developer tools": "Visa utvecklingsverktyg",
|
||||
"You are currently using Riot anonymously as a guest.": "Du använder för närvarande Riot anonymt som gäst.",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Du är administratör för denna community. Du kommer inte kunna gå med igen utan en inbjudan från en annan administratör.",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "Om du vill skapa ett Matrix-konto kan du <a>registrera dig</a> nu."
|
||||
}
|
||||
|
|
|
@ -53,10 +53,10 @@
|
|||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s గది పేరు తొలగించబడింది.",
|
||||
"Changes to who can read history will only apply to future messages in this room": "చరిత్ర చదివేవారికి మార్పులు ఈ గదిలో భవిష్య సందేశాలకు మాత్రమే వర్తిస్తాయి",
|
||||
"Changes your display nickname": "మీ ప్రదర్శన మారుపేరుని మారుస్తుంది",
|
||||
"You cannot place a call with yourself.": "మీరు మీతో కాల్ చేయలేరు.",
|
||||
"You cannot place a call with yourself.": "మీకు మీరే కాల్ చేయలేరు.",
|
||||
"You are already in a call.": "మీరు ఇప్పటికే కాల్లో ఉన్నారు.",
|
||||
"You are trying to access %(roomName)s.": "మీరు %(roomName)s లను యాక్సెస్ చేయడానికి ప్రయత్నిస్తున్నారు.",
|
||||
"You cannot place VoIP calls in this browser.": "మీరు ఈ బ్రౌజర్లో VoIP కాల్లను ఉంచలేరు.",
|
||||
"You cannot place VoIP calls in this browser.": "మీరు ఈ బ్రౌజర్లో కాల్లను చేయలేరు.",
|
||||
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "మీరు అన్ని పరికరాల నుండి లాగ్ అవుట్ అయ్యారు మరియు ఇకపై పుష్ ఉండదు.\nప్రకటనలను నోటిఫికేషన్లను పునఃప్రారంభించడానికి, ప్రతి పరికరంలో మళ్లీ సైన్ ఇన్ చేయండి",
|
||||
"You have no visible notifications": "మీకు కనిపించే నోటిఫికేషన్లు లేవు",
|
||||
"You need to be able to invite users to do that.": "మీరు దీన్ని చేయడానికి వినియోగదారులను ఆహ్వానించగలరు.",
|
||||
|
@ -275,5 +275,26 @@
|
|||
"#example": "#ఉదాహరణ",
|
||||
"Collapse panel": "ప్యానెల్ కుదించు",
|
||||
"Checking for an update...": "నవీకరణ కోసం చూస్తోంది...",
|
||||
"Saturday": "శనివారం"
|
||||
"Saturday": "శనివారం",
|
||||
"This email address is already in use": "ఈ ఇమెయిల్ అడ్రస్ ఇప్పటికే వాడుకం లో ఉంది",
|
||||
"This phone number is already in use": "ఈ ఫోన్ నంబర్ ఇప్పటికే వాడుకం లో ఉంది",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "ఇమెయిల్ అడ్రస్ ని నిరూపించలేక పోయాము. ఈమెయిల్ లో వచ్చిన లింక్ ని నొక్కారా",
|
||||
"The platform you're on": "మీరు ఉన్న ప్లాట్ఫార్మ్",
|
||||
"The version of Riot.im": "రయట్.ఐఎమ్ యొక్క వెర్సన్",
|
||||
"Your homeserver's URL": "మీ హోమ్ సర్వర్ యొక్క URL",
|
||||
"Your identity server's URL": "మీ ఐడెంటిటి సర్వర్ యొక్క URL",
|
||||
"e.g. %(exampleValue)s": "ఉ.దా. %(exampleValue)s 1",
|
||||
"Every page you use in the app": "ఆప్ లో మీరు వాడే ప్రతి పేజి",
|
||||
"e.g. <CurrentPageURL>": "ఉ.దా. <CurrentPageURL>",
|
||||
"Your User Agent": "మీ యీసర్ ఏజెంట్",
|
||||
"Call Failed": "కాల్ విఫలమయింది",
|
||||
"Review Devices": "పరికరాలని ఒక మారు చూసుకో",
|
||||
"Call": "కాల్",
|
||||
"Answer": "ఎత్తు",
|
||||
"The remote side failed to pick up": "అటు వైపు ఎత్తలేకపోయారు",
|
||||
"Unable to capture screen": "తెరని చూపలేకపోతున్నారు",
|
||||
"Existing Call": "నజుస్తున్న కాల్",
|
||||
"VoIP is unsupported": "కాల్ చేయుట ఈ పరికరం పోషించలేదు",
|
||||
"A conference call could not be started because the intgrations server is not available": "ఇంటిగ్రేషన్ సర్వర్ లేనప్పుడు కాన్ఫరెన్స్ కాల్ మొదలుపెట్టలేరు",
|
||||
"Call in Progress": "నడుస్తున్న కాల్"
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Текстове повідомлення було надіслано +%(msisdn)s. Введіть, будь ласка, код підтвердження з цього повідомлення",
|
||||
"Accept": "Прийняти",
|
||||
"Account": "Обліковка",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s прийняв запрошення.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s прийняв запрошення від %(displayName)s.",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s прийняв/ла запрошення.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "Запрошення від %(displayName)s прийнято %(targetName)s.",
|
||||
"Access Token:": "Токен доступу:",
|
||||
"Active call (%(roomName)s)": "Активний виклик (%(roomName)s)",
|
||||
"Add": "Додати",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"A new password must be entered.": "Має бути введений новий пароль.",
|
||||
"Add a widget": "Добавити віджет",
|
||||
"Allow": "Принюти",
|
||||
"%(senderName)s answered the call.": "%(senderName)s відповіла на дзвінок.",
|
||||
"%(senderName)s answered the call.": "%(senderName)s відповів/ла на дзвінок.",
|
||||
"An error has occurred.": "Трапилась помилка.",
|
||||
"Anyone": "Кожний",
|
||||
"Anyone who knows the room's link, apart from guests": "Кожний, хто знає посилання на кімнату, окрім гостей",
|
||||
|
@ -68,10 +68,10 @@
|
|||
"Are you sure you want to upload the following files?": "Ви впевнені, що ви хочете відправити наступний файл?",
|
||||
"Attachment": "Прикріплення",
|
||||
"Autoplay GIFs and videos": "Автовідтворення GIF і відео",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s заблокував(ла) %(targetName)s.",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s заблокував/ла %(targetName)s.",
|
||||
"Ban": "Заблокувати",
|
||||
"Banned users": "Заблоковані користувачі",
|
||||
"Bans user with given id": "Блокує користувача з заданим ID",
|
||||
"Bans user with given id": "Блокує користувача з вказаним ID",
|
||||
"Blacklisted": "В чорному списку",
|
||||
"Bulk Options": "Групові параметри",
|
||||
"Call Timeout": "Час очікування виклика",
|
||||
|
@ -79,22 +79,22 @@
|
|||
"Can't load user settings": "Неможливо завантажити настройки користувача",
|
||||
"Cannot add any more widgets": "Неможливо додати більше віджетів",
|
||||
"Change Password": "Поміняти пароль",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s змінив зображення профіля.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s змінив/ла зображення профілю.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s змінив(ла) рівень доступу для %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s змінив(ла) назву кімнати на %(roomName)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s змінив/ла назву кімнати на %(roomName)s.",
|
||||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s видалив ім'я кімнати.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s змінив тему на %(topic)s.",
|
||||
"Email": "е-пошта",
|
||||
"Email address": "Адреса е-почти",
|
||||
"Email address (optional)": "Адреса е-почти (не обов'язково)",
|
||||
"Email, name or matrix ID": "Е-почта, ім'я або matrix ID",
|
||||
"Email, name or matrix ID": "Електронна пошта, ім'я або matrix ID",
|
||||
"Failed to send email": "Помилка відправки е-почти",
|
||||
"Edit": "Редактувати",
|
||||
"Unpin Message": "Відкріпити повідомлення",
|
||||
"Register": "Зарегіструватись",
|
||||
"Register": "Зареєструватися",
|
||||
"Rooms": "Кімнати",
|
||||
"Add rooms to this community": "Добавити кімнати в це суспільство",
|
||||
"This email address is already in use": "Ця е-пошта вже використовується",
|
||||
"This email address is already in use": "Ця електронна пошта вже використовується",
|
||||
"This phone number is already in use": "Цей телефонний номер вже використовується",
|
||||
"Fetching third party location failed": "Не вдалось отримати стороннє місцеперебування",
|
||||
"Messages in one-to-one chats": "Повідомлення у чатах \"сам на сам\"",
|
||||
|
@ -267,10 +267,10 @@
|
|||
"The version of Riot.im": "Версія Riot.im",
|
||||
"Whether or not you're logged in (we don't record your user name)": "Чи увійшли ви, чи ні (ми не зберігаємо ваше ім'я користувача)",
|
||||
"Your language of choice": "Обрана мова",
|
||||
"Which officially provided instance you are using, if any": "Яким офіційно наданим примірником ви користуєтесь (якщо користуєтесь)",
|
||||
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим Richtext у редакторі Rich Text Editor",
|
||||
"Your homeserver's URL": "URL адреса вашого домашнього серверу",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Не вдалось перевірити адресу е-пошти: переконайтесь, що ви перейшли за посиланням у листі",
|
||||
"Which officially provided instance you are using, if any": "Яким офіційно наданим клієнтом ви користуєтесь (якщо користуєтесь)",
|
||||
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим форматованого тексту у редакторі Rich Text Editor",
|
||||
"Your homeserver's URL": "URL адреса вашого домашнього сервера",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Не вдалось перевірити адресу електронної пошти: переконайтесь, що ви перейшли за посиланням у листі",
|
||||
"The platform you're on": "Використовувана платформа",
|
||||
"Your identity server's URL": "URL адреса серверу ідентифікації",
|
||||
"e.g. %(exampleValue)s": "напр. %(exampleValue)s",
|
||||
|
@ -329,5 +329,248 @@
|
|||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(time)s",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(fullYear)s",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||
"Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?"
|
||||
"Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?",
|
||||
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Якщо сторінка містить ідентифікаційну інформацію, як то ідентифікатор кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.",
|
||||
"Could not connect to the integration server": "Неможливо приєднатися до інтеграційного сервера",
|
||||
"A conference call could not be started because the intgrations server is not available": "Неможливо здійснити дзвінок-конференцію, оскільки інтеграційний сервер недоступний",
|
||||
"Call in Progress": "Іде виклик",
|
||||
"A call is currently being placed!": "Зараз іде виклик!",
|
||||
"A call is already in progress!": "Вже здійснюється дзвінок!",
|
||||
"Permission Required": "Потрібен дозвіл",
|
||||
"You do not have permission to start a conference call in this room": "У вас немає дозволу, щоб розпочати дзвінок-конференцію в цій кімнаті",
|
||||
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Зверніть увагу: будь-яка людина, яку ви додаєте до спільноти, буде публічно видимою тим, хто знає ідентифікатор спільноти",
|
||||
"Invite new community members": "Запросити до спільноти",
|
||||
"Name or matrix ID": "Ім'я або ідентифікатор matrix",
|
||||
"Invite to Community": "Запросити до спільноти",
|
||||
"Which rooms would you like to add to this community?": "Які кімнати ви хочете додати до цієї спільноти?",
|
||||
"Show these rooms to non-members on the community page and room list?": "Показувати ці кімнати тим, хто не належить до спільноти, на сторінці спільноти та списку кімнат?",
|
||||
"Add rooms to the community": "Додати кімнати до спільноти",
|
||||
"Room name or alias": "Назва або псевдонім кімнати",
|
||||
"Add to community": "Додати до спільноти",
|
||||
"Failed to invite the following users to %(groupId)s:": "Не вдалося запросити таких користувачів до %(groupId)s:",
|
||||
"Failed to invite users to community": "Не вдалося запросити користувачів до кімнати",
|
||||
"Failed to invite users to %(groupId)s": "Не вдалося запросити користувачів до %(groupId)s",
|
||||
"Failed to add the following rooms to %(groupId)s:": "Не вдалося додати такі кімнати до %(groupId)s:",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot не має дозволу надсилати вам сповіщення — будь ласка, перевірте налаштування переглядача",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot не має дозволу надсилати сповіщення — будь ласка, спробуйте ще раз",
|
||||
"Unable to enable Notifications": "Не вдалося увімкнути сповіщення",
|
||||
"This email address was not found": "Не знайдено адресу електронної пошти",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Схоже, ваша адреса електронної пошти не пов'язана з жодним ідентифікатор Matrix на цьому домашньому сервері.",
|
||||
"Registration Required": "Потрібна реєстрація",
|
||||
"You need to register to do this. Would you like to register now?": "Вам потрібно зареєструватися, щоб це зробити. Бажаєте зареєструватися зараз?",
|
||||
"Restricted": "Обмежено",
|
||||
"Moderator": "Модератор",
|
||||
"Start a chat": "Розпочати балачку",
|
||||
"Who would you like to communicate with?": "З ким бажаєте спілкуватися?",
|
||||
"Start Chat": "Розпочати балачку",
|
||||
"Invite new room members": "Запросити до кімнати",
|
||||
"Who would you like to add to this room?": "Кого бажаєте додати до цієї кімнати?",
|
||||
"Send Invites": "Надіслати запрошення",
|
||||
"Failed to invite user": "Не вдалося запросити користувача",
|
||||
"Failed to invite": "Не вдалося запросити",
|
||||
"Failed to invite the following users to the %(roomName)s room:": "Не вдалося запросити таких користувачів до кімнати %(roomName)s:",
|
||||
"You need to be logged in.": "Вам потрібно увійти.",
|
||||
"You need to be able to invite users to do that.": "Щоб це зробити, вам необхідно мати можливість запрошувати людей.",
|
||||
"Unable to create widget.": "Неможливо створити віджет.",
|
||||
"Missing roomId.": "Бракує ідентифікатора кімнати.",
|
||||
"Failed to send request.": "Не вдалося надіслати запит.",
|
||||
"This room is not recognised.": "Кімнату не знайдено.",
|
||||
"Power level must be positive integer.": "Рівень прав мусить бути додатнім цілим числом.",
|
||||
"You are not in this room.": "Вас немає в цій кімнаті.",
|
||||
"You do not have permission to do that in this room.": "У вас немає прав виконувати для цього в цій кімнаті.",
|
||||
"Missing room_id in request": "У запиті бракує room_id",
|
||||
"Room %(roomId)s not visible": "Кімната %(roomId)s не видима",
|
||||
"Missing user_id in request": "У запиті пропущено user_id",
|
||||
"Usage": "Використання",
|
||||
"Searches DuckDuckGo for results": "Здійснює пошук через DuckDuckGo",
|
||||
"/ddg is not a command": "/ddg — це не команда",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Щоб цим скористатися, просто почекайте на підказки доповнення й перемикайтеся між ними клавішею TAB.",
|
||||
"Changes your display nickname": "Змінює ваш нік",
|
||||
"Changes colour scheme of current room": "Змінює кольорову схему кімнати",
|
||||
"Sets the room topic": "Встановлює тему кімнати",
|
||||
"Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати",
|
||||
"Joins room with given alias": "Приєднується до кімнати з поданим ідентифікатором",
|
||||
"Leave room": "Покинути кімнату",
|
||||
"Unrecognised room alias:": "Кімнату не знайдено:",
|
||||
"Kicks user with given id": "Викинути з кімнати користувача з вказаним ідентифікатором",
|
||||
"Unbans user with given id": "Розблоковує користувача з вказаним ідентифікатором",
|
||||
"Ignores a user, hiding their messages from you": "Ігнорувати користувача (приховує повідомлення від них)",
|
||||
"Ignored user": "Користувача ігноровано",
|
||||
"You are now ignoring %(userId)s": "Ви ігноруєте %(userId)s",
|
||||
"Stops ignoring a user, showing their messages going forward": "Припинити ігнорувати користувача (показує їхні повідомлення від цього моменту)",
|
||||
"Unignored user": "Припинено ігнорування користувача",
|
||||
"You are no longer ignoring %(userId)s": "Ви більше не ігноруєте %(userId)s",
|
||||
"Define the power level of a user": "Вказати рівень прав користувача",
|
||||
"Deops user with given id": "Знімає права оператора з користувача з вказаним ідентифікатором",
|
||||
"Opens the Developer Tools dialog": "Відкриває вікно інструментів розробника",
|
||||
"Verifies a user, device, and pubkey tuple": "Перевіряє комбінацію користувача, пристрою і публічного ключа",
|
||||
"Unknown (user, device) pair:": "Невідома комбінація користувача і пристрою:",
|
||||
"Device already verified!": "Пристрій вже перевірено!",
|
||||
"WARNING: Device already verified, but keys do NOT MATCH!": "УВАГА: Пристрій уже перевірено, але ключі НЕ ЗБІГАЮТЬСЯ!",
|
||||
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: КЛЮЧ НЕ ПРОЙШОВ ПЕРЕВІРКУ! Підписний ключ %(userId)s на пристрої %(deviceId)s — це «%(fprint)s», і він не збігається з наданим ключем «%(fingerprint)s». Це може означати, що ваші повідомлення перехоплюють!",
|
||||
"Verified key": "Ключ перевірено",
|
||||
"The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Підписний ключ, який ви вказали, збігається з підписним ключем, отриманим від пристрою %(deviceId)s користувача %(userId)s. Пристрій позначено як перевірений.",
|
||||
"Displays action": "Показує дію",
|
||||
"Unrecognised command:": "Невідома команда:",
|
||||
"Reason": "Причина",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s бажає розпочати дзвінок-конференцію.",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s запросив/ла %(targetName)s.",
|
||||
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s змінив/ла своє видиме ім'я на %(displayName)s.",
|
||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s вказав/ла своє видиме ім'я: %(displayName)s.",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s ліквідував/ла своє видиме ім'я (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s вилучав/ла свою світлину профілю.",
|
||||
"%(senderName)s set a profile picture.": "%(senderName)s встановив/ла світлину профілю.",
|
||||
"VoIP conference started.": "Розпочато дзвінок-конференцію.",
|
||||
"%(targetName)s joined the room.": "%(targetName)s приєднав/лася до кімнати.",
|
||||
"VoIP conference finished.": "Дзвінок-конференцію завершено.",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s відкинув/ла запрошення.",
|
||||
"%(targetName)s left the room.": "%(targetName)s залишив/ла кімнату.",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s розблокував/ла %(targetName)s.",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s викинув/ла %(targetName)s.",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s відкликав/ла запрошення %(targetName)s.",
|
||||
"%(senderDisplayName)s sent an image.": "%(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 як адресу цієї кімнати.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
|
||||
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
|
||||
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s додав/ла %(addedAddresses)s і вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
|
||||
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s призначив/ла основну адресу цієї кімнати: %(address)s.",
|
||||
"%(senderName)s removed the main address for this room.": "%(senderName)s вилучив/ла основу адресу цієї кімнати.",
|
||||
"Someone": "Хтось",
|
||||
"(not supported by this browser)": "(не підтримується цією веб-переглядачкою)",
|
||||
"(could not connect media)": "(не можливо під'єднати медіа)",
|
||||
"(no answer)": "(немає відповіді)",
|
||||
"(unknown failure: %(reason)s)": "(невідома помилка: %(reason)s)",
|
||||
"%(senderName)s ended the call.": "%(senderName)s завершив/ла дзвінок.",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s ініціював/ла дзвінок %(callType)s.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s надіслав/ла запрошення %(targetDisplayName)s приєднатися до кімнати.",
|
||||
"Show developer tools": "Показати інструменти розробки",
|
||||
"Default": "Типово",
|
||||
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх учасників, з моменту, коли вони приєдналися.",
|
||||
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх учасників, з моменту, коли вони приєдналися.",
|
||||
"%(senderName)s made future room history visible to all room members.": "%(senderName)s зробив/ла майбутню історію видимою для всіх учасників кімнати.",
|
||||
"%(senderName)s made future room history visible to anyone.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх.",
|
||||
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s зробив/ла майбутню історію видимою невідомим (%(visibility)s).",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s увімкнув/ла наскрізне шифрування (алгоритм %(algorithm)s).",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s з %(fromPowerLevel)s до %(toPowerLevel)s",
|
||||
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s змінив/ла прикріплені повідомлення для кімнати.",
|
||||
"%(widgetName)s widget modified by %(senderName)s": "%(senderName)s змінив/ла %(widgetName)s",
|
||||
"%(widgetName)s widget added by %(senderName)s": "%(senderName)s додав/ла %(widgetName)s",
|
||||
"%(widgetName)s widget removed by %(senderName)s": "%(senderName)s вилучив/ла %(widgetName)s",
|
||||
"%(displayName)s is typing": "%(displayName)s пише",
|
||||
"%(names)s and %(count)s others are typing|other": "%(names)s і %(count)s пишуть",
|
||||
"%(names)s and %(count)s others are typing|one": "%(names)s та ще одна особа пишуть",
|
||||
"Failure to create room": "Не вдалося створити кімнату",
|
||||
"Server may be unavailable, overloaded, or you hit a bug.": "Сервер може бути недоступний, перевантажений, або ж ви натрапили на ваду.",
|
||||
"Send anyway": "Все-таки надіслати",
|
||||
"Unnamed Room": "Кімната без назви",
|
||||
"This homeserver has hit its Monthly Active User limit.": "Цей домашній сервер досягнув свого ліміту щомісячних активних користувачів.",
|
||||
"This homeserver has exceeded one of its resource limits.": "Цей домашній сервер досягнув одного зі своїх лімітів ресурсів.",
|
||||
"Please <a>contact your service administrator</a> to continue using the service.": "Будь ласка, <a>зв'яжіться з адміністратором вашого сервісу</a>, щоб продовжити користуватися цим сервісом.",
|
||||
"Unable to connect to Homeserver. Retrying...": "Не вдається приєднатися до домашнього сервера. Повторення спроби...",
|
||||
"Your browser does not support the required cryptography extensions": "Ваша веб-переглядачка не підтримує необхідних криптографічних функцій",
|
||||
"Not a valid Riot keyfile": "Файл ключа Riot некоректний",
|
||||
"Authentication check failed: incorrect password?": "Помилка автентифікації: неправильний пароль?",
|
||||
"Sorry, your homeserver is too old to participate in this room.": "Вибачте, ваш домашній сервер занадто старий, щоб приєднатися до цієї кімнати.",
|
||||
"Please contact your homeserver administrator.": "Будь ласка, зв'яжіться з адміністратором вашого домашнього сервера.",
|
||||
"Failed to join room": "Не вдалося приєднатися до кімнати",
|
||||
"Message Pinning": "Прикріплені повідомлення",
|
||||
"Increase performance by only loading room members on first view": "Покращити швидкодію, завантажуючи список учасників кімнати лише при першому перегляді",
|
||||
"Disable Emoji suggestions while typing": "Вимкнути підказки емоційок під час писання",
|
||||
"Use compact timeline layout": "Використовувати компактну розкладку стрічки",
|
||||
"Hide join/leave messages (invites/kicks/bans unaffected)": "Приховувати повідомлення про приєднання/вихід (не стосується запрошень, викидань і блокувань)",
|
||||
"Hide avatar changes": "Приховувати зміни аватарів",
|
||||
"Hide display name changes": "Приховувати зміни видимих імен",
|
||||
"Hide read receipts": "Приховувати сповіщення про прочитання",
|
||||
"Show timestamps in 12 hour format (e.g. 2:30pm)": "Показувати час у 12-годинному форматі (напр. 2:30 pm)",
|
||||
"Always show encryption icons": "Завжди показувати значки шифрування",
|
||||
"Enable automatic language detection for syntax highlighting": "Показувати автоматичне визначення мови для підсвічування синтаксису",
|
||||
"Hide avatars in user and room mentions": "Приховувати аватари у згадках користувачів і кімнат",
|
||||
"Disable big emoji in chat": "Вимкнути великі емоційки в балачці",
|
||||
"Don't send typing notifications": "Не надсилати сповіщення «пишу»",
|
||||
"Automatically replace plain text Emoji": "Автоматично замінювати емоційки в простому тексті",
|
||||
"Mirror local video feed": "Показувати локальне відео віддзеркалено",
|
||||
"Disable Community Filter Panel": "Вимкнути панель фільтру спільнот",
|
||||
"Disable Peer-to-Peer for 1:1 calls": "Вимкнути peer-to-peer для дзвінків 1:1",
|
||||
"Send analytics data": "Надсилати дані аналітики",
|
||||
"Never send encrypted messages to unverified devices from this device": "Ніколи не надсилати шифрованих повідомлень до неперевірених пристроїв з цього пристрою",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Ніколи не надсилати шифрованих повідомлень до неперевірених пристроїв у цій кімнаті з цього пристрою",
|
||||
"Enable inline URL previews by default": "Увімкнути вбудований перегляд гіперпосилань за умовчанням",
|
||||
"Enable URL previews for this room (only affects you)": "Увімкнути попередній перегляд гіперпосилань в цій кімнаті (стосується тільки вас)",
|
||||
"Enable URL previews by default for participants in this room": "Увімкнути попередній перегляд гіперпосилань за умовчанням для учасників цієї кімнати",
|
||||
"Room Colour": "Колір кімнати",
|
||||
"Pin unread rooms to the top of the room list": "Прикріпити кімнати з непрочитаними повідомленнями на початку списку",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Прикріпити кімнати, у яких мене згадували, на початку списку",
|
||||
"Incoming voice call from %(name)s": "Вхідний дзвінок від %(name)s",
|
||||
"Incoming video call from %(name)s": "Вхідний відеодзвінок від %(name)s",
|
||||
"Incoming call from %(name)s": "Вхідний дзвінок від %(name)s",
|
||||
"Decline": "Відхилити",
|
||||
"Incorrect verification code": "Неправильний код перевірки",
|
||||
"Enter Code": "Вкажіть код",
|
||||
"Submit": "Надіслати",
|
||||
"Phone": "Телефон",
|
||||
"Failed to upload profile picture!": "Не вдалося завантажити світлину профілю!",
|
||||
"Upload new:": "Завантажити нову:",
|
||||
"No display name": "Немає імені для показу",
|
||||
"New passwords don't match": "Нові паролі не збігаються",
|
||||
"Passwords can't be empty": "Пароль не може бути пустим",
|
||||
"Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Зміна пароля поки що також скидає ключі наскрізного шифрування на всіх пристроях, внаслідок чого шифрована історія балачки стає недоступною, якщо ви не експортуєте ключі шифрування кімнат і не імпортуєте їх після зміни пароля. У майбутньому це буде поліпшено.",
|
||||
"Export E2E room keys": "Експортувати ключі наскрізного шифрування кімнат",
|
||||
"Do you want to set an email address?": "Бажаєте вказати адресу електронної пошти?",
|
||||
"Current password": "Поточний пароль",
|
||||
"Password": "Пароль",
|
||||
"New Password": "Новий пароль",
|
||||
"Confirm password": "Підтвердження пароля",
|
||||
"Your home server does not support device management.": "Ваш домашній сервер не підтримує керування пристроями.",
|
||||
"Unable to load device list": "Не вдалося завантажити перелік пристроїв",
|
||||
"Delete %(count)s devices|other": "Вилучити %(count)s пристроїв",
|
||||
"Delete %(count)s devices|one": "Вилучити пристрій",
|
||||
"Device ID": "ID пристрою",
|
||||
"Device Name": "Назва пристрою",
|
||||
"Last seen": "Востаннє з'являвся",
|
||||
"Select devices": "Вибрати пристрої",
|
||||
"Failed to set display name": "Не вдалося встановити ім'я для показу",
|
||||
"Disable Notifications": "Вимкнути сповіщення",
|
||||
"Enable Notifications": "Увімкнути сповіщення",
|
||||
"The maximum permitted number of widgets have already been added to this room.": "Максимально дозволену кількість віджетів уже додано до цієї кімнати.",
|
||||
"Drop File Here": "Киньте файл сюди",
|
||||
"Drop file here to upload": "Киньте файл сюди, щоб вивантажити",
|
||||
" (unsupported)": " (не підтримується)",
|
||||
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.": "Приєднатися <voiceText>голосом</voiceText> або <videoText>відео</videoText>.",
|
||||
"Ongoing conference call%(supportedText)s.": "Триває дзвінок-конференція%(supportedText)s.",
|
||||
"This event could not be displayed": "Неможливо показати цю подію",
|
||||
"%(senderName)s sent an image": "%(senderName)s надіслав/ла зображення",
|
||||
"%(senderName)s sent a video": "%(senderName)s надіслав/ла відео",
|
||||
"%(senderName)s uploaded a file": "%(senderName)s надіслав/ла файл",
|
||||
"Options": "Налаштування",
|
||||
"Your key share request has been sent - please check your other devices for key share requests.": "Ваш запит поширення ключа надіслано — гляньте запити поширення ключа на своїх інших пристроях.",
|
||||
"Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "Запити поширення ключа надсилаються на ваші пристрої автоматично. Якщо ви відкинули запит поширення ключа на своїх інших пристроях, натисніть тут, щоб наново надіслати запит поширення ключа для цього сеансу.",
|
||||
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Якщо на ваших інших пристроях немає ключа для цього повідомлення, ви не зможете його прочитати.",
|
||||
"Key request sent.": "Запит ключа надіслано.",
|
||||
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "<requestLink>Наново надіслати запит ключів шифрування</requestLink> на ваші інші пристрої.",
|
||||
"Undecryptable": "Неможливо розшифрувати",
|
||||
"Encrypting": "Шифрування",
|
||||
"Encrypted, not sent": "Зашифровано, не надіслано",
|
||||
"Encrypted by a verified device": "Зашифровано перевіреним пристроєм",
|
||||
"Encrypted by an unverified device": "Зашифровано неперевіреним пристроєм",
|
||||
"Unencrypted message": "Незашифроване повідомлення",
|
||||
"Please select the destination room for this message": "Будь ласка, виберіть кімнату, куди потрібно надіслати це повідомлення",
|
||||
"Verified": "Перевірено",
|
||||
"Unverified": "Неперевірено",
|
||||
"device id: ": "id пристрою: ",
|
||||
"Disinvite": "Скасувати запрошення",
|
||||
"Kick": "Викинути",
|
||||
"Disinvite this user?": "Скасувати запрошення для цього користувача?",
|
||||
"Kick this user?": "Викинути цього користувача?",
|
||||
"Failed to kick": "Не вдалося викинути",
|
||||
"Unban": "Розблокувати",
|
||||
"Unban this user?": "Розблокувати цього користувача?",
|
||||
"Ban this user?": "Заблокувати цього користувача?",
|
||||
"Failed to ban user": "Не вдалося заблокувати користувача",
|
||||
"Demote yourself?": "Знизити свій рівень прав?",
|
||||
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Ви не зможете скасувати цю дію, оскільки ви знижуєте свій рівень прав. Якщо ви останній користувач з правами в цій кімнаті, ви не зможете отримати повноваження знову.",
|
||||
"Demote": "Знизити рівень прав",
|
||||
"Failed to mute user": "Не вдалося заглушити користувача",
|
||||
"Failed to toggle moderator status": "Не вдалося перемкнути статус модератора",
|
||||
"Failed to change power level": "Не вдалося змінити рівень повноважень"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"Disinvite": "取消邀请",
|
||||
"Display name": "昵称",
|
||||
"Displays action": "显示操作",
|
||||
"Don't send typing notifications": "不要发送我的打字状态",
|
||||
"Don't send typing notifications": "不要发送“正在输入”提示",
|
||||
"Download %(text)s": "下载 %(text)s",
|
||||
"Email": "电子邮箱",
|
||||
"Email address": "邮箱地址",
|
||||
|
@ -155,7 +155,7 @@
|
|||
"New password": "新密码",
|
||||
"Add a topic": "添加主题",
|
||||
"Admin": "管理员",
|
||||
"Admin Tools": "管理工具",
|
||||
"Admin Tools": "管理员工具",
|
||||
"VoIP": "IP 电话",
|
||||
"Missing Media Permissions, click here to request.": "没有媒体存储权限,点此获取。",
|
||||
"No Microphones detected": "未检测到麦克风",
|
||||
|
@ -424,7 +424,7 @@
|
|||
"Unable to capture screen": "无法录制屏幕",
|
||||
"Unable to enable Notifications": "无法启用通知",
|
||||
"Unable to load device list": "无法加载设备列表",
|
||||
"Undecryptable": "无法解密的",
|
||||
"Undecryptable": "无法解密",
|
||||
"Unencrypted room": "未加密的聊天室",
|
||||
"unencrypted": "未加密的",
|
||||
"Unencrypted message": "未加密消息",
|
||||
|
@ -488,7 +488,7 @@
|
|||
"This room is not recognised.": "无法识别此聊天室。",
|
||||
"To get started, please pick a username!": "请点击用户名!",
|
||||
"Unable to add email address": "无法添加邮箱地址",
|
||||
"Automatically replace plain text Emoji": "文字、表情自动转换",
|
||||
"Automatically replace plain text Emoji": "将符号表情转换为 Emoji",
|
||||
"To reset your password, enter the email address linked to your account": "要重置你的密码,请输入关联你的帐号的邮箱地址",
|
||||
"Unable to verify email address.": "无法验证邮箱地址。",
|
||||
"Unknown room %(roomId)s": "未知聊天室 %(roomId)s",
|
||||
|
@ -498,7 +498,7 @@
|
|||
"Use with caution": "谨慎使用",
|
||||
"User Interface": "用户界面",
|
||||
"User name": "用户名",
|
||||
"(no answer)": "(无响应)",
|
||||
"(no answer)": "(无回复)",
|
||||
"(warning: cannot be disabled again!)": "(警告:无法再被禁用!)",
|
||||
"WARNING: Device already verified, but keys do NOT MATCH!": "警告:设备已验证,但密钥不匹配!",
|
||||
"Who can access this room?": "谁有权访问此聊天室?",
|
||||
|
@ -807,7 +807,7 @@
|
|||
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "从其他设备上 <requestLink>重新请求密钥</requestLink>。",
|
||||
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "如果您是房间中最后一位有权限的用户,在您降低自己的权限等级后将无法撤回此修改,因为你将无法重新获得权限。",
|
||||
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "您将无法撤回此修改,因为您正在将此用户的滥权等级提升至与你相同。",
|
||||
"No devices with registered encryption keys": "没有设备有已注册的加密密钥",
|
||||
"No devices with registered encryption keys": "没有已注册加密密钥的设备",
|
||||
"Unmute": "取消静音",
|
||||
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s(滥权等级 %(powerLevelNumber)s)",
|
||||
"Hide Stickers": "隐藏贴图",
|
||||
|
@ -817,15 +817,15 @@
|
|||
"%(duration)sh": "%(duration)s 小时",
|
||||
"%(duration)sd": "%(duration)s 天",
|
||||
"Online for %(duration)s": "已上线 %(duration)s",
|
||||
"Idle for %(duration)s": "已 idle %(duration)s",
|
||||
"Idle for %(duration)s": "已闲置%(duration)s",
|
||||
"Offline for %(duration)s": "已离线 %(duration)s",
|
||||
"Unknown for %(duration)s": "在线状态未知 %(duration)s",
|
||||
"Unknown for %(duration)s": "未知状态 %(duration)s",
|
||||
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) 在 %(dateTime)s 看到这里",
|
||||
"Remove avatar": "移除头像",
|
||||
"Drop here to favourite": "拖动到这里以加入收藏",
|
||||
"Drop here to tag direct chat": "拖动到这里以加入私聊",
|
||||
"Drop here to restore": "拖动到这里以还原",
|
||||
"Drop here to demote": "拖动到这里以加入低优先级",
|
||||
"Drop here to demote": "拖动到这里以降低优先级",
|
||||
"Unable to ascertain that the address this invite was sent to matches one associated with your account.": "无法确定此邀请发送给的邮件地址是否与您帐户所关联的邮件地址相匹配。",
|
||||
"You have been kicked from this room by %(userName)s.": "您已被 %(userName)s 从此聊天室中移除。",
|
||||
"'%(groupId)s' is not a valid community ID": "“%(groupId)s” 不是有效的社区 ID",
|
||||
|
@ -885,7 +885,7 @@
|
|||
"To change the room's avatar, you must be a": "无法修改此聊天室的头像,因为您不是此聊天室的",
|
||||
"To change the room's name, you must be a": "无法修改此聊天室的名称,因为您不是此聊天室的",
|
||||
"To change the room's main address, you must be a": "无法修改此聊天室的主地址,因为您不是此聊天室的",
|
||||
"To change the room's history visibility, you must be a": "无法修改此聊天室的历史聊天记录可见性,因为您不是此聊天室的",
|
||||
"To change the room's history visibility, you must be a": "无法更改本聊天室历史记录的可见性,您必须为",
|
||||
"To change the permissions in the room, you must be a": "无法修改此聊天室中的权限情况,因为您不是此聊天室的",
|
||||
"Showing flair for these communities:": "为这些社区显示 flair:",
|
||||
"This room is not showing flair for any communities": "此聊天室没有对任何社区显示 flair",
|
||||
|
@ -1150,7 +1150,7 @@
|
|||
"A call is currently being placed!": "已发起一次通话!",
|
||||
"Permission Required": "需要权限",
|
||||
"You do not have permission to start a conference call in this room": "您没有在此聊天室发起通话会议的权限",
|
||||
"Show empty room list headings": "为空的聊天室列表显示 heading",
|
||||
"Show empty room list headings": "为空的聊天室列表显示标题",
|
||||
"This event could not be displayed": "无法显示此事件",
|
||||
"Share Link to User": "分享链接给其他用户",
|
||||
"deleted": "删除线",
|
||||
|
@ -1210,7 +1210,7 @@
|
|||
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "这些聊天室对社区成员可见。社区成员可通过点击来加入它们。",
|
||||
"Your community hasn't got a Long Description, a HTML page to show to community members.<br />Click here to open settings and give it one!": "您的社区还没有详细介绍,一个展示给社区成员的 HTML 页面。<br>点击这里即可打开设置添加详细介绍!",
|
||||
"Failed to load %(groupId)s": "%(groupId)s 加载失败",
|
||||
"This room is not public. You will not be able to rejoin without an invite.": "此聊天室不是公开的。没有邀请的话,您将无法重新加入。",
|
||||
"This room is not public. You will not be able to rejoin without an invite.": "此聊天室不是公开聊天室。如果没有成员邀请,您将无法重新加入。",
|
||||
"Can't leave Server Notices room": "无法退出服务器公告聊天室",
|
||||
"This room is used for important messages from the Homeserver, so you cannot leave it.": "此聊天室是用于发布来自主服务器的重要讯息的,所以您不能退出它。",
|
||||
"Terms and Conditions": "条款与要求",
|
||||
|
|
|
@ -165,7 +165,7 @@
|
|||
"Success": "成功",
|
||||
"The default role for new room members is": "此聊天室新成員的預設角色是",
|
||||
"The main address for this room is": "此聊天室的主要地址是",
|
||||
"This email address is already in use": "此電子郵件地址已經被使用",
|
||||
"This email address is already in use": "這個電子郵件位址已被使用",
|
||||
"This email address was not found": "未找到此電子郵件地址",
|
||||
"The email address linked to your account must be entered.": "必須輸入和你帳號關聯的電子郵件地址。",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "文件 '%(fileName)s' 超過了這個家伺服器的上傳大小限制",
|
||||
|
@ -1289,5 +1289,16 @@
|
|||
"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.": "您之前曾在 %(host)s 上使用 Riot 並啟用成員列表的延遲載入。在此版本中延遲載入已停用。由於本機快取在這兩個設定間不相容,Riot 必須重新同步您的帳號。",
|
||||
"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.": "若其他分頁仍有不同版本的 Riot,請將其關閉,因為在同一個主機上同時啟用和停用延遲載入將會發生問題。",
|
||||
"Incompatible local cache": "不相容的本機快取",
|
||||
"Clear cache and resync": "清除快取並重新同步"
|
||||
"Clear cache and resync": "清除快取並重新同步",
|
||||
"Please accept all of the policies": "請接受所有政策",
|
||||
"Please review and accept the policies of this homeserver:": "請審閱並接受此家伺服器的政策:",
|
||||
"Add some now": "現在就新增一些",
|
||||
"Joining room...": "正在加入聊天室……",
|
||||
"Pin unread rooms to the top of the room list": "釘選未讀的聊天示到聊天室清單頂端",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "釘選我被提及的聊天室到聊天室清單頂端",
|
||||
"If you would like to create a Matrix account you can <a>register</a> now.": "若您想要建立一個 Matrix 帳號,您現在可以<a>註冊</a>了。",
|
||||
"You are currently using Riot anonymously as a guest.": "您目前是以訪客的身份匿名使用 Riot。",
|
||||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "您是此社群的管理員。您將無法在沒有其他管理員的邀請下重新加入。",
|
||||
"Open Devtools": "開啟開發者工具",
|
||||
"Show developer tools": "顯示開發者工具"
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue