Merge branch 'develop' into travis/redact-to-remove
This commit is contained in:
commit
1b6685c5da
120 changed files with 7005 additions and 2054 deletions
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
/**
|
||||
* Allows a user to add a third party identifier to their Home Server and,
|
||||
|
@ -44,7 +45,7 @@ class AddThreepid {
|
|||
return res;
|
||||
}, function(err) {
|
||||
if (err.errcode == 'M_THREEPID_IN_USE') {
|
||||
err.message = "This email address is already in use";
|
||||
err.message = _t('This email address is already in use');
|
||||
} else if (err.httpStatus) {
|
||||
err.message = err.message + ` (Status ${err.httpStatus})`;
|
||||
}
|
||||
|
@ -69,7 +70,7 @@ class AddThreepid {
|
|||
return res;
|
||||
}, function(err) {
|
||||
if (err.errcode == 'M_THREEPID_IN_USE') {
|
||||
err.message = "This phone number is already in use";
|
||||
err.message = _t('This phone number is already in use');
|
||||
} else if (err.httpStatus) {
|
||||
err.message = err.message + ` (Status ${err.httpStatus})`;
|
||||
}
|
||||
|
@ -91,7 +92,7 @@ class AddThreepid {
|
|||
id_server: identityServerDomain
|
||||
}, this.bind).catch(function(err) {
|
||||
if (err.httpStatus === 401) {
|
||||
err.message = "Failed to verify email address: make sure you clicked the link in the email";
|
||||
err.message = _t('Failed to verify email address: make sure you clicked the link in the email');
|
||||
}
|
||||
else if (err.httpStatus) {
|
||||
err.message += ` (Status ${err.httpStatus})`;
|
||||
|
|
|
@ -22,8 +22,8 @@ module.exports = {
|
|||
avatarUrlForMember: function(member, width, height, resizeMethod) {
|
||||
var url = member.getAvatarUrl(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
width,
|
||||
height,
|
||||
Math.floor(width * window.devicePixelRatio),
|
||||
Math.floor(height * window.devicePixelRatio),
|
||||
resizeMethod,
|
||||
false,
|
||||
false
|
||||
|
@ -40,7 +40,9 @@ module.exports = {
|
|||
avatarUrlForUser: function(user, width, height, resizeMethod) {
|
||||
var url = ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl,
|
||||
width, height, resizeMethod
|
||||
Math.floor(width * window.devicePixelRatio),
|
||||
Math.floor(height * window.devicePixelRatio),
|
||||
resizeMethod
|
||||
);
|
||||
if (!url || url.length === 0) {
|
||||
return null;
|
||||
|
@ -57,4 +59,3 @@ module.exports = {
|
|||
return 'img/' + images[total % images.length] + '.png';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ var MatrixClientPeg = require('./MatrixClientPeg');
|
|||
var PlatformPeg = require("./PlatformPeg");
|
||||
var Modal = require('./Modal');
|
||||
var sdk = require('./index');
|
||||
import { _t } from './languageHandler';
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var dis = require("./dispatcher");
|
||||
|
||||
|
@ -142,8 +143,8 @@ function _setCallListeners(call) {
|
|||
play("busyAudio");
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Call Timeout",
|
||||
description: "The remote side failed to pick up."
|
||||
title: _t('Call Timeout'),
|
||||
description: _t('The remote side failed to pick up') + '.',
|
||||
});
|
||||
}
|
||||
else if (oldState === "invite_sent") {
|
||||
|
@ -179,7 +180,8 @@ function _setCallState(call, roomId, status) {
|
|||
}
|
||||
dis.dispatch({
|
||||
action: 'call_state',
|
||||
room_id: roomId
|
||||
room_id: roomId,
|
||||
state: status,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -203,8 +205,8 @@ function _onAction(payload) {
|
|||
console.log("Can't capture screen: " + screenCapErrorString);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Unable to capture screen",
|
||||
description: screenCapErrorString
|
||||
title: _t('Unable to capture screen'),
|
||||
description: screenCapErrorString,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -223,8 +225,8 @@ function _onAction(payload) {
|
|||
if (module.exports.getAnyActiveCall()) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Existing Call",
|
||||
description: "You are already in a call."
|
||||
title: _t('Existing Call'),
|
||||
description: _t('You are already in a call') + '.',
|
||||
});
|
||||
return; // don't allow >1 call to be placed.
|
||||
}
|
||||
|
@ -233,8 +235,8 @@ function _onAction(payload) {
|
|||
if (!MatrixClientPeg.get().supportsVoip()) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "VoIP is unsupported",
|
||||
description: "You cannot place VoIP calls in this browser."
|
||||
title: _t('VoIP is unsupported'),
|
||||
description: _t('You cannot place VoIP calls in this browser') + '.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -249,7 +251,7 @@ function _onAction(payload) {
|
|||
if (members.length <= 1) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
description: "You cannot place a call with yourself."
|
||||
description: _t('You cannot place a call with yourself') + '.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -275,14 +277,14 @@ function _onAction(payload) {
|
|||
if (!ConferenceHandler) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
description: "Conference calls are not supported in this client"
|
||||
description: _t('Conference calls are not supported in this client'),
|
||||
});
|
||||
}
|
||||
else if (!MatrixClientPeg.get().supportsVoip()) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "VoIP is unsupported",
|
||||
description: "You cannot place VoIP calls in this browser."
|
||||
title: _t('VoIP is unsupported'),
|
||||
description: _t('You cannot place VoIP calls in this browser') + '.',
|
||||
});
|
||||
}
|
||||
else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
|
||||
|
@ -294,14 +296,14 @@ function _onAction(payload) {
|
|||
// Therefore we disable conference calling in E2E rooms.
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
description: "Conference calls are not supported in encrypted rooms",
|
||||
description: _t('Conference calls are not supported in encrypted rooms'),
|
||||
});
|
||||
}
|
||||
else {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning!",
|
||||
description: "Conference calling is in development and may not be reliable.",
|
||||
title: _t('Warning!'),
|
||||
description: _t('Conference calling is in development and may not be reliable') + '.',
|
||||
onFinished: confirm=>{
|
||||
if (confirm) {
|
||||
ConferenceHandler.createNewMatrixCall(
|
||||
|
@ -312,8 +314,8 @@ function _onAction(payload) {
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Conference call failed: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to set up conference call",
|
||||
description: "Conference call failed. " + ((err && err.message) ? err.message : ""),
|
||||
title: _t('Failed to set up conference call'),
|
||||
description: _t('Conference call failed') + '. ' + ((err && err.message) ? err.message : ''),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// singleton which dispatches invocations of a given type & argument
|
||||
// rather than just a type (as per EventEmitter and Flux's dispatcher etc)
|
||||
//
|
||||
// This means you can have a single point which listens for an EventEmitter event
|
||||
// and then dispatches out to one of thousands of RoomTiles (for instance) rather than
|
||||
// having each RoomTile register for the EventEmitter event and having to
|
||||
// iterate over all of them.
|
||||
class ConstantTimeDispatcher {
|
||||
constructor() {
|
||||
// type -> arg -> [ listener(arg, params) ]
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
register(type, arg, listener) {
|
||||
if (!this.listeners[type]) this.listeners[type] = {};
|
||||
if (!this.listeners[type][arg]) this.listeners[type][arg] = [];
|
||||
this.listeners[type][arg].push(listener);
|
||||
}
|
||||
|
||||
unregister(type, arg, listener) {
|
||||
if (this.listeners[type] && this.listeners[type][arg]) {
|
||||
var i = this.listeners[type][arg].indexOf(listener);
|
||||
if (i > -1) {
|
||||
this.listeners[type][arg].splice(i, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn("Unregistering unrecognised listener (type=" + type + ", arg=" + arg + ")");
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(type, arg, params) {
|
||||
if (!this.listeners[type] || !this.listeners[type][arg]) {
|
||||
console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")");
|
||||
return;
|
||||
}
|
||||
this.listeners[type][arg].forEach(listener=>{
|
||||
listener.call(arg, params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.constantTimeDispatcher) {
|
||||
global.constantTimeDispatcher = new ConstantTimeDispatcher();
|
||||
}
|
||||
module.exports = global.constantTimeDispatcher;
|
|
@ -21,6 +21,7 @@ var extend = require('./extend');
|
|||
var dis = require('./dispatcher');
|
||||
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||
var sdk = require('./index');
|
||||
import { _t } from './languageHandler';
|
||||
var Modal = require('./Modal');
|
||||
|
||||
var encrypt = require("browser-encrypt-attachment");
|
||||
|
@ -347,14 +348,14 @@ class ContentMessages {
|
|||
}, function(err) {
|
||||
error = err;
|
||||
if (!upload.canceled) {
|
||||
var desc = "The file '"+upload.fileName+"' failed to upload.";
|
||||
var desc = _t('The file \'%(fileName)s\' failed to upload', {fileName: upload.fileName}) + '.';
|
||||
if (err.http_status == 413) {
|
||||
desc = "The file '"+upload.fileName+"' exceeds this home server's size limit for uploads";
|
||||
desc = _t('The file \'%(fileName)s\' exceeds this home server\'s size limit for uploads', {fileName: upload.fileName});
|
||||
}
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Upload Failed",
|
||||
description: desc
|
||||
title: _t('Upload Failed'),
|
||||
description: desc,
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -15,38 +16,89 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
function getDaysArray() {
|
||||
return [
|
||||
_t('Sun'),
|
||||
_t('Mon'),
|
||||
_t('Tue'),
|
||||
_t('Wed'),
|
||||
_t('Thu'),
|
||||
_t('Fri'),
|
||||
_t('Sat'),
|
||||
];
|
||||
}
|
||||
|
||||
function getMonthsArray() {
|
||||
return [
|
||||
_t('Jan'),
|
||||
_t('Feb'),
|
||||
_t('Mar'),
|
||||
_t('Apr'),
|
||||
_t('May'),
|
||||
_t('Jun'),
|
||||
_t('Jul'),
|
||||
_t('Aug'),
|
||||
_t('Sep'),
|
||||
_t('Oct'),
|
||||
_t('Nov'),
|
||||
_t('Dec'),
|
||||
];
|
||||
}
|
||||
|
||||
function pad(n) {
|
||||
return (n < 10 ? '0' : '') + n;
|
||||
}
|
||||
|
||||
function twelveHourTime(date) {
|
||||
let hours = date.getHours() % 12;
|
||||
const minutes = pad(date.getMinutes());
|
||||
const ampm = date.getHours() >= 12 ? 'PM' : 'AM';
|
||||
hours = pad(hours ? hours : 12);
|
||||
return `${hours}:${minutes} ${ampm}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatDate: function(date) {
|
||||
// date.toLocaleTimeString is completely system dependent.
|
||||
// just go 24h for now
|
||||
function pad(n) {
|
||||
return (n < 10 ? '0' : '') + n;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
const days = getDaysArray();
|
||||
const months = getMonthsArray();
|
||||
if (date.toDateString() === now.toDateString()) {
|
||||
return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
return this.formatTime(date);
|
||||
}
|
||||
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
|
||||
return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
// TODO: use standard date localize function provided in counterpart
|
||||
return _t('%(weekDayName)s %(time)s', {weekDayName: days[date.getDay()], time: this.formatTime(date)});
|
||||
}
|
||||
else /* if (now.getFullYear() === date.getFullYear()) */ {
|
||||
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
else if (now.getFullYear() === date.getFullYear()) {
|
||||
// TODO: use standard date localize function provided in counterpart
|
||||
return _t('%(weekDayName)s, %(monthName)s %(day)s %(time)s', {
|
||||
weekDayName: days[date.getDay()],
|
||||
monthName: months[date.getMonth()],
|
||||
day: date.getDate(),
|
||||
time: this.formatTime(date),
|
||||
});
|
||||
}
|
||||
/*
|
||||
else {
|
||||
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
}
|
||||
*/
|
||||
return this.formatFullDate(date);
|
||||
},
|
||||
|
||||
formatTime: function(date) {
|
||||
//return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
return ('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2);
|
||||
}
|
||||
};
|
||||
formatFullDate: function(date) {
|
||||
const days = getDaysArray();
|
||||
const months = getMonthsArray();
|
||||
return _t('%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s', {
|
||||
weekDayName: days[date.getDay()],
|
||||
monthName: months[date.getMonth()],
|
||||
day: date.getDate(),
|
||||
fullYear: date.getFullYear(),
|
||||
time: this.formatTime(date),
|
||||
});
|
||||
},
|
||||
|
||||
formatTime: function(date, showTwelveHour=false) {
|
||||
if (showTwelveHour) {
|
||||
return twelveHourTime(date);
|
||||
}
|
||||
return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||
},
|
||||
};
|
||||
|
|
|
@ -111,8 +111,7 @@ var sanitizeHtmlParams = {
|
|||
allowedTags: [
|
||||
'font', // custom to matrix for IRC-style font coloring
|
||||
'del', // for markdown
|
||||
// deliberately no h1/h2 to stop people shouting.
|
||||
'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||
'nl', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
|
||||
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'span',
|
||||
],
|
||||
|
@ -149,17 +148,18 @@ var sanitizeHtmlParams = {
|
|||
attribs.href = m[1];
|
||||
delete attribs.target;
|
||||
}
|
||||
|
||||
m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
|
||||
if (m) {
|
||||
var entity = m[1];
|
||||
if (entity[0] === '@') {
|
||||
attribs.href = '#/user/' + entity;
|
||||
else {
|
||||
m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
|
||||
if (m) {
|
||||
var entity = m[1];
|
||||
if (entity[0] === '@') {
|
||||
attribs.href = '#/user/' + entity;
|
||||
}
|
||||
else if (entity[0] === '#' || entity[0] === '!') {
|
||||
attribs.href = '#/room/' + entity;
|
||||
}
|
||||
delete attribs.target;
|
||||
}
|
||||
else if (entity[0] === '#' || entity[0] === '!') {
|
||||
attribs.href = '#/room/' + entity;
|
||||
}
|
||||
delete attribs.target;
|
||||
}
|
||||
}
|
||||
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
|
||||
|
|
|
@ -32,5 +32,4 @@ module.exports = {
|
|||
DELETE: 46,
|
||||
KEY_D: 68,
|
||||
KEY_E: 69,
|
||||
KEY_K: 75,
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ import DMRoomMap from './utils/DMRoomMap';
|
|||
import RtsClient from './RtsClient';
|
||||
import Modal from './Modal';
|
||||
import sdk from './index';
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
/**
|
||||
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
||||
|
@ -49,7 +50,7 @@ import sdk from './index';
|
|||
* If any of steps 1-4 are successful, it will call {setLoggedIn}, which in
|
||||
* turn will raise on_logged_in and will_start_client events.
|
||||
*
|
||||
* It returns a promise which resolves when the above process completes.
|
||||
* @param {object} opts
|
||||
*
|
||||
* @param {object} opts.realQueryParams: string->string map of the
|
||||
* query-parameters extracted from the real query-string of the starting
|
||||
|
@ -67,6 +68,7 @@ import sdk from './index';
|
|||
* @params {string} opts.guestIsUrl: homeserver URL. Only used if enableGuest is
|
||||
* true; defines the IS to use.
|
||||
*
|
||||
* @returns {Promise} a promise which resolves when the above process completes.
|
||||
*/
|
||||
export function loadSession(opts) {
|
||||
const realQueryParams = opts.realQueryParams || {};
|
||||
|
@ -127,7 +129,7 @@ export function loadSession(opts) {
|
|||
|
||||
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||
// create a temporary MatrixClient to do the login
|
||||
var client = Matrix.createClient({
|
||||
const client = Matrix.createClient({
|
||||
baseUrl: queryParams.homeserver,
|
||||
});
|
||||
|
||||
|
@ -159,7 +161,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
|
|||
// Not really sure where the right home for it is.
|
||||
|
||||
// create a temporary MatrixClient to do the login
|
||||
var client = Matrix.createClient({
|
||||
const client = Matrix.createClient({
|
||||
baseUrl: hsUrl,
|
||||
});
|
||||
|
||||
|
@ -188,30 +190,30 @@ function _restoreFromLocalStorage() {
|
|||
if (!localStorage) {
|
||||
return q(false);
|
||||
}
|
||||
const hs_url = localStorage.getItem("mx_hs_url");
|
||||
const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
||||
const access_token = localStorage.getItem("mx_access_token");
|
||||
const user_id = localStorage.getItem("mx_user_id");
|
||||
const device_id = localStorage.getItem("mx_device_id");
|
||||
const hsUrl = localStorage.getItem("mx_hs_url");
|
||||
const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
||||
const accessToken = localStorage.getItem("mx_access_token");
|
||||
const userId = localStorage.getItem("mx_user_id");
|
||||
const deviceId = localStorage.getItem("mx_device_id");
|
||||
|
||||
let is_guest;
|
||||
let isGuest;
|
||||
if (localStorage.getItem("mx_is_guest") !== null) {
|
||||
is_guest = localStorage.getItem("mx_is_guest") === "true";
|
||||
isGuest = localStorage.getItem("mx_is_guest") === "true";
|
||||
} else {
|
||||
// legacy key name
|
||||
is_guest = localStorage.getItem("matrix-is-guest") === "true";
|
||||
isGuest = localStorage.getItem("matrix-is-guest") === "true";
|
||||
}
|
||||
|
||||
if (access_token && user_id && hs_url) {
|
||||
console.log("Restoring session for %s", user_id);
|
||||
if (accessToken && userId && hsUrl) {
|
||||
console.log("Restoring session for %s", userId);
|
||||
try {
|
||||
setLoggedIn({
|
||||
userId: user_id,
|
||||
deviceId: device_id,
|
||||
accessToken: access_token,
|
||||
homeserverUrl: hs_url,
|
||||
identityServerUrl: is_url,
|
||||
guest: is_guest,
|
||||
userId: userId,
|
||||
deviceId: deviceId,
|
||||
accessToken: accessToken,
|
||||
homeserverUrl: hsUrl,
|
||||
identityServerUrl: isUrl,
|
||||
guest: isGuest,
|
||||
});
|
||||
return q(true);
|
||||
} catch (e) {
|
||||
|
@ -228,14 +230,14 @@ function _handleRestoreFailure(e) {
|
|||
|
||||
let msg = e.message;
|
||||
if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") {
|
||||
msg = "You need to log back in to generate end-to-end encryption keys "
|
||||
+ "for this device and submit the public key to your homeserver. "
|
||||
+ "This is a once off; sorry for the inconvenience.";
|
||||
msg = _t(
|
||||
'You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.'
|
||||
);
|
||||
|
||||
_clearLocalStorage();
|
||||
|
||||
return q.reject(new Error(
|
||||
"Unable to restore previous session: " + msg,
|
||||
_t('Unable to restore previous session') + ': ' + msg,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -273,9 +275,13 @@ export function initRtsClient(url) {
|
|||
*/
|
||||
export function setLoggedIn(credentials) {
|
||||
credentials.guest = Boolean(credentials.guest);
|
||||
console.log("setLoggedIn => %s (guest=%s) hs=%s",
|
||||
credentials.userId, credentials.guest,
|
||||
credentials.homeserverUrl);
|
||||
|
||||
console.log(
|
||||
"setLoggedIn: mxid:", credentials.userId,
|
||||
"deviceId:", credentials.deviceId,
|
||||
"guest:", credentials.guest,
|
||||
"hs:", credentials.homeserverUrl,
|
||||
);
|
||||
// This is dispatched to indicate that the user is still in the process of logging in
|
||||
// because `teamPromise` may take some time to resolve, breaking the assumption that
|
||||
// `setLoggedIn` takes an "instant" to complete, and dispatch `on_logged_in` a few ms
|
||||
|
@ -352,7 +358,7 @@ export function logout() {
|
|||
return;
|
||||
}
|
||||
|
||||
return MatrixClientPeg.get().logout().then(onLoggedOut,
|
||||
MatrixClientPeg.get().logout().then(onLoggedOut,
|
||||
(err) => {
|
||||
// Just throwing an error here is going to be very unhelpful
|
||||
// if you're trying to log out because your server's down and
|
||||
|
@ -363,8 +369,8 @@ export function logout() {
|
|||
// change your password).
|
||||
console.log("Failed to call logout API: token will not be invalidated");
|
||||
onLoggedOut();
|
||||
}
|
||||
);
|
||||
},
|
||||
).done();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,7 +426,7 @@ export function stopMatrixClient() {
|
|||
UserActivity.stop();
|
||||
Presence.stop();
|
||||
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
|
||||
var cli = MatrixClientPeg.get();
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.stopClient();
|
||||
cli.removeAllListeners();
|
||||
|
|
|
@ -15,11 +15,14 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var PlatformPeg = require("./PlatformPeg");
|
||||
var TextForEvent = require('./TextForEvent');
|
||||
var Avatar = require('./Avatar');
|
||||
var dis = require("./dispatcher");
|
||||
import MatrixClientPeg from './MatrixClientPeg';
|
||||
import PlatformPeg from './PlatformPeg';
|
||||
import TextForEvent from './TextForEvent';
|
||||
import Avatar from './Avatar';
|
||||
import dis from './dispatcher';
|
||||
import sdk from './index';
|
||||
import { _t } from './languageHandler';
|
||||
import Modal from './Modal';
|
||||
|
||||
/*
|
||||
* Dispatches:
|
||||
|
@ -29,7 +32,7 @@ var dis = require("./dispatcher");
|
|||
* }
|
||||
*/
|
||||
|
||||
var Notifier = {
|
||||
const Notifier = {
|
||||
notifsByRoom: {},
|
||||
|
||||
notificationMessageForEvent: function(ev) {
|
||||
|
@ -48,16 +51,16 @@ var Notifier = {
|
|||
return;
|
||||
}
|
||||
|
||||
var msg = this.notificationMessageForEvent(ev);
|
||||
let msg = this.notificationMessageForEvent(ev);
|
||||
if (!msg) return;
|
||||
|
||||
var title;
|
||||
if (!ev.sender || room.name == ev.sender.name) {
|
||||
let title;
|
||||
if (!ev.sender || room.name === ev.sender.name) {
|
||||
title = room.name;
|
||||
// notificationMessageForEvent includes sender,
|
||||
// but we already have the sender here
|
||||
if (ev.getContent().body) msg = ev.getContent().body;
|
||||
} else if (ev.getType() == 'm.room.member') {
|
||||
} else if (ev.getType() === 'm.room.member') {
|
||||
// context is all in the message here, we don't need
|
||||
// to display sender info
|
||||
title = room.name;
|
||||
|
@ -68,7 +71,7 @@ var Notifier = {
|
|||
if (ev.getContent().body) msg = ev.getContent().body;
|
||||
}
|
||||
|
||||
var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
|
||||
const avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
|
||||
ev.sender, 40, 40, 'crop'
|
||||
) : null;
|
||||
|
||||
|
@ -83,7 +86,7 @@ var Notifier = {
|
|||
},
|
||||
|
||||
_playAudioNotification: function(ev, room) {
|
||||
var e = document.getElementById("messageAudio");
|
||||
const e = document.getElementById("messageAudio");
|
||||
if (e) {
|
||||
e.load();
|
||||
e.play();
|
||||
|
@ -95,7 +98,7 @@ var Notifier = {
|
|||
this.boundOnSyncStateChange = this.onSyncStateChange.bind(this);
|
||||
this.boundOnRoomReceipt = this.onRoomReceipt.bind(this);
|
||||
MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
|
||||
MatrixClientPeg.get().on("Room.receipt", this.boundOnRoomReceipt);
|
||||
MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt);
|
||||
MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange);
|
||||
this.toolbarHidden = false;
|
||||
this.isSyncing = false;
|
||||
|
@ -104,7 +107,7 @@ var Notifier = {
|
|||
stop: function() {
|
||||
if (MatrixClientPeg.get() && this.boundOnRoomTimeline) {
|
||||
MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline);
|
||||
MatrixClientPeg.get().removeListener("Room.receipt", this.boundOnRoomReceipt);
|
||||
MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt);
|
||||
MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange);
|
||||
}
|
||||
this.isSyncing = false;
|
||||
|
@ -121,7 +124,7 @@ var Notifier = {
|
|||
// make sure that we persist the current setting audio_enabled setting
|
||||
// before changing anything
|
||||
if (global.localStorage) {
|
||||
if(global.localStorage.getItem('audio_notifications_enabled') == null) {
|
||||
if (global.localStorage.getItem('audio_notifications_enabled') === null) {
|
||||
this.setAudioEnabled(this.isEnabled());
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +134,14 @@ var Notifier = {
|
|||
plaf.requestNotificationPermission().done((result) => {
|
||||
if (result !== 'granted') {
|
||||
// The permission request was dismissed or denied
|
||||
const description = result === 'denied'
|
||||
? _t('Riot does not have permission to send you notifications - please check your browser settings')
|
||||
: _t('Riot was not given permission to send notifications - please try again');
|
||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t('Unable to enable Notifications'),
|
||||
description,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -141,7 +152,7 @@ var Notifier = {
|
|||
if (callback) callback();
|
||||
dis.dispatch({
|
||||
action: "notifier_enabled",
|
||||
value: true
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
// clear the notifications_hidden flag, so that if notifications are
|
||||
|
@ -152,7 +163,7 @@ var Notifier = {
|
|||
global.localStorage.setItem('notifications_enabled', 'false');
|
||||
dis.dispatch({
|
||||
action: "notifier_enabled",
|
||||
value: false
|
||||
value: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -165,7 +176,7 @@ var Notifier = {
|
|||
|
||||
if (!global.localStorage) return true;
|
||||
|
||||
var enabled = global.localStorage.getItem('notifications_enabled');
|
||||
const enabled = global.localStorage.getItem('notifications_enabled');
|
||||
if (enabled === null) return true;
|
||||
return enabled === 'true';
|
||||
},
|
||||
|
@ -173,12 +184,12 @@ var Notifier = {
|
|||
setAudioEnabled: function(enable) {
|
||||
if (!global.localStorage) return;
|
||||
global.localStorage.setItem('audio_notifications_enabled',
|
||||
enable ? 'true' : 'false');
|
||||
enable ? 'true' : 'false');
|
||||
},
|
||||
|
||||
isAudioEnabled: function(enable) {
|
||||
if (!global.localStorage) return true;
|
||||
var enabled = global.localStorage.getItem(
|
||||
const enabled = global.localStorage.getItem(
|
||||
'audio_notifications_enabled');
|
||||
// default to true if the popups are enabled
|
||||
if (enabled === null) return this.isEnabled();
|
||||
|
@ -192,7 +203,7 @@ var Notifier = {
|
|||
// this is nothing to do with notifier_enabled
|
||||
dis.dispatch({
|
||||
action: "notifier_enabled",
|
||||
value: this.isEnabled()
|
||||
value: this.isEnabled(),
|
||||
});
|
||||
|
||||
// update the info to localStorage for persistent settings
|
||||
|
@ -215,8 +226,7 @@ var Notifier = {
|
|||
onSyncStateChange: function(state) {
|
||||
if (state === "SYNCING") {
|
||||
this.isSyncing = true;
|
||||
}
|
||||
else if (state === "STOPPED" || state === "ERROR") {
|
||||
} else if (state === "STOPPED" || state === "ERROR") {
|
||||
this.isSyncing = false;
|
||||
}
|
||||
},
|
||||
|
@ -225,10 +235,10 @@ var Notifier = {
|
|||
if (toStartOfTimeline) return;
|
||||
if (!room) return;
|
||||
if (!this.isSyncing) return; // don't alert for any messages initially
|
||||
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;
|
||||
if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return;
|
||||
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
|
||||
|
||||
var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
||||
const actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
||||
if (actions && actions.notify) {
|
||||
if (this.isEnabled()) {
|
||||
this._displayPopupNotification(ev, room);
|
||||
|
@ -240,7 +250,7 @@ var Notifier = {
|
|||
},
|
||||
|
||||
onRoomReceipt: function(ev, room) {
|
||||
if (room.getUnreadNotificationCount() == 0) {
|
||||
if (room.getUnreadNotificationCount() === 0) {
|
||||
// ideally we would clear each notification when it was read,
|
||||
// but we have no way, given a read receipt, to know whether
|
||||
// the receipt comes before or after an event, so we can't
|
||||
|
@ -255,7 +265,7 @@ var Notifier = {
|
|||
}
|
||||
delete this.notifsByRoom[room.roomId];
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (!global.mxNotifier) {
|
||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
/**
|
||||
* Allows a user to reset their password on a homeserver.
|
||||
|
@ -53,7 +54,7 @@ class PasswordReset {
|
|||
return res;
|
||||
}, function(err) {
|
||||
if (err.errcode == 'M_THREEPID_NOT_FOUND') {
|
||||
err.message = "This email address was not found";
|
||||
err.message = _t('This email address was not found');
|
||||
} else if (err.httpStatus) {
|
||||
err.message = err.message + ` (Status ${err.httpStatus})`;
|
||||
}
|
||||
|
@ -78,10 +79,10 @@ class PasswordReset {
|
|||
}
|
||||
}, this.password).catch(function(err) {
|
||||
if (err.httpStatus === 401) {
|
||||
err.message = "Failed to verify email address: make sure you clicked the link in the email";
|
||||
err.message = _t('Failed to verify email address: make sure you clicked the link in the email');
|
||||
}
|
||||
else if (err.httpStatus === 404) {
|
||||
err.message = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
|
||||
err.message = _t('Your email address does not appear to be associated with a Matrix ID on this Homeserver') + '.';
|
||||
}
|
||||
else if (err.httpStatus) {
|
||||
err.message += ` (Status ${err.httpStatus})`;
|
||||
|
|
17
src/Roles.js
17
src/Roles.js
|
@ -13,14 +13,19 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
export const LEVEL_ROLE_MAP = {
|
||||
undefined: 'Default',
|
||||
0: 'User',
|
||||
50: 'Moderator',
|
||||
100: 'Admin',
|
||||
};
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
export function levelRoleMap() {
|
||||
return {
|
||||
undefined: _t('Default'),
|
||||
0: _t('User'),
|
||||
50: _t('Moderator'),
|
||||
100: _t('Admin'),
|
||||
};
|
||||
}
|
||||
|
||||
export function textualPowerLevel(level, userDefault) {
|
||||
const LEVEL_ROLE_MAP = this.levelRoleMap();
|
||||
if (LEVEL_ROLE_MAP[level]) {
|
||||
return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`);
|
||||
} else {
|
||||
|
|
|
@ -125,6 +125,7 @@ const SdkConfig = require('./SdkConfig');
|
|||
const MatrixClientPeg = require("./MatrixClientPeg");
|
||||
const MatrixEvent = require("matrix-js-sdk").MatrixEvent;
|
||||
const dis = require("./dispatcher");
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
function sendResponse(event, res) {
|
||||
const data = JSON.parse(JSON.stringify(event.data));
|
||||
|
@ -150,7 +151,7 @@ function inviteUser(event, roomId, userId) {
|
|||
console.log(`Received request to invite ${userId} into room ${roomId}`);
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
sendError(event, "You need to be logged in.");
|
||||
sendError(event, _t('You need to be logged in.'));
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
|
@ -170,7 +171,7 @@ function inviteUser(event, roomId, userId) {
|
|||
success: true,
|
||||
});
|
||||
}, function(err) {
|
||||
sendError(event, "You need to be able to invite users to do that.", err);
|
||||
sendError(event, _t('You need to be able to invite users to do that.'), err);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -181,7 +182,7 @@ function setPlumbingState(event, roomId, status) {
|
|||
console.log(`Received request to set plumbing state to status "${status}" in room ${roomId}`);
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
sendError(event, "You need to be logged in.");
|
||||
sendError(event, _t('You need to be logged in.'));
|
||||
return;
|
||||
}
|
||||
client.sendStateEvent(roomId, "m.room.plumbing", { status : status }).done(() => {
|
||||
|
@ -189,7 +190,7 @@ function setPlumbingState(event, roomId, status) {
|
|||
success: true,
|
||||
});
|
||||
}, (err) => {
|
||||
sendError(event, err.message ? err.message : "Failed to send request.", err);
|
||||
sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,7 @@ function setBotOptions(event, roomId, userId) {
|
|||
console.log(`Received request to set options for bot ${userId} in room ${roomId}`);
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
sendError(event, "You need to be logged in.");
|
||||
sendError(event, _t('You need to be logged in.'));
|
||||
return;
|
||||
}
|
||||
client.sendStateEvent(roomId, "m.room.bot.options", event.data.content, "_" + userId).done(() => {
|
||||
|
@ -205,20 +206,20 @@ function setBotOptions(event, roomId, userId) {
|
|||
success: true,
|
||||
});
|
||||
}, (err) => {
|
||||
sendError(event, err.message ? err.message : "Failed to send request.", err);
|
||||
sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
|
||||
});
|
||||
}
|
||||
|
||||
function setBotPower(event, roomId, userId, level) {
|
||||
if (!(Number.isInteger(level) && level >= 0)) {
|
||||
sendError(event, "Power level must be positive integer.");
|
||||
sendError(event, _t('Power level must be positive integer.'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Received request to set power level to ${level} for bot ${userId} in room ${roomId}.`);
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
sendError(event, "You need to be logged in.");
|
||||
sendError(event, _t('You need to be logged in.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -235,7 +236,7 @@ function setBotPower(event, roomId, userId, level) {
|
|||
success: true,
|
||||
});
|
||||
}, (err) => {
|
||||
sendError(event, err.message ? err.message : "Failed to send request.", err);
|
||||
sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -258,12 +259,12 @@ function botOptions(event, roomId, userId) {
|
|||
function returnStateEvent(event, roomId, eventType, stateKey) {
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!client) {
|
||||
sendError(event, "You need to be logged in.");
|
||||
sendError(event, _t('You need to be logged in.'));
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
if (!room) {
|
||||
sendError(event, "This room is not recognised.");
|
||||
sendError(event, _t('This room is not recognised.'));
|
||||
return;
|
||||
}
|
||||
const stateEvent = room.currentState.getStateEvents(eventType, stateKey);
|
||||
|
@ -313,13 +314,13 @@ const onMessage = function(event) {
|
|||
const roomId = event.data.room_id;
|
||||
const userId = event.data.user_id;
|
||||
if (!roomId) {
|
||||
sendError(event, "Missing room_id in request");
|
||||
sendError(event, _t('Missing room_id in request'));
|
||||
return;
|
||||
}
|
||||
let promise = Promise.resolve(currentRoomId);
|
||||
if (!currentRoomId) {
|
||||
if (!currentRoomAlias) {
|
||||
sendError(event, "Must be viewing a room");
|
||||
sendError(event, _t('Must be viewing a room'));
|
||||
return;
|
||||
}
|
||||
// no room ID but there is an alias, look it up.
|
||||
|
@ -331,7 +332,7 @@ const onMessage = function(event) {
|
|||
|
||||
promise.then((viewingRoomId) => {
|
||||
if (roomId !== viewingRoomId) {
|
||||
sendError(event, "Room " + roomId + " not visible");
|
||||
sendError(event, _t('Room %(roomId)s not visible', {roomId: roomId}));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -345,7 +346,7 @@ const onMessage = function(event) {
|
|||
}
|
||||
|
||||
if (!userId) {
|
||||
sendError(event, "Missing user_id in request");
|
||||
sendError(event, _t('Missing user_id in request'));
|
||||
return;
|
||||
}
|
||||
switch (event.data.action) {
|
||||
|
@ -370,7 +371,7 @@ const onMessage = function(event) {
|
|||
}
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
sendError(event, "Failed to lookup current room.");
|
||||
sendError(event, _t('Failed to lookup current room') + '.');
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var dis = require("./dispatcher");
|
||||
var Tinter = require("./Tinter");
|
||||
import MatrixClientPeg from "./MatrixClientPeg";
|
||||
import dis from "./dispatcher";
|
||||
import Tinter from "./Tinter";
|
||||
import sdk from './index';
|
||||
import { _t } from './languageHandler';
|
||||
import Modal from './Modal';
|
||||
|
||||
|
||||
|
@ -41,58 +42,64 @@ class Command {
|
|||
}
|
||||
|
||||
getUsage() {
|
||||
return "Usage: " + this.getCommandWithArgs();
|
||||
return _t('Usage') + ': ' + this.getCommandWithArgs();
|
||||
}
|
||||
}
|
||||
|
||||
var reject = function(msg) {
|
||||
function reject(msg) {
|
||||
return {
|
||||
error: msg
|
||||
error: msg,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
var success = function(promise) {
|
||||
function success(promise) {
|
||||
return {
|
||||
promise: promise
|
||||
promise: promise,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
var commands = {
|
||||
/* Disable the "unexpected this" error for these commands - all of the run
|
||||
* functions are called with `this` bound to the Command instance.
|
||||
*/
|
||||
|
||||
/* eslint-disable babel/no-invalid-this */
|
||||
|
||||
const commands = {
|
||||
ddg: new Command("ddg", "<query>", function(roomId, args) {
|
||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||
// TODO Don't explain this away, actually show a search UI here.
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "/ddg is not a command",
|
||||
description: "To use it, just wait for autocomplete results to load and tab through them.",
|
||||
title: _t('/ddg is not a command'),
|
||||
description: _t('To use it, just wait for autocomplete results to load and tab through them.'),
|
||||
});
|
||||
return success();
|
||||
}),
|
||||
|
||||
// Change your nickname
|
||||
nick: new Command("nick", "<display_name>", function(room_id, args) {
|
||||
nick: new Command("nick", "<display_name>", function(roomId, args) {
|
||||
if (args) {
|
||||
return success(
|
||||
MatrixClientPeg.get().setDisplayName(args)
|
||||
MatrixClientPeg.get().setDisplayName(args),
|
||||
);
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
}),
|
||||
|
||||
// Changes the colorscheme of your current room
|
||||
tint: new Command("tint", "<color1> [<color2>]", function(room_id, args) {
|
||||
tint: new Command("tint", "<color1> [<color2>]", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
|
||||
const matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
|
||||
if (matches) {
|
||||
Tinter.tint(matches[1], matches[4]);
|
||||
var colorScheme = {};
|
||||
const colorScheme = {};
|
||||
colorScheme.primary_color = matches[1];
|
||||
if (matches[4]) {
|
||||
colorScheme.secondary_color = matches[4];
|
||||
}
|
||||
return success(
|
||||
MatrixClientPeg.get().setRoomAccountData(
|
||||
room_id, "org.matrix.room.color_scheme", colorScheme
|
||||
)
|
||||
roomId, "org.matrix.room.color_scheme", colorScheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -100,22 +107,22 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Change the room topic
|
||||
topic: new Command("topic", "<topic>", function(room_id, args) {
|
||||
topic: new Command("topic", "<topic>", function(roomId, args) {
|
||||
if (args) {
|
||||
return success(
|
||||
MatrixClientPeg.get().setRoomTopic(room_id, args)
|
||||
MatrixClientPeg.get().setRoomTopic(roomId, args),
|
||||
);
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
}),
|
||||
|
||||
// Invite a user
|
||||
invite: new Command("invite", "<userId>", function(room_id, args) {
|
||||
invite: new Command("invite", "<userId>", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().invite(room_id, matches[1])
|
||||
MatrixClientPeg.get().invite(roomId, matches[1]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -123,21 +130,21 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Join a room
|
||||
join: new Command("join", "#alias:domain", function(room_id, args) {
|
||||
join: new Command("join", "#alias:domain", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room_alias = matches[1];
|
||||
if (room_alias[0] !== '#') {
|
||||
let roomAlias = matches[1];
|
||||
if (roomAlias[0] !== '#') {
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
if (!room_alias.match(/:/)) {
|
||||
room_alias += ':' + MatrixClientPeg.get().getDomain();
|
||||
if (!roomAlias.match(/:/)) {
|
||||
roomAlias += ':' + MatrixClientPeg.get().getDomain();
|
||||
}
|
||||
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_alias: room_alias,
|
||||
room_alias: roomAlias,
|
||||
auto_join: true,
|
||||
});
|
||||
|
||||
|
@ -147,29 +154,29 @@ var commands = {
|
|||
return reject(this.getUsage());
|
||||
}),
|
||||
|
||||
part: new Command("part", "[#alias:domain]", function(room_id, args) {
|
||||
var targetRoomId;
|
||||
part: new Command("part", "[#alias:domain]", function(roomId, args) {
|
||||
let targetRoomId;
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room_alias = matches[1];
|
||||
if (room_alias[0] !== '#') {
|
||||
let roomAlias = matches[1];
|
||||
if (roomAlias[0] !== '#') {
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
if (!room_alias.match(/:/)) {
|
||||
room_alias += ':' + MatrixClientPeg.get().getDomain();
|
||||
if (!roomAlias.match(/:/)) {
|
||||
roomAlias += ':' + MatrixClientPeg.get().getDomain();
|
||||
}
|
||||
|
||||
// Try to find a room with this alias
|
||||
var rooms = MatrixClientPeg.get().getRooms();
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var aliasEvents = rooms[i].currentState.getStateEvents(
|
||||
"m.room.aliases"
|
||||
const rooms = MatrixClientPeg.get().getRooms();
|
||||
for (let i = 0; i < rooms.length; i++) {
|
||||
const aliasEvents = rooms[i].currentState.getStateEvents(
|
||||
"m.room.aliases",
|
||||
);
|
||||
for (var j = 0; j < aliasEvents.length; j++) {
|
||||
var aliases = aliasEvents[j].getContent().aliases || [];
|
||||
for (var k = 0; k < aliases.length; k++) {
|
||||
if (aliases[k] === room_alias) {
|
||||
for (let j = 0; j < aliasEvents.length; j++) {
|
||||
const aliases = aliasEvents[j].getContent().aliases || [];
|
||||
for (let k = 0; k < aliases.length; k++) {
|
||||
if (aliases[k] === roomAlias) {
|
||||
targetRoomId = rooms[i].roomId;
|
||||
break;
|
||||
}
|
||||
|
@ -178,27 +185,28 @@ var commands = {
|
|||
}
|
||||
if (targetRoomId) { break; }
|
||||
}
|
||||
}
|
||||
if (!targetRoomId) {
|
||||
return reject("Unrecognised room alias: " + room_alias);
|
||||
if (!targetRoomId) {
|
||||
return reject("Unrecognised room alias: " + roomAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!targetRoomId) targetRoomId = room_id;
|
||||
if (!targetRoomId) targetRoomId = roomId;
|
||||
return success(
|
||||
MatrixClientPeg.get().leave(targetRoomId).then(
|
||||
function() {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
})
|
||||
function() {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
||||
// Kick a user from the room with an optional reason
|
||||
kick: new Command("kick", "<userId> [<reason>]", function(room_id, args) {
|
||||
kick: new Command("kick", "<userId> [<reason>]", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
const matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().kick(room_id, matches[1], matches[3])
|
||||
MatrixClientPeg.get().kick(roomId, matches[1], matches[3]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -206,12 +214,12 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Ban a user from the room with an optional reason
|
||||
ban: new Command("ban", "<userId> [<reason>]", function(room_id, args) {
|
||||
ban: new Command("ban", "<userId> [<reason>]", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
const matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().ban(room_id, matches[1], matches[3])
|
||||
MatrixClientPeg.get().ban(roomId, matches[1], matches[3]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -219,13 +227,13 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Unban a user from the room
|
||||
unban: new Command("unban", "<userId>", function(room_id, args) {
|
||||
unban: new Command("unban", "<userId>", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
// Reset the user membership to "leave" to unban him
|
||||
return success(
|
||||
MatrixClientPeg.get().unban(room_id, matches[1])
|
||||
MatrixClientPeg.get().unban(roomId, matches[1]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -233,27 +241,27 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Define the power level of a user
|
||||
op: new Command("op", "<userId> [<power level>]", function(room_id, args) {
|
||||
op: new Command("op", "<userId> [<power level>]", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(\d+))?$/);
|
||||
var powerLevel = 50; // default power level for op
|
||||
const matches = args.match(/^(\S+?)( +(\d+))?$/);
|
||||
let powerLevel = 50; // default power level for op
|
||||
if (matches) {
|
||||
var user_id = matches[1];
|
||||
const userId = matches[1];
|
||||
if (matches.length === 4 && undefined !== matches[3]) {
|
||||
powerLevel = parseInt(matches[3]);
|
||||
}
|
||||
if (powerLevel !== NaN) {
|
||||
var room = MatrixClientPeg.get().getRoom(room_id);
|
||||
if (!isNaN(powerLevel)) {
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) {
|
||||
return reject("Bad room ID: " + room_id);
|
||||
return reject("Bad room ID: " + roomId);
|
||||
}
|
||||
var powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", ""
|
||||
const powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", "",
|
||||
);
|
||||
return success(
|
||||
MatrixClientPeg.get().setPowerLevel(
|
||||
room_id, user_id, powerLevel, powerLevelEvent
|
||||
)
|
||||
roomId, userId, powerLevel, powerLevelEvent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -262,32 +270,87 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Reset the power level of a user
|
||||
deop: new Command("deop", "<userId>", function(room_id, args) {
|
||||
deop: new Command("deop", "<userId>", function(roomId, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
const matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room = MatrixClientPeg.get().getRoom(room_id);
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) {
|
||||
return reject("Bad room ID: " + room_id);
|
||||
return reject("Bad room ID: " + roomId);
|
||||
}
|
||||
|
||||
var powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", ""
|
||||
const powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", "",
|
||||
);
|
||||
return success(
|
||||
MatrixClientPeg.get().setPowerLevel(
|
||||
room_id, args, undefined, powerLevelEvent
|
||||
)
|
||||
roomId, args, undefined, powerLevelEvent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
})
|
||||
}),
|
||||
|
||||
// Verify a user, device, and pubkey tuple
|
||||
verify: new Command("verify", "<userId> <deviceId> <deviceSigningKey>", function(roomId, args) {
|
||||
if (args) {
|
||||
const matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
|
||||
if (matches) {
|
||||
const userId = matches[1];
|
||||
const deviceId = matches[2];
|
||||
const fingerprint = matches[3];
|
||||
|
||||
const device = MatrixClientPeg.get().getStoredDevice(userId, deviceId);
|
||||
if (!device) {
|
||||
return reject(`Unknown (user, device) pair: (${userId}, ${deviceId})`);
|
||||
}
|
||||
|
||||
if (device.isVerified()) {
|
||||
if (device.getFingerprint() === fingerprint) {
|
||||
return reject(`Device already verified!`);
|
||||
} else {
|
||||
return reject(`WARNING: Device already verified, but keys do NOT MATCH!`);
|
||||
}
|
||||
}
|
||||
|
||||
if (device.getFingerprint() === fingerprint) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
userId, deviceId, true,
|
||||
);
|
||||
|
||||
// Tell the user we verified everything!
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Verified key",
|
||||
description: (
|
||||
<div>
|
||||
<p>
|
||||
The signing key you provided matches the signing key you received
|
||||
from { userId }'s device { deviceId }. Device marked as verified.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
hasCancelButton: false,
|
||||
});
|
||||
|
||||
return success();
|
||||
} else {
|
||||
return reject(`WARNING: KEY VERIFICATION FAILED! The signing key for ${userId} and device
|
||||
${deviceId} is "${device.getFingerprint()}" which does not match the provided key
|
||||
"${fingerprint}". This could mean your communications are being intercepted!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
}),
|
||||
};
|
||||
/* eslint-enable babel/no-invalid-this */
|
||||
|
||||
|
||||
// helpful aliases
|
||||
var aliases = {
|
||||
j: "join"
|
||||
const aliases = {
|
||||
j: "join",
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
@ -304,13 +367,13 @@ module.exports = {
|
|||
// IRC-style commands
|
||||
input = input.replace(/\s+$/, "");
|
||||
if (input[0] === "/" && input[1] !== "/") {
|
||||
var bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
|
||||
var cmd, args;
|
||||
const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
|
||||
let cmd;
|
||||
let args;
|
||||
if (bits) {
|
||||
cmd = bits[1].substring(1).toLowerCase();
|
||||
args = bits[3];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
cmd = input;
|
||||
}
|
||||
if (cmd === "me") return null;
|
||||
|
@ -319,8 +382,7 @@ module.exports = {
|
|||
}
|
||||
if (commands[cmd]) {
|
||||
return commands[cmd].run(roomId, args);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return reject("Unrecognised command: " + input);
|
||||
}
|
||||
}
|
||||
|
@ -329,12 +391,12 @@ module.exports = {
|
|||
|
||||
getCommandList: function() {
|
||||
// Return all the commands plus /me and /markdown which aren't handled like normal commands
|
||||
var cmds = Object.keys(commands).sort().map(function(cmdKey) {
|
||||
const cmds = Object.keys(commands).sort().map(function(cmdKey) {
|
||||
return commands[cmdKey];
|
||||
});
|
||||
cmds.push(new Command("me", "<action>", function() {}));
|
||||
cmds.push(new Command("markdown", "<on|off>", function() {}));
|
||||
|
||||
return cmds;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var CallHandler = require("./CallHandler");
|
||||
|
||||
import { _t } from './languageHandler';
|
||||
import * as Roles from './Roles';
|
||||
|
||||
function textForMemberEvent(ev) {
|
||||
|
@ -25,95 +25,100 @@ function textForMemberEvent(ev) {
|
|||
var targetName = ev.target ? ev.target.name : ev.getStateKey();
|
||||
var ConferenceHandler = CallHandler.getConferenceHandler();
|
||||
var reason = ev.getContent().reason ? (
|
||||
" Reason: " + ev.getContent().reason
|
||||
_t('Reason') + ': ' + ev.getContent().reason
|
||||
) : "";
|
||||
switch (ev.getContent().membership) {
|
||||
case 'invite':
|
||||
var threePidContent = ev.getContent().third_party_invite;
|
||||
if (threePidContent) {
|
||||
if (threePidContent.display_name) {
|
||||
return targetName + " accepted the invitation for " +
|
||||
threePidContent.display_name + ".";
|
||||
return _t('%(targetName)s accepted the invitation for %(displayName)s.', {targetName: targetName, displayName: threePidContent.display_name});
|
||||
} else {
|
||||
return targetName + " accepted an invitation.";
|
||||
return _t('%(targetName)s accepted an invitation.', {targetName: targetName});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
|
||||
return senderName + " requested a VoIP conference";
|
||||
return _t('%(senderName)s requested a VoIP conference.', {senderName: senderName});
|
||||
}
|
||||
else {
|
||||
return senderName + " invited " + targetName + ".";
|
||||
return _t('%(senderName)s invited %(targetName)s.', {senderName: senderName, targetName: targetName});
|
||||
}
|
||||
}
|
||||
case 'ban':
|
||||
return senderName + " banned " + targetName + "." + reason;
|
||||
return _t(
|
||||
'%(senderName)s banned %(targetName)s.',
|
||||
{senderName: senderName, targetName: targetName}
|
||||
) + ' ' + reason;
|
||||
case 'join':
|
||||
if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
|
||||
if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
|
||||
return ev.getSender() + " changed their display name from " +
|
||||
ev.getPrevContent().displayname + " to " +
|
||||
ev.getContent().displayname;
|
||||
return _t('%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.', {senderName: ev.getSender(), oldDisplayName: ev.getPrevContent().displayname, displayName: ev.getContent().displayname});
|
||||
} else if (!ev.getPrevContent().displayname && ev.getContent().displayname) {
|
||||
return ev.getSender() + " set their display name to " + ev.getContent().displayname;
|
||||
return _t('%(senderName)s set their display name to %(displayName)s.', {senderName: ev.getSender(), displayName: ev.getContent().displayname});
|
||||
} else if (ev.getPrevContent().displayname && !ev.getContent().displayname) {
|
||||
return ev.getSender() + " removed their display name (" + ev.getPrevContent().displayname + ")";
|
||||
return _t('%(senderName)s removed their display name (%(oldDisplayName)s).', {senderName: ev.getSender(), oldDisplayName: ev.getPrevContent().displayname});
|
||||
} else if (ev.getPrevContent().avatar_url && !ev.getContent().avatar_url) {
|
||||
return senderName + " removed their profile picture";
|
||||
return _t('%(senderName)s removed their profile picture.', {senderName: senderName});
|
||||
} else if (ev.getPrevContent().avatar_url && ev.getContent().avatar_url && ev.getPrevContent().avatar_url != ev.getContent().avatar_url) {
|
||||
return senderName + " changed their profile picture";
|
||||
return _t('%(senderName)s changed their profile picture.', {senderName: senderName});
|
||||
} else if (!ev.getPrevContent().avatar_url && ev.getContent().avatar_url) {
|
||||
return senderName + " set a profile picture";
|
||||
return _t('%(senderName)s set a profile picture.', {senderName: senderName});
|
||||
} else {
|
||||
// hacky hack for https://github.com/vector-im/vector-web/issues/2020
|
||||
return senderName + " rejoined the room.";
|
||||
// suppress null rejoins
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
|
||||
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
|
||||
return "VoIP conference started";
|
||||
return _t('VoIP conference started.');
|
||||
}
|
||||
else {
|
||||
return targetName + " joined the room.";
|
||||
return _t('%(targetName)s joined the room.', {targetName: targetName});
|
||||
}
|
||||
}
|
||||
case 'leave':
|
||||
if (ev.getSender() === ev.getStateKey()) {
|
||||
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
|
||||
return "VoIP conference finished";
|
||||
return _t('VoIP conference finished.');
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "invite") {
|
||||
return targetName + " rejected the invitation.";
|
||||
return _t('%(targetName)s rejected the invitation.', {targetName: targetName});
|
||||
}
|
||||
else {
|
||||
return targetName + " left the room.";
|
||||
return _t('%(targetName)s left the room.', {targetName: targetName});
|
||||
}
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "ban") {
|
||||
return senderName + " unbanned " + targetName + ".";
|
||||
return _t('%(senderName)s unbanned %(targetName)s.', {senderName: senderName, targetName: targetName});
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "join") {
|
||||
return senderName + " kicked " + targetName + "." + reason;
|
||||
return _t(
|
||||
'%(senderName)s kicked %(targetName)s.',
|
||||
{senderName: senderName, targetName: targetName}
|
||||
) + ' ' + reason;
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "invite") {
|
||||
return senderName + " withdrew " + targetName + "'s invitation." + reason;
|
||||
return _t(
|
||||
'%(senderName)s withdrew %(targetName)s\'s invitation.',
|
||||
{senderName: senderName, targetName: targetName}
|
||||
) + ' ' + reason;
|
||||
}
|
||||
else {
|
||||
return targetName + " left the room.";
|
||||
return _t('%(targetName)s left the room.', {targetName: targetName});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function textForTopicEvent(ev) {
|
||||
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
|
||||
return senderDisplayName + ' changed the topic to "' + ev.getContent().topic + '"';
|
||||
return _t('%(senderDisplayName)s changed the topic to "%(topic)s".', {senderDisplayName: senderDisplayName, topic: ev.getContent().topic});
|
||||
}
|
||||
|
||||
function textForRoomNameEvent(ev) {
|
||||
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
|
||||
return senderDisplayName + ' changed the room name to "' + ev.getContent().name + '"';
|
||||
return _t('%(senderDisplayName)s changed the room name to %(roomName)s.', {senderDisplayName: senderDisplayName, roomName: ev.getContent().name});
|
||||
}
|
||||
|
||||
function textForMessageEvent(ev) {
|
||||
|
@ -122,66 +127,66 @@ function textForMessageEvent(ev) {
|
|||
if (ev.getContent().msgtype === "m.emote") {
|
||||
message = "* " + senderDisplayName + " " + message;
|
||||
} else if (ev.getContent().msgtype === "m.image") {
|
||||
message = senderDisplayName + " sent an image.";
|
||||
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName: senderDisplayName});
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function textForCallAnswerEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
|
||||
return senderName + " answered the call." + supported;
|
||||
var senderName = event.sender ? event.sender.name : _t('Someone');
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
|
||||
return _t('%(senderName)s answered the call.', {senderName: senderName}) + ' ' + supported;
|
||||
}
|
||||
|
||||
function textForCallHangupEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
|
||||
return senderName + " ended the call." + supported;
|
||||
var senderName = event.sender ? event.sender.name : _t('Someone');
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
|
||||
return _t('%(senderName)s ended the call.', {senderName: senderName}) + ' ' + supported;
|
||||
}
|
||||
|
||||
function textForCallInviteEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
var senderName = event.sender ? event.sender.name : _t('Someone');
|
||||
// FIXME: Find a better way to determine this from the event?
|
||||
var type = "voice";
|
||||
if (event.getContent().offer && event.getContent().offer.sdp &&
|
||||
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
|
||||
type = "video";
|
||||
}
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
|
||||
return senderName + " placed a " + type + " call." + supported;
|
||||
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
|
||||
return _t('%(senderName)s placed a %(callType)s call.', {senderName: senderName, callType: type}) + ' ' + supported;
|
||||
}
|
||||
|
||||
function textForThreePidInviteEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : event.getSender();
|
||||
return senderName + " sent an invitation to " + event.getContent().display_name +
|
||||
" to join the room.";
|
||||
return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {senderName: senderName, targetDisplayName: event.getContent().display_name});
|
||||
}
|
||||
|
||||
function textForHistoryVisibilityEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : event.getSender();
|
||||
var vis = event.getContent().history_visibility;
|
||||
var text = senderName + " made future room history visible to ";
|
||||
// XXX: This i18n just isn't going to work for languages with different sentence structure.
|
||||
var text = _t('%(senderName)s made future room history visible to', {senderName: senderName}) + ' ';
|
||||
if (vis === "invited") {
|
||||
text += "all room members, from the point they are invited.";
|
||||
text += _t('all room members, from the point they are invited') + '.';
|
||||
}
|
||||
else if (vis === "joined") {
|
||||
text += "all room members, from the point they joined.";
|
||||
text += _t('all room members, from the point they joined') + '.';
|
||||
}
|
||||
else if (vis === "shared") {
|
||||
text += "all room members.";
|
||||
text += _t('all room members') + '.';
|
||||
}
|
||||
else if (vis === "world_readable") {
|
||||
text += "anyone.";
|
||||
text += _t('anyone') + '.';
|
||||
}
|
||||
else {
|
||||
text += " unknown (" + vis + ")";
|
||||
text += ' ' + _t('unknown') + ' (' + vis + ').';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function textForEncryptionEvent(event) {
|
||||
var senderName = event.sender ? event.sender.name : event.getSender();
|
||||
return senderName + " turned on end-to-end encryption (algorithm " + event.getContent().algorithm + ")";
|
||||
return _t('%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).', {senderName: senderName, algorithm: event.getContent().algorithm});
|
||||
}
|
||||
|
||||
// Currently will only display a change if a user's power level is changed
|
||||
|
@ -204,6 +209,7 @@ function textForPowerEvent(event) {
|
|||
}
|
||||
);
|
||||
let diff = [];
|
||||
// XXX: This is also surely broken for i18n
|
||||
users.forEach((userId) => {
|
||||
// Previous power level
|
||||
const from = event.getPrevContent().users[userId];
|
||||
|
@ -211,16 +217,21 @@ function textForPowerEvent(event) {
|
|||
const to = event.getContent().users[userId];
|
||||
if (to !== from) {
|
||||
diff.push(
|
||||
userId +
|
||||
' from ' + Roles.textualPowerLevel(from, userDefault) +
|
||||
' to ' + Roles.textualPowerLevel(to, userDefault)
|
||||
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
|
||||
userId: userId,
|
||||
fromPowerLevel: Roles.textualPowerLevel(from, userDefault),
|
||||
toPowerLevel: Roles.textualPowerLevel(to, userDefault)
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
if (!diff.length) {
|
||||
return '';
|
||||
}
|
||||
return senderName + ' changed the power level of ' + diff.join(', ');
|
||||
return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
|
||||
senderName: senderName,
|
||||
powerLevelDiffText: diff.join(", ")
|
||||
});
|
||||
}
|
||||
|
||||
var handlers = {
|
||||
|
|
|
@ -22,7 +22,7 @@ let isDialogOpen = false;
|
|||
|
||||
const onAction = function(payload) {
|
||||
if (payload.action === 'unknown_device_error' && !isDialogOpen) {
|
||||
var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog");
|
||||
const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
|
||||
isDialogOpen = true;
|
||||
Modal.createDialog(UnknownDeviceDialog, {
|
||||
devices: payload.err.devices,
|
||||
|
@ -33,17 +33,17 @@ const onAction = function(payload) {
|
|||
// https://github.com/vector-im/riot-web/issues/3148
|
||||
console.log('UnknownDeviceDialog closed with '+r);
|
||||
},
|
||||
}, "mx_Dialog_unknownDevice");
|
||||
}, 'mx_Dialog_unknownDevice');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ref = null;
|
||||
|
||||
export function startListening () {
|
||||
export function startListening() {
|
||||
ref = dis.register(onAction);
|
||||
}
|
||||
|
||||
export function stopListening () {
|
||||
export function stopListening() {
|
||||
if (ref) {
|
||||
dis.unregister(ref);
|
||||
ref = null;
|
||||
|
|
|
@ -25,7 +25,9 @@ module.exports = {
|
|||
eventTriggersUnreadCount: function(ev) {
|
||||
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
|
||||
return false;
|
||||
} else if (ev.getType() == "m.room.member") {
|
||||
} else if (ev.getType() == 'm.room.member') {
|
||||
return false;
|
||||
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
|
||||
return false;
|
||||
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
||||
return false;
|
||||
|
|
|
@ -32,7 +32,7 @@ class UserActivity {
|
|||
start() {
|
||||
document.onmousedown = this._onUserActivity.bind(this);
|
||||
document.onmousemove = this._onUserActivity.bind(this);
|
||||
document.onkeypress = this._onUserActivity.bind(this);
|
||||
document.onkeydown = this._onUserActivity.bind(this);
|
||||
// can't use document.scroll here because that's only the document
|
||||
// itself being scrolled. Need to use addEventListener's useCapture.
|
||||
// also this needs to be the wheel event, not scroll, as scroll is
|
||||
|
@ -50,7 +50,7 @@ class UserActivity {
|
|||
stop() {
|
||||
document.onmousedown = undefined;
|
||||
document.onmousemove = undefined;
|
||||
document.onkeypress = undefined;
|
||||
document.onkeydown = undefined;
|
||||
window.removeEventListener('wheel', this._onUserActivity.bind(this),
|
||||
{ passive: true, capture: true });
|
||||
}
|
||||
|
|
|
@ -14,26 +14,29 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var q = require("q");
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var Notifier = require("./Notifier");
|
||||
import q from 'q';
|
||||
import MatrixClientPeg from './MatrixClientPeg';
|
||||
import Notifier from './Notifier';
|
||||
|
||||
/*
|
||||
* TODO: Make things use this. This is all WIP - see UserSettings.js for usage.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
* TODO: Find a way to translate the names of LABS_FEATURES. In other words, guarantee that languages were already loaded before building this array.
|
||||
*/
|
||||
|
||||
export default {
|
||||
LABS_FEATURES: [
|
||||
{
|
||||
name: 'New Composer & Autocomplete',
|
||||
name: "New Composer & Autocomplete",
|
||||
id: 'rich_text_editor',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
|
||||
loadProfileInfo: function() {
|
||||
var cli = MatrixClientPeg.get();
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli.getProfileInfo(cli.credentials.userId);
|
||||
},
|
||||
|
||||
|
@ -44,7 +47,7 @@ module.exports = {
|
|||
loadThreePids: function() {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
return q({
|
||||
threepids: []
|
||||
threepids: [],
|
||||
}); // guests can't poke 3pid endpoint
|
||||
}
|
||||
return MatrixClientPeg.get().getThreePids();
|
||||
|
@ -73,19 +76,19 @@ module.exports = {
|
|||
Notifier.setAudioEnabled(enable);
|
||||
},
|
||||
|
||||
changePassword: function(old_password, new_password) {
|
||||
var cli = MatrixClientPeg.get();
|
||||
changePassword: function(oldPassword, newPassword) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
var authDict = {
|
||||
const authDict = {
|
||||
type: 'm.login.password',
|
||||
user: cli.credentials.userId,
|
||||
password: old_password
|
||||
password: oldPassword,
|
||||
};
|
||||
|
||||
return cli.setPassword(authDict, new_password);
|
||||
return cli.setPassword(authDict, newPassword);
|
||||
},
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns the email pusher (pusher of type 'email') for a given
|
||||
* email address. Email pushers all have the same app ID, so since
|
||||
* pushers are unique over (app ID, pushkey), there will be at most
|
||||
|
@ -95,8 +98,8 @@ module.exports = {
|
|||
if (pushers === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
for (var i = 0; i < pushers.length; ++i) {
|
||||
if (pushers[i].kind == 'email' && pushers[i].pushkey == address) {
|
||||
for (let i = 0; i < pushers.length; ++i) {
|
||||
if (pushers[i].kind === 'email' && pushers[i].pushkey === address) {
|
||||
return pushers[i];
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +113,7 @@ module.exports = {
|
|||
addEmailPusher: function(address, data) {
|
||||
return MatrixClientPeg.get().setPusher({
|
||||
kind: 'email',
|
||||
app_id: "m.email",
|
||||
app_id: 'm.email',
|
||||
pushkey: address,
|
||||
app_display_name: 'Email Notifications',
|
||||
device_display_name: address,
|
||||
|
@ -121,46 +124,46 @@ module.exports = {
|
|||
},
|
||||
|
||||
getUrlPreviewsDisabled: function() {
|
||||
var event = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
|
||||
const event = MatrixClientPeg.get().getAccountData('org.matrix.preview_urls');
|
||||
return (event && event.getContent().disable);
|
||||
},
|
||||
|
||||
setUrlPreviewsDisabled: function(disabled) {
|
||||
// FIXME: handle errors
|
||||
return MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", {
|
||||
disable: disabled
|
||||
return MatrixClientPeg.get().setAccountData('org.matrix.preview_urls', {
|
||||
disable: disabled,
|
||||
});
|
||||
},
|
||||
|
||||
getSyncedSettings: function() {
|
||||
var event = MatrixClientPeg.get().getAccountData("im.vector.web.settings");
|
||||
const event = MatrixClientPeg.get().getAccountData('im.vector.web.settings');
|
||||
return event ? event.getContent() : {};
|
||||
},
|
||||
|
||||
getSyncedSetting: function(type, defaultValue = null) {
|
||||
var settings = this.getSyncedSettings();
|
||||
return settings.hasOwnProperty(type) ? settings[type] : null;
|
||||
const settings = this.getSyncedSettings();
|
||||
return settings.hasOwnProperty(type) ? settings[type] : defaultValue;
|
||||
},
|
||||
|
||||
setSyncedSetting: function(type, value) {
|
||||
var settings = this.getSyncedSettings();
|
||||
const settings = this.getSyncedSettings();
|
||||
settings[type] = value;
|
||||
// FIXME: handle errors
|
||||
return MatrixClientPeg.get().setAccountData("im.vector.web.settings", settings);
|
||||
return MatrixClientPeg.get().setAccountData('im.vector.web.settings', settings);
|
||||
},
|
||||
|
||||
getLocalSettings: function() {
|
||||
var localSettingsString = localStorage.getItem('mx_local_settings') || '{}';
|
||||
const localSettingsString = localStorage.getItem('mx_local_settings') || '{}';
|
||||
return JSON.parse(localSettingsString);
|
||||
},
|
||||
|
||||
getLocalSetting: function(type, defaultValue = null) {
|
||||
var settings = this.getLocalSettings();
|
||||
return settings.hasOwnProperty(type) ? settings[type] : null;
|
||||
const settings = this.getLocalSettings();
|
||||
return settings.hasOwnProperty(type) ? settings[type] : defaultValue;
|
||||
},
|
||||
|
||||
setLocalSetting: function(type, value) {
|
||||
var settings = this.getLocalSettings();
|
||||
const settings = this.getLocalSettings();
|
||||
settings[type] = value;
|
||||
// FIXME: handle errors
|
||||
localStorage.setItem('mx_local_settings', JSON.stringify(settings));
|
||||
|
@ -171,8 +174,8 @@ module.exports = {
|
|||
if (MatrixClientPeg.get().isGuest()) return false;
|
||||
|
||||
if (localStorage.getItem(`mx_labs_feature_${feature}`) === null) {
|
||||
for (var i = 0; i < this.LABS_FEATURES.length; i++) {
|
||||
var f = this.LABS_FEATURES[i];
|
||||
for (let i = 0; i < this.LABS_FEATURES.length; i++) {
|
||||
const f = this.LABS_FEATURES[i];
|
||||
if (f.id === feature) {
|
||||
return f.default;
|
||||
}
|
||||
|
@ -183,5 +186,5 @@ module.exports = {
|
|||
|
||||
setFeatureEnabled: function(feature: string, enabled: boolean) {
|
||||
localStorage.setItem(`mx_labs_feature_${feature}`, enabled);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
import { _t } from './languageHandler';
|
||||
|
||||
module.exports = {
|
||||
usersTypingApartFromMe: function(room) {
|
||||
|
@ -56,18 +57,18 @@ module.exports = {
|
|||
if (whoIsTyping.length == 0) {
|
||||
return '';
|
||||
} else if (whoIsTyping.length == 1) {
|
||||
return whoIsTyping[0].name + ' is typing';
|
||||
return _t('%(displayName)s is typing', {displayName: whoIsTyping[0].name});
|
||||
}
|
||||
const names = whoIsTyping.map(function(m) {
|
||||
return m.name;
|
||||
});
|
||||
if (othersCount) {
|
||||
const other = ' other' + (othersCount > 1 ? 's' : '');
|
||||
return names.slice(0, limit - 1).join(', ') + ' and ' +
|
||||
othersCount + other + ' are typing';
|
||||
if (othersCount==1) {
|
||||
return _t('%(names)s and one other are typing', {names: names.slice(0, limit - 1).join(', ')});
|
||||
} else if (othersCount>1) {
|
||||
return _t('%(names)s and %(count)s others are typing', {names: names.slice(0, limit - 1).join(', '), count: othersCount});
|
||||
} else {
|
||||
const lastPerson = names.pop();
|
||||
return names.join(', ') + ' and ' + lastPerson + ' are typing';
|
||||
return _t('%(names)s and %(lastPerson)s are typing', {names: names.join(', '), lastPerson: lastPerson});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var React = require("react");
|
||||
import { _t } from '../../../languageHandler';
|
||||
var sdk = require('../../../index');
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
|
||||
|
@ -78,33 +79,33 @@ module.exports = React.createClass({
|
|||
_renderDeviceInfo: function() {
|
||||
var device = this.state.device;
|
||||
if (!device) {
|
||||
return (<i>unknown device</i>);
|
||||
return (<i>{ _t('unknown device') }</i>);
|
||||
}
|
||||
|
||||
var verificationStatus = (<b>NOT verified</b>);
|
||||
var verificationStatus = (<b>{ _t('NOT verified') }</b>);
|
||||
if (device.isBlocked()) {
|
||||
verificationStatus = (<b>Blacklisted</b>);
|
||||
verificationStatus = (<b>{ _t('Blacklisted') }</b>);
|
||||
} else if (device.isVerified()) {
|
||||
verificationStatus = "verified";
|
||||
verificationStatus = _t('verified');
|
||||
}
|
||||
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{ _t('Name') }</td>
|
||||
<td>{ device.getDisplayName() }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Device ID</td>
|
||||
<td>{ _t('Device ID') }</td>
|
||||
<td><code>{ device.deviceId }</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Verification</td>
|
||||
<td>{ _t('Verification') }</td>
|
||||
<td>{ verificationStatus }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ed25519 fingerprint</td>
|
||||
<td>{ _t('Ed25519 fingerprint') }</td>
|
||||
<td><code>{device.getFingerprint()}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -119,32 +120,32 @@ module.exports = React.createClass({
|
|||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>User ID</td>
|
||||
<td>{ _t('User ID') }</td>
|
||||
<td>{ event.getSender() }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Curve25519 identity key</td>
|
||||
<td><code>{ event.getSenderKey() || <i>none</i> }</code></td>
|
||||
<td>{ _t('Curve25519 identity key') }</td>
|
||||
<td><code>{ event.getSenderKey() || <i>{ _t('none') }</i> }</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Claimed Ed25519 fingerprint key</td>
|
||||
<td><code>{ event.getKeysClaimed().ed25519 || <i>none</i> }</code></td>
|
||||
<td>{ _t('Claimed Ed25519 fingerprint key') }</td>
|
||||
<td><code>{ event.getKeysClaimed().ed25519 || <i>{ _t('none') }</i> }</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Algorithm</td>
|
||||
<td>{ event.getWireContent().algorithm || <i>unencrypted</i> }</td>
|
||||
<td>{ _t('Algorithm') }</td>
|
||||
<td>{ event.getWireContent().algorithm || <i>{ _t('unencrypted') }</i> }</td>
|
||||
</tr>
|
||||
{
|
||||
event.getContent().msgtype === 'm.bad.encrypted' ? (
|
||||
<tr>
|
||||
<td>Decryption error</td>
|
||||
<td>{ _t('Decryption error') }</td>
|
||||
<td>{ event.getContent().body }</td>
|
||||
</tr>
|
||||
) : null
|
||||
}
|
||||
<tr>
|
||||
<td>Session ID</td>
|
||||
<td><code>{ event.getWireContent().session_id || <i>none</i> }</code></td>
|
||||
<td>{ _t('Session ID') }</td>
|
||||
<td><code>{ event.getWireContent().session_id || <i>{ _t('none') }</i> }</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -166,18 +167,18 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<div className="mx_EncryptedEventDialog" onKeyDown={ this.onKeyDown }>
|
||||
<div className="mx_Dialog_title">
|
||||
End-to-end encryption information
|
||||
{ _t('End-to-end encryption information') }
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<h4>Event information</h4>
|
||||
<h4>{ _t('Event information') }</h4>
|
||||
{this._renderEventInfo()}
|
||||
|
||||
<h4>Sender device information</h4>
|
||||
<h4>{ _t('Sender device information') }</h4>
|
||||
{this._renderDeviceInfo()}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button className="mx_Dialog_primary" onClick={ this.props.onFinished } autoFocus={ true }>
|
||||
OK
|
||||
{ _t('OK') }
|
||||
</button>
|
||||
{buttons}
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react';
|
||||
import { _t } from '../languageHandler';
|
||||
import AutocompleteProvider from './AutocompleteProvider';
|
||||
import Fuse from 'fuse.js';
|
||||
import {TextualCompletion} from './Components';
|
||||
|
||||
// Warning: Since the description string will be translated in _t(result.description), all these strings below must be in i18n/strings/en_EN.json file
|
||||
const COMMANDS = [
|
||||
{
|
||||
command: '/me',
|
||||
|
@ -68,7 +70,7 @@ export default class CommandProvider extends AutocompleteProvider {
|
|||
component: (<TextualCompletion
|
||||
title={result.command}
|
||||
subtitle={result.args}
|
||||
description={result.description}
|
||||
description={ t_(result.description) }
|
||||
/>),
|
||||
range,
|
||||
};
|
||||
|
@ -78,7 +80,7 @@ export default class CommandProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName() {
|
||||
return '*️⃣ Commands';
|
||||
return '*️⃣ ' + _t('Commands');
|
||||
}
|
||||
|
||||
static getInstance(): CommandProvider {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { _t } from '../languageHandler';
|
||||
import AutocompleteProvider from './AutocompleteProvider';
|
||||
import {emojioneList, shortnameToImage, shortnameToUnicode} from 'emojione';
|
||||
import Fuse from 'fuse.js';
|
||||
|
@ -14,7 +15,7 @@ let instance = null;
|
|||
export default class EmojiProvider extends AutocompleteProvider {
|
||||
constructor() {
|
||||
super(EMOJI_REGEX);
|
||||
this.fuse = new Fuse(EMOJI_SHORTNAMES);
|
||||
this.fuse = new Fuse(EMOJI_SHORTNAMES, {});
|
||||
}
|
||||
|
||||
async getCompletions(query: string, selection: SelectionRange) {
|
||||
|
@ -39,7 +40,7 @@ export default class EmojiProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName() {
|
||||
return '😃 Emoji';
|
||||
return '😃 ' + _t('Emoji');
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { _t } from '../languageHandler';
|
||||
import AutocompleteProvider from './AutocompleteProvider';
|
||||
import MatrixClientPeg from '../MatrixClientPeg';
|
||||
import Fuse from 'fuse.js';
|
||||
|
@ -50,7 +51,7 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName() {
|
||||
return '💬 Rooms';
|
||||
return '💬 ' + _t('Rooms');
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { _t } from '../languageHandler';
|
||||
import AutocompleteProvider from './AutocompleteProvider';
|
||||
import Q from 'q';
|
||||
import Fuse from 'fuse.js';
|
||||
|
@ -51,7 +52,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName() {
|
||||
return '👥 Users';
|
||||
return '👥 ' + _t('Users');
|
||||
}
|
||||
|
||||
setUserList(users) {
|
||||
|
|
|
@ -1,253 +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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* THIS FILE IS AUTO-GENERATED
|
||||
* You can edit it you like, but your changes will be overwritten,
|
||||
* so you'd just be trying to swim upstream like a salmon.
|
||||
* You are not a salmon.
|
||||
*
|
||||
* To update it, run:
|
||||
* ./reskindex.js -h header
|
||||
*/
|
||||
|
||||
module.exports.components = {};
|
||||
import structures$ContextualMenu from './components/structures/ContextualMenu';
|
||||
structures$ContextualMenu && (module.exports.components['structures.ContextualMenu'] = structures$ContextualMenu);
|
||||
import structures$CreateRoom from './components/structures/CreateRoom';
|
||||
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
|
||||
import structures$FilePanel from './components/structures/FilePanel';
|
||||
structures$FilePanel && (module.exports.components['structures.FilePanel'] = structures$FilePanel);
|
||||
import structures$InteractiveAuth from './components/structures/InteractiveAuth';
|
||||
structures$InteractiveAuth && (module.exports.components['structures.InteractiveAuth'] = structures$InteractiveAuth);
|
||||
import structures$LoggedInView from './components/structures/LoggedInView';
|
||||
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
|
||||
import structures$MatrixChat from './components/structures/MatrixChat';
|
||||
structures$MatrixChat && (module.exports.components['structures.MatrixChat'] = structures$MatrixChat);
|
||||
import structures$MessagePanel from './components/structures/MessagePanel';
|
||||
structures$MessagePanel && (module.exports.components['structures.MessagePanel'] = structures$MessagePanel);
|
||||
import structures$NotificationPanel from './components/structures/NotificationPanel';
|
||||
structures$NotificationPanel && (module.exports.components['structures.NotificationPanel'] = structures$NotificationPanel);
|
||||
import structures$RoomStatusBar from './components/structures/RoomStatusBar';
|
||||
structures$RoomStatusBar && (module.exports.components['structures.RoomStatusBar'] = structures$RoomStatusBar);
|
||||
import structures$RoomView from './components/structures/RoomView';
|
||||
structures$RoomView && (module.exports.components['structures.RoomView'] = structures$RoomView);
|
||||
import structures$ScrollPanel from './components/structures/ScrollPanel';
|
||||
structures$ScrollPanel && (module.exports.components['structures.ScrollPanel'] = structures$ScrollPanel);
|
||||
import structures$TimelinePanel from './components/structures/TimelinePanel';
|
||||
structures$TimelinePanel && (module.exports.components['structures.TimelinePanel'] = structures$TimelinePanel);
|
||||
import structures$UploadBar from './components/structures/UploadBar';
|
||||
structures$UploadBar && (module.exports.components['structures.UploadBar'] = structures$UploadBar);
|
||||
import structures$UserSettings from './components/structures/UserSettings';
|
||||
structures$UserSettings && (module.exports.components['structures.UserSettings'] = structures$UserSettings);
|
||||
import structures$login$ForgotPassword from './components/structures/login/ForgotPassword';
|
||||
structures$login$ForgotPassword && (module.exports.components['structures.login.ForgotPassword'] = structures$login$ForgotPassword);
|
||||
import structures$login$Login from './components/structures/login/Login';
|
||||
structures$login$Login && (module.exports.components['structures.login.Login'] = structures$login$Login);
|
||||
import structures$login$PostRegistration from './components/structures/login/PostRegistration';
|
||||
structures$login$PostRegistration && (module.exports.components['structures.login.PostRegistration'] = structures$login$PostRegistration);
|
||||
import structures$login$Registration from './components/structures/login/Registration';
|
||||
structures$login$Registration && (module.exports.components['structures.login.Registration'] = structures$login$Registration);
|
||||
import views$avatars$BaseAvatar from './components/views/avatars/BaseAvatar';
|
||||
views$avatars$BaseAvatar && (module.exports.components['views.avatars.BaseAvatar'] = views$avatars$BaseAvatar);
|
||||
import views$avatars$MemberAvatar from './components/views/avatars/MemberAvatar';
|
||||
views$avatars$MemberAvatar && (module.exports.components['views.avatars.MemberAvatar'] = views$avatars$MemberAvatar);
|
||||
import views$avatars$RoomAvatar from './components/views/avatars/RoomAvatar';
|
||||
views$avatars$RoomAvatar && (module.exports.components['views.avatars.RoomAvatar'] = views$avatars$RoomAvatar);
|
||||
import views$create_room$CreateRoomButton from './components/views/create_room/CreateRoomButton';
|
||||
views$create_room$CreateRoomButton && (module.exports.components['views.create_room.CreateRoomButton'] = views$create_room$CreateRoomButton);
|
||||
import views$create_room$Presets from './components/views/create_room/Presets';
|
||||
views$create_room$Presets && (module.exports.components['views.create_room.Presets'] = views$create_room$Presets);
|
||||
import views$create_room$RoomAlias from './components/views/create_room/RoomAlias';
|
||||
views$create_room$RoomAlias && (module.exports.components['views.create_room.RoomAlias'] = views$create_room$RoomAlias);
|
||||
import views$dialogs$BaseDialog from './components/views/dialogs/BaseDialog';
|
||||
views$dialogs$BaseDialog && (module.exports.components['views.dialogs.BaseDialog'] = views$dialogs$BaseDialog);
|
||||
import views$dialogs$ChatCreateOrReuseDialog from './components/views/dialogs/ChatCreateOrReuseDialog';
|
||||
views$dialogs$ChatCreateOrReuseDialog && (module.exports.components['views.dialogs.ChatCreateOrReuseDialog'] = views$dialogs$ChatCreateOrReuseDialog);
|
||||
import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInviteDialog';
|
||||
views$dialogs$ChatInviteDialog && (module.exports.components['views.dialogs.ChatInviteDialog'] = views$dialogs$ChatInviteDialog);
|
||||
import views$dialogs$ConfirmRedactDialog from './components/views/dialogs/ConfirmRedactDialog';
|
||||
views$dialogs$ConfirmRedactDialog && (module.exports.components['views.dialogs.ConfirmRedactDialog'] = views$dialogs$ConfirmRedactDialog);
|
||||
import views$dialogs$ConfirmUserActionDialog from './components/views/dialogs/ConfirmUserActionDialog';
|
||||
views$dialogs$ConfirmUserActionDialog && (module.exports.components['views.dialogs.ConfirmUserActionDialog'] = views$dialogs$ConfirmUserActionDialog);
|
||||
import views$dialogs$DeactivateAccountDialog from './components/views/dialogs/DeactivateAccountDialog';
|
||||
views$dialogs$DeactivateAccountDialog && (module.exports.components['views.dialogs.DeactivateAccountDialog'] = views$dialogs$DeactivateAccountDialog);
|
||||
import views$dialogs$ErrorDialog from './components/views/dialogs/ErrorDialog';
|
||||
views$dialogs$ErrorDialog && (module.exports.components['views.dialogs.ErrorDialog'] = views$dialogs$ErrorDialog);
|
||||
import views$dialogs$InteractiveAuthDialog from './components/views/dialogs/InteractiveAuthDialog';
|
||||
views$dialogs$InteractiveAuthDialog && (module.exports.components['views.dialogs.InteractiveAuthDialog'] = views$dialogs$InteractiveAuthDialog);
|
||||
import views$dialogs$NeedToRegisterDialog from './components/views/dialogs/NeedToRegisterDialog';
|
||||
views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog);
|
||||
import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog';
|
||||
views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = views$dialogs$QuestionDialog);
|
||||
import views$dialogs$SessionRestoreErrorDialog from './components/views/dialogs/SessionRestoreErrorDialog';
|
||||
views$dialogs$SessionRestoreErrorDialog && (module.exports.components['views.dialogs.SessionRestoreErrorDialog'] = views$dialogs$SessionRestoreErrorDialog);
|
||||
import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog';
|
||||
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
|
||||
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
|
||||
views$dialogs$TextInputDialog && (module.exports.components['views.dialogs.TextInputDialog'] = views$dialogs$TextInputDialog);
|
||||
import views$dialogs$UnknownDeviceDialog from './components/views/dialogs/UnknownDeviceDialog';
|
||||
views$dialogs$UnknownDeviceDialog && (module.exports.components['views.dialogs.UnknownDeviceDialog'] = views$dialogs$UnknownDeviceDialog);
|
||||
import views$elements$AccessibleButton from './components/views/elements/AccessibleButton';
|
||||
views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
|
||||
import views$elements$AddressSelector from './components/views/elements/AddressSelector';
|
||||
views$elements$AddressSelector && (module.exports.components['views.elements.AddressSelector'] = views$elements$AddressSelector);
|
||||
import views$elements$AddressTile from './components/views/elements/AddressTile';
|
||||
views$elements$AddressTile && (module.exports.components['views.elements.AddressTile'] = views$elements$AddressTile);
|
||||
import views$elements$DeviceVerifyButtons from './components/views/elements/DeviceVerifyButtons';
|
||||
views$elements$DeviceVerifyButtons && (module.exports.components['views.elements.DeviceVerifyButtons'] = views$elements$DeviceVerifyButtons);
|
||||
import views$elements$DirectorySearchBox from './components/views/elements/DirectorySearchBox';
|
||||
views$elements$DirectorySearchBox && (module.exports.components['views.elements.DirectorySearchBox'] = views$elements$DirectorySearchBox);
|
||||
import views$elements$Dropdown from './components/views/elements/Dropdown';
|
||||
views$elements$Dropdown && (module.exports.components['views.elements.Dropdown'] = views$elements$Dropdown);
|
||||
import views$elements$EditableText from './components/views/elements/EditableText';
|
||||
views$elements$EditableText && (module.exports.components['views.elements.EditableText'] = views$elements$EditableText);
|
||||
import views$elements$EditableTextContainer from './components/views/elements/EditableTextContainer';
|
||||
views$elements$EditableTextContainer && (module.exports.components['views.elements.EditableTextContainer'] = views$elements$EditableTextContainer);
|
||||
import views$elements$EmojiText from './components/views/elements/EmojiText';
|
||||
views$elements$EmojiText && (module.exports.components['views.elements.EmojiText'] = views$elements$EmojiText);
|
||||
import views$elements$MemberEventListSummary from './components/views/elements/MemberEventListSummary';
|
||||
views$elements$MemberEventListSummary && (module.exports.components['views.elements.MemberEventListSummary'] = views$elements$MemberEventListSummary);
|
||||
import views$elements$PowerSelector from './components/views/elements/PowerSelector';
|
||||
views$elements$PowerSelector && (module.exports.components['views.elements.PowerSelector'] = views$elements$PowerSelector);
|
||||
import views$elements$ProgressBar from './components/views/elements/ProgressBar';
|
||||
views$elements$ProgressBar && (module.exports.components['views.elements.ProgressBar'] = views$elements$ProgressBar);
|
||||
import views$elements$TintableSvg from './components/views/elements/TintableSvg';
|
||||
views$elements$TintableSvg && (module.exports.components['views.elements.TintableSvg'] = views$elements$TintableSvg);
|
||||
import views$elements$TruncatedList from './components/views/elements/TruncatedList';
|
||||
views$elements$TruncatedList && (module.exports.components['views.elements.TruncatedList'] = views$elements$TruncatedList);
|
||||
import views$elements$UserSelector from './components/views/elements/UserSelector';
|
||||
views$elements$UserSelector && (module.exports.components['views.elements.UserSelector'] = views$elements$UserSelector);
|
||||
import views$login$CaptchaForm from './components/views/login/CaptchaForm';
|
||||
views$login$CaptchaForm && (module.exports.components['views.login.CaptchaForm'] = views$login$CaptchaForm);
|
||||
import views$login$CasLogin from './components/views/login/CasLogin';
|
||||
views$login$CasLogin && (module.exports.components['views.login.CasLogin'] = views$login$CasLogin);
|
||||
import views$login$CountryDropdown from './components/views/login/CountryDropdown';
|
||||
views$login$CountryDropdown && (module.exports.components['views.login.CountryDropdown'] = views$login$CountryDropdown);
|
||||
import views$login$CustomServerDialog from './components/views/login/CustomServerDialog';
|
||||
views$login$CustomServerDialog && (module.exports.components['views.login.CustomServerDialog'] = views$login$CustomServerDialog);
|
||||
import views$login$InteractiveAuthEntryComponents from './components/views/login/InteractiveAuthEntryComponents';
|
||||
views$login$InteractiveAuthEntryComponents && (module.exports.components['views.login.InteractiveAuthEntryComponents'] = views$login$InteractiveAuthEntryComponents);
|
||||
import views$login$LoginFooter from './components/views/login/LoginFooter';
|
||||
views$login$LoginFooter && (module.exports.components['views.login.LoginFooter'] = views$login$LoginFooter);
|
||||
import views$login$LoginHeader from './components/views/login/LoginHeader';
|
||||
views$login$LoginHeader && (module.exports.components['views.login.LoginHeader'] = views$login$LoginHeader);
|
||||
import views$login$PasswordLogin from './components/views/login/PasswordLogin';
|
||||
views$login$PasswordLogin && (module.exports.components['views.login.PasswordLogin'] = views$login$PasswordLogin);
|
||||
import views$login$RegistrationForm from './components/views/login/RegistrationForm';
|
||||
views$login$RegistrationForm && (module.exports.components['views.login.RegistrationForm'] = views$login$RegistrationForm);
|
||||
import views$login$ServerConfig from './components/views/login/ServerConfig';
|
||||
views$login$ServerConfig && (module.exports.components['views.login.ServerConfig'] = views$login$ServerConfig);
|
||||
import views$messages$MAudioBody from './components/views/messages/MAudioBody';
|
||||
views$messages$MAudioBody && (module.exports.components['views.messages.MAudioBody'] = views$messages$MAudioBody);
|
||||
import views$messages$MFileBody from './components/views/messages/MFileBody';
|
||||
views$messages$MFileBody && (module.exports.components['views.messages.MFileBody'] = views$messages$MFileBody);
|
||||
import views$messages$MImageBody from './components/views/messages/MImageBody';
|
||||
views$messages$MImageBody && (module.exports.components['views.messages.MImageBody'] = views$messages$MImageBody);
|
||||
import views$messages$MVideoBody from './components/views/messages/MVideoBody';
|
||||
views$messages$MVideoBody && (module.exports.components['views.messages.MVideoBody'] = views$messages$MVideoBody);
|
||||
import views$messages$MessageEvent from './components/views/messages/MessageEvent';
|
||||
views$messages$MessageEvent && (module.exports.components['views.messages.MessageEvent'] = views$messages$MessageEvent);
|
||||
import views$messages$SenderProfile from './components/views/messages/SenderProfile';
|
||||
views$messages$SenderProfile && (module.exports.components['views.messages.SenderProfile'] = views$messages$SenderProfile);
|
||||
import views$messages$TextualBody from './components/views/messages/TextualBody';
|
||||
views$messages$TextualBody && (module.exports.components['views.messages.TextualBody'] = views$messages$TextualBody);
|
||||
import views$messages$TextualEvent from './components/views/messages/TextualEvent';
|
||||
views$messages$TextualEvent && (module.exports.components['views.messages.TextualEvent'] = views$messages$TextualEvent);
|
||||
import views$messages$UnknownBody from './components/views/messages/UnknownBody';
|
||||
views$messages$UnknownBody && (module.exports.components['views.messages.UnknownBody'] = views$messages$UnknownBody);
|
||||
import views$room_settings$AliasSettings from './components/views/room_settings/AliasSettings';
|
||||
views$room_settings$AliasSettings && (module.exports.components['views.room_settings.AliasSettings'] = views$room_settings$AliasSettings);
|
||||
import views$room_settings$ColorSettings from './components/views/room_settings/ColorSettings';
|
||||
views$room_settings$ColorSettings && (module.exports.components['views.room_settings.ColorSettings'] = views$room_settings$ColorSettings);
|
||||
import views$room_settings$UrlPreviewSettings from './components/views/room_settings/UrlPreviewSettings';
|
||||
views$room_settings$UrlPreviewSettings && (module.exports.components['views.room_settings.UrlPreviewSettings'] = views$room_settings$UrlPreviewSettings);
|
||||
import views$rooms$Autocomplete from './components/views/rooms/Autocomplete';
|
||||
views$rooms$Autocomplete && (module.exports.components['views.rooms.Autocomplete'] = views$rooms$Autocomplete);
|
||||
import views$rooms$AuxPanel from './components/views/rooms/AuxPanel';
|
||||
views$rooms$AuxPanel && (module.exports.components['views.rooms.AuxPanel'] = views$rooms$AuxPanel);
|
||||
import views$rooms$EntityTile from './components/views/rooms/EntityTile';
|
||||
views$rooms$EntityTile && (module.exports.components['views.rooms.EntityTile'] = views$rooms$EntityTile);
|
||||
import views$rooms$EventTile from './components/views/rooms/EventTile';
|
||||
views$rooms$EventTile && (module.exports.components['views.rooms.EventTile'] = views$rooms$EventTile);
|
||||
import views$rooms$LinkPreviewWidget from './components/views/rooms/LinkPreviewWidget';
|
||||
views$rooms$LinkPreviewWidget && (module.exports.components['views.rooms.LinkPreviewWidget'] = views$rooms$LinkPreviewWidget);
|
||||
import views$rooms$MemberDeviceInfo from './components/views/rooms/MemberDeviceInfo';
|
||||
views$rooms$MemberDeviceInfo && (module.exports.components['views.rooms.MemberDeviceInfo'] = views$rooms$MemberDeviceInfo);
|
||||
import views$rooms$MemberInfo from './components/views/rooms/MemberInfo';
|
||||
views$rooms$MemberInfo && (module.exports.components['views.rooms.MemberInfo'] = views$rooms$MemberInfo);
|
||||
import views$rooms$MemberList from './components/views/rooms/MemberList';
|
||||
views$rooms$MemberList && (module.exports.components['views.rooms.MemberList'] = views$rooms$MemberList);
|
||||
import views$rooms$MemberTile from './components/views/rooms/MemberTile';
|
||||
views$rooms$MemberTile && (module.exports.components['views.rooms.MemberTile'] = views$rooms$MemberTile);
|
||||
import views$rooms$MessageComposer from './components/views/rooms/MessageComposer';
|
||||
views$rooms$MessageComposer && (module.exports.components['views.rooms.MessageComposer'] = views$rooms$MessageComposer);
|
||||
import views$rooms$MessageComposerInput from './components/views/rooms/MessageComposerInput';
|
||||
views$rooms$MessageComposerInput && (module.exports.components['views.rooms.MessageComposerInput'] = views$rooms$MessageComposerInput);
|
||||
import views$rooms$MessageComposerInputOld from './components/views/rooms/MessageComposerInputOld';
|
||||
views$rooms$MessageComposerInputOld && (module.exports.components['views.rooms.MessageComposerInputOld'] = views$rooms$MessageComposerInputOld);
|
||||
import views$rooms$PresenceLabel from './components/views/rooms/PresenceLabel';
|
||||
views$rooms$PresenceLabel && (module.exports.components['views.rooms.PresenceLabel'] = views$rooms$PresenceLabel);
|
||||
import views$rooms$ReadReceiptMarker from './components/views/rooms/ReadReceiptMarker';
|
||||
views$rooms$ReadReceiptMarker && (module.exports.components['views.rooms.ReadReceiptMarker'] = views$rooms$ReadReceiptMarker);
|
||||
import views$rooms$RoomHeader from './components/views/rooms/RoomHeader';
|
||||
views$rooms$RoomHeader && (module.exports.components['views.rooms.RoomHeader'] = views$rooms$RoomHeader);
|
||||
import views$rooms$RoomList from './components/views/rooms/RoomList';
|
||||
views$rooms$RoomList && (module.exports.components['views.rooms.RoomList'] = views$rooms$RoomList);
|
||||
import views$rooms$RoomNameEditor from './components/views/rooms/RoomNameEditor';
|
||||
views$rooms$RoomNameEditor && (module.exports.components['views.rooms.RoomNameEditor'] = views$rooms$RoomNameEditor);
|
||||
import views$rooms$RoomPreviewBar from './components/views/rooms/RoomPreviewBar';
|
||||
views$rooms$RoomPreviewBar && (module.exports.components['views.rooms.RoomPreviewBar'] = views$rooms$RoomPreviewBar);
|
||||
import views$rooms$RoomSettings from './components/views/rooms/RoomSettings';
|
||||
views$rooms$RoomSettings && (module.exports.components['views.rooms.RoomSettings'] = views$rooms$RoomSettings);
|
||||
import views$rooms$RoomTile from './components/views/rooms/RoomTile';
|
||||
views$rooms$RoomTile && (module.exports.components['views.rooms.RoomTile'] = views$rooms$RoomTile);
|
||||
import views$rooms$RoomTopicEditor from './components/views/rooms/RoomTopicEditor';
|
||||
views$rooms$RoomTopicEditor && (module.exports.components['views.rooms.RoomTopicEditor'] = views$rooms$RoomTopicEditor);
|
||||
import views$rooms$SearchResultTile from './components/views/rooms/SearchResultTile';
|
||||
views$rooms$SearchResultTile && (module.exports.components['views.rooms.SearchResultTile'] = views$rooms$SearchResultTile);
|
||||
import views$rooms$SearchableEntityList from './components/views/rooms/SearchableEntityList';
|
||||
views$rooms$SearchableEntityList && (module.exports.components['views.rooms.SearchableEntityList'] = views$rooms$SearchableEntityList);
|
||||
import views$rooms$SimpleRoomHeader from './components/views/rooms/SimpleRoomHeader';
|
||||
views$rooms$SimpleRoomHeader && (module.exports.components['views.rooms.SimpleRoomHeader'] = views$rooms$SimpleRoomHeader);
|
||||
import views$rooms$TabCompleteBar from './components/views/rooms/TabCompleteBar';
|
||||
views$rooms$TabCompleteBar && (module.exports.components['views.rooms.TabCompleteBar'] = views$rooms$TabCompleteBar);
|
||||
import views$rooms$TopUnreadMessagesBar from './components/views/rooms/TopUnreadMessagesBar';
|
||||
views$rooms$TopUnreadMessagesBar && (module.exports.components['views.rooms.TopUnreadMessagesBar'] = views$rooms$TopUnreadMessagesBar);
|
||||
import views$rooms$UserTile from './components/views/rooms/UserTile';
|
||||
views$rooms$UserTile && (module.exports.components['views.rooms.UserTile'] = views$rooms$UserTile);
|
||||
import views$settings$AddPhoneNumber from './components/views/settings/AddPhoneNumber';
|
||||
views$settings$AddPhoneNumber && (module.exports.components['views.settings.AddPhoneNumber'] = views$settings$AddPhoneNumber);
|
||||
import views$settings$ChangeAvatar from './components/views/settings/ChangeAvatar';
|
||||
views$settings$ChangeAvatar && (module.exports.components['views.settings.ChangeAvatar'] = views$settings$ChangeAvatar);
|
||||
import views$settings$ChangeDisplayName from './components/views/settings/ChangeDisplayName';
|
||||
views$settings$ChangeDisplayName && (module.exports.components['views.settings.ChangeDisplayName'] = views$settings$ChangeDisplayName);
|
||||
import views$settings$ChangePassword from './components/views/settings/ChangePassword';
|
||||
views$settings$ChangePassword && (module.exports.components['views.settings.ChangePassword'] = views$settings$ChangePassword);
|
||||
import views$settings$DevicesPanel from './components/views/settings/DevicesPanel';
|
||||
views$settings$DevicesPanel && (module.exports.components['views.settings.DevicesPanel'] = views$settings$DevicesPanel);
|
||||
import views$settings$DevicesPanelEntry from './components/views/settings/DevicesPanelEntry';
|
||||
views$settings$DevicesPanelEntry && (module.exports.components['views.settings.DevicesPanelEntry'] = views$settings$DevicesPanelEntry);
|
||||
import views$settings$EnableNotificationsButton from './components/views/settings/EnableNotificationsButton';
|
||||
views$settings$EnableNotificationsButton && (module.exports.components['views.settings.EnableNotificationsButton'] = views$settings$EnableNotificationsButton);
|
||||
import views$voip$CallView from './components/views/voip/CallView';
|
||||
views$voip$CallView && (module.exports.components['views.voip.CallView'] = views$voip$CallView);
|
||||
import views$voip$IncomingCallBox from './components/views/voip/IncomingCallBox';
|
||||
views$voip$IncomingCallBox && (module.exports.components['views.voip.IncomingCallBox'] = views$voip$IncomingCallBox);
|
||||
import views$voip$VideoFeed from './components/views/voip/VideoFeed';
|
||||
views$voip$VideoFeed && (module.exports.components['views.voip.VideoFeed'] = views$voip$VideoFeed);
|
||||
import views$voip$VideoView from './components/views/voip/VideoView';
|
||||
views$voip$VideoView && (module.exports.components['views.voip.VideoView'] = views$voip$VideoView);
|
|
@ -16,15 +16,16 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require("react");
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var PresetValues = {
|
||||
import React from 'react';
|
||||
import q from 'q';
|
||||
import { _t } from '../../languageHandler';
|
||||
import sdk from '../../index';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
const PresetValues = {
|
||||
PrivateChat: "private_chat",
|
||||
PublicChat: "public_chat",
|
||||
Custom: "custom",
|
||||
};
|
||||
var q = require('q');
|
||||
var sdk = require('../../index');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoom',
|
||||
|
@ -231,7 +232,7 @@ module.exports = React.createClass({
|
|||
if (curr_phase == this.phases.ERROR) {
|
||||
error_box = (
|
||||
<div className="mx_Error">
|
||||
An error occured: {this.state.error_string}
|
||||
{_t('An error occured: %(error_string)s', {error_string: this.state.error_string})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -248,27 +249,27 @@ module.exports = React.createClass({
|
|||
<div className="mx_CreateRoom">
|
||||
<SimpleRoomHeader title="CreateRoom" collapsedRhs={ this.props.collapsedRhs }/>
|
||||
<div className="mx_CreateRoom_body">
|
||||
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br />
|
||||
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Topic"/> <br />
|
||||
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder={_t('Name')}/> <br />
|
||||
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder={_t('Topic')}/> <br />
|
||||
<RoomAlias ref="alias" alias={this.state.alias} homeserver={ domain } onChange={this.onAliasChanged}/> <br />
|
||||
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
|
||||
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/>
|
||||
Make this room private
|
||||
{_t('Make this room private')}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/>
|
||||
Share message history with new users
|
||||
{_t('Share message history with new users')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="mx_CreateRoom_encrypt">
|
||||
<label>
|
||||
<input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/>
|
||||
Encrypt room
|
||||
{_t('Encrypt room')}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require("react-dom");
|
||||
import React from 'react';
|
||||
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var sdk = require('../../index');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var dis = require("../../dispatcher");
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import sdk from '../../index';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
/*
|
||||
* Component which shows the filtered file using a TimelinePanel
|
||||
|
@ -59,6 +58,8 @@ var FilePanel = React.createClass({
|
|||
var client = MatrixClientPeg.get();
|
||||
var room = client.getRoom(roomId);
|
||||
|
||||
this.noRoom = !room;
|
||||
|
||||
if (room) {
|
||||
var filter = new Matrix.Filter(client.credentials.userId);
|
||||
filter.setDefinition(
|
||||
|
@ -82,13 +83,22 @@ var FilePanel = React.createClass({
|
|||
console.error("Failed to get or create file panel filter", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
|
||||
<div className="mx_RoomView_empty">You must <a href="#/register">register</a> to use this functionality</div>
|
||||
</div>;
|
||||
} else if (this.noRoom) {
|
||||
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
|
||||
<div className="mx_RoomView_empty">You must join the room to see its files</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
||||
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
|
@ -105,7 +115,7 @@ var FilePanel = React.createClass({
|
|||
showUrlPreview = { false }
|
||||
tileShape="file_grid"
|
||||
opacity={ this.props.opacity }
|
||||
empty="There are no visible files in this room"
|
||||
empty={_t('There are no visible files in this room')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -106,18 +107,6 @@ export default React.createClass({
|
|||
var handled = false;
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case KeyCode.ESCAPE:
|
||||
|
||||
// Implemented this way so possible handling for other pages is neater
|
||||
switch (this.props.page_type) {
|
||||
case PageTypes.UserSettings:
|
||||
this.props.onUserSettingsClose();
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KeyCode.UP:
|
||||
case KeyCode.DOWN:
|
||||
if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
|
||||
|
@ -162,19 +151,19 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var LeftPanel = sdk.getComponent('structures.LeftPanel');
|
||||
var RightPanel = sdk.getComponent('structures.RightPanel');
|
||||
var RoomView = sdk.getComponent('structures.RoomView');
|
||||
var UserSettings = sdk.getComponent('structures.UserSettings');
|
||||
var CreateRoom = sdk.getComponent('structures.CreateRoom');
|
||||
var RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
var HomePage = sdk.getComponent('structures.HomePage');
|
||||
var MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
||||
var GuestWarningBar = sdk.getComponent('globals.GuestWarningBar');
|
||||
var NewVersionBar = sdk.getComponent('globals.NewVersionBar');
|
||||
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
||||
const RightPanel = sdk.getComponent('structures.RightPanel');
|
||||
const RoomView = sdk.getComponent('structures.RoomView');
|
||||
const UserSettings = sdk.getComponent('structures.UserSettings');
|
||||
const CreateRoom = sdk.getComponent('structures.CreateRoom');
|
||||
const RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
const HomePage = sdk.getComponent('structures.HomePage');
|
||||
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
||||
const GuestWarningBar = sdk.getComponent('globals.GuestWarningBar');
|
||||
const NewVersionBar = sdk.getComponent('globals.NewVersionBar');
|
||||
|
||||
var page_element;
|
||||
var right_panel = '';
|
||||
let page_element;
|
||||
let right_panel = '';
|
||||
|
||||
switch (this.props.page_type) {
|
||||
case PageTypes.RoomView:
|
||||
|
@ -194,7 +183,7 @@ export default React.createClass({
|
|||
ConferenceHandler={this.props.ConferenceHandler}
|
||||
scrollStateMap={this._scrollStateMap}
|
||||
/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.sideOpacity} />;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.rightOpacity} />;
|
||||
break;
|
||||
|
||||
case PageTypes.UserSettings:
|
||||
|
@ -206,7 +195,7 @@ export default React.createClass({
|
|||
referralBaseUrl={this.props.config.referralBaseUrl}
|
||||
teamToken={this.props.teamToken}
|
||||
/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
|
||||
break;
|
||||
|
||||
case PageTypes.CreateRoom:
|
||||
|
@ -214,16 +203,14 @@ export default React.createClass({
|
|||
onRoomCreated={this.props.onRoomCreated}
|
||||
collapsedRhs={this.props.collapse_rhs}
|
||||
/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
|
||||
break;
|
||||
|
||||
case PageTypes.RoomDirectory:
|
||||
page_element = <RoomDirectory
|
||||
ref="roomDirectory"
|
||||
collapsedRhs={this.props.collapse_rhs}
|
||||
config={this.props.config.roomDirectory}
|
||||
/>;
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>;
|
||||
break;
|
||||
|
||||
case PageTypes.HomePage:
|
||||
|
@ -232,12 +219,12 @@ export default React.createClass({
|
|||
teamServerUrl={this.props.config.teamServerConfig.teamServerURL}
|
||||
teamToken={this.props.teamToken}
|
||||
/>
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>
|
||||
if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>
|
||||
break;
|
||||
|
||||
case PageTypes.UserView:
|
||||
page_element = null; // deliberately null for now
|
||||
right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.sideOpacity} />;
|
||||
right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.rightOpacity} />;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -266,7 +253,7 @@ export default React.createClass({
|
|||
<LeftPanel
|
||||
selectedRoom={this.props.currentRoomId}
|
||||
collapsed={this.props.collapse_lhs || false}
|
||||
opacity={this.props.sideOpacity}
|
||||
opacity={this.props.leftOpacity}
|
||||
teamToken={this.props.teamToken}
|
||||
/>
|
||||
<main className='mx_MatrixChat_middlePanel'>
|
||||
|
|
|
@ -17,28 +17,26 @@ limitations under the License.
|
|||
|
||||
import q from 'q';
|
||||
|
||||
var React = require('react');
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
import React from 'react';
|
||||
import Matrix from "matrix-js-sdk";
|
||||
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var PlatformPeg = require("../../PlatformPeg");
|
||||
var SdkConfig = require("../../SdkConfig");
|
||||
var ContextualMenu = require("./ContextualMenu");
|
||||
var RoomListSorter = require("../../RoomListSorter");
|
||||
var UserActivity = require("../../UserActivity");
|
||||
var Presence = require("../../Presence");
|
||||
var dis = require("../../dispatcher");
|
||||
import MatrixClientPeg from "../../MatrixClientPeg";
|
||||
import PlatformPeg from "../../PlatformPeg";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import * as RoomListSorter from "../../RoomListSorter";
|
||||
import dis from "../../dispatcher";
|
||||
|
||||
var Modal = require("../../Modal");
|
||||
var Tinter = require("../../Tinter");
|
||||
var sdk = require('../../index');
|
||||
var Rooms = require('../../Rooms');
|
||||
var linkifyMatrix = require("../../linkify-matrix");
|
||||
var Lifecycle = require('../../Lifecycle');
|
||||
var PageTypes = require('../../PageTypes');
|
||||
import Modal from "../../Modal";
|
||||
import Tinter from "../../Tinter";
|
||||
import sdk from '../../index';
|
||||
import * as Rooms from '../../Rooms';
|
||||
import linkifyMatrix from "../../linkify-matrix";
|
||||
import * as Lifecycle from '../../Lifecycle';
|
||||
import PageTypes from '../../PageTypes';
|
||||
|
||||
var createRoom = require("../../createRoom");
|
||||
import createRoom from "../../createRoom";
|
||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MatrixChat',
|
||||
|
@ -89,7 +87,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var s = {
|
||||
const s = {
|
||||
loading: true,
|
||||
screen: undefined,
|
||||
screenAfterLogin: this.props.initialScreenAfterLogin,
|
||||
|
@ -119,8 +117,9 @@ module.exports = React.createClass({
|
|||
collapse_rhs: false,
|
||||
ready: false,
|
||||
width: 10000,
|
||||
sideOpacity: 1.0,
|
||||
leftOpacity: 1.0,
|
||||
middleOpacity: 1.0,
|
||||
rightOpacity: 1.0,
|
||||
|
||||
version: null,
|
||||
newVersion: null,
|
||||
|
@ -156,11 +155,9 @@ module.exports = React.createClass({
|
|||
return this.state.register_hs_url;
|
||||
} else if (MatrixClientPeg.get()) {
|
||||
return MatrixClientPeg.get().getHomeserverUrl();
|
||||
}
|
||||
else if (window.localStorage && window.localStorage.getItem("mx_hs_url")) {
|
||||
} else if (window.localStorage && window.localStorage.getItem("mx_hs_url")) {
|
||||
return window.localStorage.getItem("mx_hs_url");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return this.getDefaultHsUrl();
|
||||
}
|
||||
},
|
||||
|
@ -178,11 +175,9 @@ module.exports = React.createClass({
|
|||
return this.state.register_is_url;
|
||||
} else if (MatrixClientPeg.get()) {
|
||||
return MatrixClientPeg.get().getIdentityServerUrl();
|
||||
}
|
||||
else if (window.localStorage && window.localStorage.getItem("mx_is_url")) {
|
||||
} else if (window.localStorage && window.localStorage.getItem("mx_is_url")) {
|
||||
return window.localStorage.getItem("mx_is_url");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return this.getDefaultIsUrl();
|
||||
}
|
||||
},
|
||||
|
@ -253,7 +248,6 @@ module.exports = React.createClass({
|
|||
UDEHandler.startListening();
|
||||
|
||||
this.focusComposer = false;
|
||||
window.addEventListener("focus", this.onFocus);
|
||||
|
||||
// this can technically be done anywhere but doing this here keeps all
|
||||
// the routing url path logic together.
|
||||
|
@ -324,28 +318,14 @@ module.exports = React.createClass({
|
|||
onAction: function(payload) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
var roomIndexDelta = 1;
|
||||
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
||||
|
||||
var self = this;
|
||||
switch (payload.action) {
|
||||
case 'logout':
|
||||
Lifecycle.logout();
|
||||
break;
|
||||
case 'start_registration':
|
||||
const params = payload.params || {};
|
||||
this.setStateForNewScreen({
|
||||
screen: 'register',
|
||||
// these params may be undefined, but if they are,
|
||||
// unset them from our state: we don't want to
|
||||
// resume a previous registration session if the
|
||||
// user just clicked 'register'
|
||||
register_client_secret: params.client_secret,
|
||||
register_session_id: params.session_id,
|
||||
register_hs_url: params.hs_url,
|
||||
register_is_url: params.is_url,
|
||||
register_id_sid: params.sid,
|
||||
});
|
||||
this.notifyNewScreen('register');
|
||||
this._startRegistration(payload.params || {});
|
||||
break;
|
||||
case 'start_login':
|
||||
if (MatrixClientPeg.get() &&
|
||||
|
@ -362,7 +342,7 @@ module.exports = React.createClass({
|
|||
break;
|
||||
case 'start_post_registration':
|
||||
this.setState({ // don't clobber loggedIn status
|
||||
screen: 'post_registration'
|
||||
screen: 'post_registration',
|
||||
});
|
||||
break;
|
||||
case 'start_upgrade_registration':
|
||||
|
@ -392,38 +372,12 @@ module.exports = React.createClass({
|
|||
this.notifyNewScreen('forgot_password');
|
||||
break;
|
||||
case 'leave_room':
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Leave room",
|
||||
description: "Are you sure you want to leave the room?",
|
||||
onFinished: (should_leave) => {
|
||||
if (should_leave) {
|
||||
const d = MatrixClientPeg.get().leave(payload.room_id);
|
||||
|
||||
// FIXME: controller shouldn't be loading a view :(
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
|
||||
|
||||
d.then(() => {
|
||||
modal.close();
|
||||
if (this.currentRoomId === payload.room_id) {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
}
|
||||
}, (err) => {
|
||||
modal.close();
|
||||
console.error("Failed to leave room " + payload.room_id + " " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to leave room",
|
||||
description: (err && err.message ? err.message : "Server may be unavailable, overloaded, or you hit a bug."),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
this._leaveRoom(payload.room_id);
|
||||
break;
|
||||
case 'reject_invite':
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Reject invitation",
|
||||
description: "Are you sure you want to reject the invitation?",
|
||||
title: _t('Reject invitation'),
|
||||
description: _t('Are you sure you want to reject the invitation?'),
|
||||
onFinished: (confirm) => {
|
||||
if (confirm) {
|
||||
// FIXME: controller shouldn't be loading a view :(
|
||||
|
@ -438,12 +392,12 @@ module.exports = React.createClass({
|
|||
}, (err) => {
|
||||
modal.close();
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to reject invitation",
|
||||
description: err.toString()
|
||||
title: _t('Failed to reject invitation'),
|
||||
description: err.toString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
break;
|
||||
case 'view_user':
|
||||
|
@ -468,30 +422,13 @@ module.exports = React.createClass({
|
|||
this._viewRoom(payload);
|
||||
break;
|
||||
case 'view_prev_room':
|
||||
roomIndexDelta = -1;
|
||||
this._viewNextRoom(-1);
|
||||
break;
|
||||
case 'view_next_room':
|
||||
var allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
MatrixClientPeg.get().getRooms()
|
||||
);
|
||||
var roomIndex = -1;
|
||||
for (var i = 0; i < allRooms.length; ++i) {
|
||||
if (allRooms[i].roomId == this.state.currentRoomId) {
|
||||
roomIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
|
||||
if (roomIndex < 0) roomIndex = allRooms.length - 1;
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
this._viewNextRoom(1);
|
||||
break;
|
||||
case 'view_indexed_room':
|
||||
var allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
MatrixClientPeg.get().getRooms()
|
||||
);
|
||||
var roomIndex = payload.roomIndex;
|
||||
if (allRooms[roomIndex]) {
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
}
|
||||
this._viewIndexedRoom(payload.roomIndex);
|
||||
break;
|
||||
case 'view_user_settings':
|
||||
this._setPage(PageTypes.UserSettings);
|
||||
|
@ -500,19 +437,17 @@ module.exports = React.createClass({
|
|||
case 'view_create_room':
|
||||
//this._setPage(PageTypes.CreateRoom);
|
||||
//this.notifyNewScreen('new');
|
||||
|
||||
var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
||||
Modal.createDialog(TextInputDialog, {
|
||||
title: "Create Room",
|
||||
description: "Room name (optional)",
|
||||
button: "Create Room",
|
||||
title: _t('Create Room'),
|
||||
description: _t('Room name (optional)'),
|
||||
button: _t('Create Room'),
|
||||
onFinished: (should_create, name) => {
|
||||
if (should_create) {
|
||||
const createOpts = {};
|
||||
if (name) createOpts.name = name;
|
||||
createRoom({createOpts}).done();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
break;
|
||||
case 'view_room_directory':
|
||||
|
@ -556,12 +491,14 @@ module.exports = React.createClass({
|
|||
collapse_rhs: false,
|
||||
});
|
||||
break;
|
||||
case 'ui_opacity':
|
||||
case 'ui_opacity': {
|
||||
const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0;
|
||||
this.setState({
|
||||
sideOpacity: payload.sideOpacity,
|
||||
middleOpacity: payload.middleOpacity,
|
||||
leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault,
|
||||
middleOpacity: payload.middleOpacity || 1.0,
|
||||
rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault,
|
||||
});
|
||||
break;
|
||||
break; }
|
||||
case 'set_theme':
|
||||
this._onSetTheme(payload.value);
|
||||
break;
|
||||
|
@ -583,7 +520,7 @@ module.exports = React.createClass({
|
|||
case 'new_version':
|
||||
this.onVersion(
|
||||
payload.currentVersion, payload.newVersion,
|
||||
payload.releaseNotes
|
||||
payload.releaseNotes,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -595,6 +532,47 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_startRegistration: function(params) {
|
||||
this.setStateForNewScreen({
|
||||
screen: 'register',
|
||||
// these params may be undefined, but if they are,
|
||||
// unset them from our state: we don't want to
|
||||
// resume a previous registration session if the
|
||||
// user just clicked 'register'
|
||||
register_client_secret: params.client_secret,
|
||||
register_session_id: params.session_id,
|
||||
register_hs_url: params.hs_url,
|
||||
register_is_url: params.is_url,
|
||||
register_id_sid: params.sid,
|
||||
});
|
||||
this.notifyNewScreen('register');
|
||||
},
|
||||
|
||||
_viewNextRoom: function(roomIndexDelta) {
|
||||
const allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
MatrixClientPeg.get().getRooms(),
|
||||
);
|
||||
let roomIndex = -1;
|
||||
for (let i = 0; i < allRooms.length; ++i) {
|
||||
if (allRooms[i].roomId == this.state.currentRoomId) {
|
||||
roomIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
|
||||
if (roomIndex < 0) roomIndex = allRooms.length - 1;
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
},
|
||||
|
||||
_viewIndexedRoom: function(roomIndex) {
|
||||
const allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
MatrixClientPeg.get().getRooms(),
|
||||
);
|
||||
if (allRooms[roomIndex]) {
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
}
|
||||
},
|
||||
|
||||
// switch view to the given room
|
||||
//
|
||||
// @param {Object} room_info Object containing data about the room to be joined
|
||||
|
@ -614,7 +592,7 @@ module.exports = React.createClass({
|
|||
_viewRoom: function(room_info) {
|
||||
this.focusComposer = true;
|
||||
|
||||
var newState = {
|
||||
const newState = {
|
||||
initialEventId: room_info.event_id,
|
||||
highlightedEventId: room_info.event_id,
|
||||
initialEventPixelOffset: undefined,
|
||||
|
@ -634,7 +612,7 @@ module.exports = React.createClass({
|
|||
//
|
||||
// TODO: do this in RoomView rather than here
|
||||
if (!room_info.event_id && this.refs.loggedInView) {
|
||||
var scrollState = this.refs.loggedInView.getScrollStateForRoom(room_info.room_id);
|
||||
const scrollState = this.refs.loggedInView.getScrollStateForRoom(room_info.room_id);
|
||||
if (scrollState) {
|
||||
newState.initialEventId = scrollState.focussedEvent;
|
||||
newState.initialEventPixelOffset = scrollState.pixelOffset;
|
||||
|
@ -676,22 +654,61 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_createChat: function() {
|
||||
var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
|
||||
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
|
||||
Modal.createDialog(ChatInviteDialog, {
|
||||
title: "Start a new chat",
|
||||
title: _t('Start a chat'),
|
||||
description: _t("Who would you like to communicate with?"),
|
||||
placeholder: _t("Email, name or matrix ID"),
|
||||
button: _t("Start Chat")
|
||||
});
|
||||
},
|
||||
|
||||
_invite: function(roomId) {
|
||||
var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
|
||||
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
|
||||
Modal.createDialog(ChatInviteDialog, {
|
||||
title: "Invite new room members",
|
||||
button: "Send Invites",
|
||||
description: "Who would you like to add to this room?",
|
||||
title: _t('Invite new room members'),
|
||||
description: _t('Who would you like to add to this room?'),
|
||||
button: _t('Send Invites'),
|
||||
placeholder: _t("Email, name or matrix ID"),
|
||||
roomId: roomId,
|
||||
});
|
||||
},
|
||||
|
||||
_leaveRoom: function(roomId) {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Leave room",
|
||||
description: <span>Are you sure you want to leave the room <i>{roomToLeave.name}</i>?</span>,
|
||||
onFinished: (shouldLeave) => {
|
||||
if (shouldLeave) {
|
||||
const d = MatrixClientPeg.get().leave(roomId);
|
||||
|
||||
// FIXME: controller shouldn't be loading a view :(
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
|
||||
|
||||
d.then(() => {
|
||||
modal.close();
|
||||
if (this.currentRoomId === roomId) {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
}
|
||||
}, (err) => {
|
||||
modal.close();
|
||||
console.error("Failed to leave room " + roomId + " " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to leave room",
|
||||
description: (err && err.message ? err.message :
|
||||
"Server may be unavailable, overloaded, or you hit a bug."),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the sessionloader has finished
|
||||
*/
|
||||
|
@ -710,6 +727,8 @@ module.exports = React.createClass({
|
|||
|
||||
/**
|
||||
* Called whenever someone changes the theme
|
||||
*
|
||||
* @param {string} theme new theme
|
||||
*/
|
||||
_onSetTheme: function(theme) {
|
||||
if (!theme) {
|
||||
|
@ -718,12 +737,12 @@ module.exports = React.createClass({
|
|||
|
||||
// look for the stylesheet elements.
|
||||
// styleElements is a map from style name to HTMLLinkElement.
|
||||
var styleElements = Object.create(null);
|
||||
var i, a;
|
||||
for (i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
|
||||
var href = a.getAttribute("href");
|
||||
const styleElements = Object.create(null);
|
||||
let a;
|
||||
for (let i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
|
||||
const href = a.getAttribute("href");
|
||||
// shouldn't we be using the 'title' tag rather than the href?
|
||||
var match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
|
||||
const match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
|
||||
if (match) {
|
||||
styleElements[match[1]] = a;
|
||||
}
|
||||
|
@ -746,14 +765,15 @@ module.exports = React.createClass({
|
|||
// abuse the tinter to change all the SVG's #fff to #2d2d2d
|
||||
// XXX: obviously this shouldn't be hardcoded here.
|
||||
Tinter.tintSvgWhite('#2d2d2d');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Tinter.tintSvgWhite('#ffffff');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a new logged in session has started
|
||||
*
|
||||
* @param {string} teamToken
|
||||
*/
|
||||
_onLoggedIn: function(teamToken) {
|
||||
this.setState({
|
||||
|
@ -767,8 +787,12 @@ module.exports = React.createClass({
|
|||
this._teamToken = teamToken;
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
} else if (this._is_registered) {
|
||||
if (this.props.config.welcomeUserId) {
|
||||
createRoom({dmUserId: this.props.config.welcomeUserId});
|
||||
return;
|
||||
}
|
||||
// The user has just logged in after registering
|
||||
dis.dispatch({action: 'view_user_settings'});
|
||||
dis.dispatch({action: 'view_room_directory'});
|
||||
} else {
|
||||
this._showScreenAfterLogin();
|
||||
}
|
||||
|
@ -780,7 +804,7 @@ module.exports = React.createClass({
|
|||
if (this.state.screenAfterLogin && this.state.screenAfterLogin.screen) {
|
||||
this.showScreen(
|
||||
this.state.screenAfterLogin.screen,
|
||||
this.state.screenAfterLogin.params
|
||||
this.state.screenAfterLogin.params,
|
||||
);
|
||||
this.notifyNewScreen(this.state.screenAfterLogin.screen);
|
||||
this.setState({screenAfterLogin: null});
|
||||
|
@ -821,8 +845,8 @@ module.exports = React.createClass({
|
|||
* (useful for setting listeners)
|
||||
*/
|
||||
_onWillStartClient() {
|
||||
var self = this;
|
||||
var cli = MatrixClientPeg.get();
|
||||
const self = this;
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
// Allow the JS SDK to reap timeline events. This reduces the amount of
|
||||
// memory consumed as the JS SDK stores multiple distinct copies of room
|
||||
|
@ -863,17 +887,17 @@ module.exports = React.createClass({
|
|||
cli.on('Call.incoming', function(call) {
|
||||
dis.dispatch({
|
||||
action: 'incoming_call',
|
||||
call: call
|
||||
call: call,
|
||||
});
|
||||
});
|
||||
cli.on('Session.logged_out', function(call) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Signed Out",
|
||||
description: "For security, this session has been signed out. Please sign in again."
|
||||
title: _t('Signed Out'),
|
||||
description: _t('For security, this session has been signed out. Please sign in again.'),
|
||||
});
|
||||
dis.dispatch({
|
||||
action: 'logout'
|
||||
action: 'logout',
|
||||
});
|
||||
});
|
||||
cli.on("accountData", function(ev) {
|
||||
|
@ -888,25 +912,21 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onFocus: function(ev) {
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
showScreen: function(screen, params) {
|
||||
if (screen == 'register') {
|
||||
dis.dispatch({
|
||||
action: 'start_registration',
|
||||
params: params
|
||||
params: params,
|
||||
});
|
||||
} else if (screen == 'login') {
|
||||
dis.dispatch({
|
||||
action: 'start_login',
|
||||
params: params
|
||||
params: params,
|
||||
});
|
||||
} else if (screen == 'forgot_password') {
|
||||
dis.dispatch({
|
||||
action: 'start_password_recovery',
|
||||
params: params
|
||||
params: params,
|
||||
});
|
||||
} else if (screen == 'new') {
|
||||
dis.dispatch({
|
||||
|
@ -929,26 +949,26 @@ module.exports = React.createClass({
|
|||
action: 'start_post_registration',
|
||||
});
|
||||
} else if (screen.indexOf('room/') == 0) {
|
||||
var segments = screen.substring(5).split('/');
|
||||
var roomString = segments[0];
|
||||
var eventId = segments[1]; // undefined if no event id given
|
||||
const segments = screen.substring(5).split('/');
|
||||
const roomString = segments[0];
|
||||
const eventId = segments[1]; // undefined if no event id given
|
||||
|
||||
// FIXME: sort_out caseConsistency
|
||||
var third_party_invite = {
|
||||
const thirdPartyInvite = {
|
||||
inviteSignUrl: params.signurl,
|
||||
invitedEmail: params.email,
|
||||
};
|
||||
var oob_data = {
|
||||
const oobData = {
|
||||
name: params.room_name,
|
||||
avatarUrl: params.room_avatar_url,
|
||||
inviterName: params.inviter_name,
|
||||
};
|
||||
|
||||
var payload = {
|
||||
const payload = {
|
||||
action: 'view_room',
|
||||
event_id: eventId,
|
||||
third_party_invite: third_party_invite,
|
||||
oob_data: oob_data,
|
||||
third_party_invite: thirdPartyInvite,
|
||||
oob_data: oobData,
|
||||
};
|
||||
if (roomString[0] == '#') {
|
||||
payload.room_alias = roomString;
|
||||
|
@ -962,19 +982,18 @@ module.exports = React.createClass({
|
|||
dis.dispatch(payload);
|
||||
}
|
||||
} else if (screen.indexOf('user/') == 0) {
|
||||
var userId = screen.substring(5);
|
||||
const userId = screen.substring(5);
|
||||
this.setState({ viewUserId: userId });
|
||||
this._setPage(PageTypes.UserView);
|
||||
this.notifyNewScreen('user/' + userId);
|
||||
var member = new Matrix.RoomMember(null, userId);
|
||||
const member = new Matrix.RoomMember(null, userId);
|
||||
if (member) {
|
||||
dis.dispatch({
|
||||
action: 'view_user',
|
||||
member: member,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
console.info("Ignoring showScreen for '%s'", screen);
|
||||
}
|
||||
},
|
||||
|
@ -993,7 +1012,7 @@ module.exports = React.createClass({
|
|||
onUserClick: function(event, userId) {
|
||||
event.preventDefault();
|
||||
|
||||
var member = new Matrix.RoomMember(null, userId);
|
||||
const member = new Matrix.RoomMember(null, userId);
|
||||
if (!member) { return; }
|
||||
dis.dispatch({
|
||||
action: 'view_user',
|
||||
|
@ -1003,17 +1022,17 @@ module.exports = React.createClass({
|
|||
|
||||
onLogoutClick: function(event) {
|
||||
dis.dispatch({
|
||||
action: 'logout'
|
||||
action: 'logout',
|
||||
});
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
handleResize: function(e) {
|
||||
var hideLhsThreshold = 1000;
|
||||
var showLhsThreshold = 1000;
|
||||
var hideRhsThreshold = 820;
|
||||
var showRhsThreshold = 820;
|
||||
const hideLhsThreshold = 1000;
|
||||
const showLhsThreshold = 1000;
|
||||
const hideRhsThreshold = 820;
|
||||
const showRhsThreshold = 820;
|
||||
|
||||
if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
|
||||
dis.dispatch({ action: 'hide_left_panel' });
|
||||
|
@ -1031,10 +1050,10 @@ module.exports = React.createClass({
|
|||
this.setState({width: window.innerWidth});
|
||||
},
|
||||
|
||||
onRoomCreated: function(room_id) {
|
||||
onRoomCreated: function(roomId) {
|
||||
dis.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room_id,
|
||||
room_id: roomId,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1068,7 +1087,7 @@ module.exports = React.createClass({
|
|||
onFinishPostRegistration: function() {
|
||||
// Don't confuse this with "PageType" which is the middle window to show
|
||||
this.setState({
|
||||
screen: undefined
|
||||
screen: undefined,
|
||||
});
|
||||
this.showScreen("settings");
|
||||
},
|
||||
|
@ -1083,10 +1102,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
updateStatusIndicator: function(state, prevState) {
|
||||
var notifCount = 0;
|
||||
let notifCount = 0;
|
||||
|
||||
var rooms = MatrixClientPeg.get().getRooms();
|
||||
for (var i = 0; i < rooms.length; ++i) {
|
||||
const rooms = MatrixClientPeg.get().getRooms();
|
||||
for (let i = 0; i < rooms.length; ++i) {
|
||||
if (rooms[i].hasMembershipState(MatrixClientPeg.get().credentials.userId, 'invite')) {
|
||||
notifCount++;
|
||||
} else if (rooms[i].getUnreadNotificationCount()) {
|
||||
|
@ -1113,19 +1132,18 @@ module.exports = React.createClass({
|
|||
action: 'view_room',
|
||||
room_id: this.state.currentRoomId,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dis.dispatch({
|
||||
action: 'view_room_directory',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRoomIdResolved: function(room_id) {
|
||||
onRoomIdResolved: function(roomId) {
|
||||
// It's the RoomView's resposibility to look up room aliases, but we need the
|
||||
// ID to pass into things like the Member List, so the Room View tells us when
|
||||
// its done that resolution so we can display things that take a room ID.
|
||||
this.setState({currentRoomId: room_id});
|
||||
this.setState({currentRoomId: roomId});
|
||||
},
|
||||
|
||||
_makeRegistrationUrl: function(params) {
|
||||
|
@ -1148,14 +1166,20 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// needs to be before normal PageTypes as you are logged in technically
|
||||
else if (this.state.screen == 'post_registration') {
|
||||
if (this.state.screen == 'post_registration') {
|
||||
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
|
||||
return (
|
||||
<PostRegistration
|
||||
onComplete={this.onFinishPostRegistration} />
|
||||
);
|
||||
} else if (this.state.loggedIn && this.state.ready) {
|
||||
}
|
||||
|
||||
// `ready` and `loggedIn` may be set before `page_type` (because the
|
||||
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||
// keep showing the spinner for now.
|
||||
if (this.state.loggedIn && this.state.ready && this.state.page_type) {
|
||||
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
||||
* we should go through and figure out what we actually need to pass down, as well
|
||||
* as using something like redux to avoid having a billion bits of state kicking around.
|
||||
|
@ -1178,7 +1202,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_MatrixChat_splash">
|
||||
<Spinner />
|
||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
||||
Logout
|
||||
{ _t('Logout') }
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
@ -1237,5 +1261,5 @@ module.exports = React.createClass({
|
|||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -84,6 +84,12 @@ module.exports = React.createClass({
|
|||
|
||||
// shape parameter to be passed to EventTiles
|
||||
tileShape: React.PropTypes.string,
|
||||
|
||||
// show twelve hour timestamps
|
||||
isTwelveHour: React.PropTypes.bool,
|
||||
|
||||
// show timestamps always
|
||||
alwaysShowTimestamps: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -230,8 +236,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_getEventTiles: function() {
|
||||
var EventTile = sdk.getComponent('rooms.EventTile');
|
||||
var DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
|
||||
|
||||
this.eventNodes = {};
|
||||
|
@ -279,20 +285,19 @@ module.exports = React.createClass({
|
|||
this.currentGhostEventId = null;
|
||||
}
|
||||
|
||||
var isMembershipChange = (e) =>
|
||||
e.getType() === 'm.room.member'
|
||||
&& (!e.getPrevContent() || e.getContent().membership !== e.getPrevContent().membership);
|
||||
var isMembershipChange = (e) => e.getType() === 'm.room.member';
|
||||
|
||||
for (i = 0; i < this.props.events.length; i++) {
|
||||
var mxEv = this.props.events[i];
|
||||
var wantTile = true;
|
||||
var eventId = mxEv.getId();
|
||||
let mxEv = this.props.events[i];
|
||||
let wantTile = true;
|
||||
let eventId = mxEv.getId();
|
||||
let readMarkerInMels = false;
|
||||
|
||||
if (!EventTile.haveTileForEvent(mxEv)) {
|
||||
wantTile = false;
|
||||
}
|
||||
|
||||
var last = (i == lastShownEventIndex);
|
||||
let last = (i == lastShownEventIndex);
|
||||
|
||||
// Wrap consecutive member events in a ListSummary, ignore if redacted
|
||||
if (isMembershipChange(mxEv) &&
|
||||
|
@ -334,6 +339,9 @@ module.exports = React.createClass({
|
|||
|
||||
let eventTiles = summarisedEvents.map(
|
||||
(e) => {
|
||||
if (e.getId() === this.props.readMarkerEventId) {
|
||||
readMarkerInMels = true;
|
||||
}
|
||||
// In order to prevent DateSeparators from appearing in the expanded form
|
||||
// of MemberEventListSummary, render each member event as if the previous
|
||||
// one was itself. This way, the timestamp of the previous event === the
|
||||
|
@ -352,12 +360,16 @@ module.exports = React.createClass({
|
|||
<MemberEventListSummary
|
||||
key={key}
|
||||
events={summarisedEvents}
|
||||
data-scroll-token={eventId}
|
||||
onToggle={this._onWidgetLoad} // Update scroll state
|
||||
>
|
||||
{eventTiles}
|
||||
</MemberEventListSummary>
|
||||
);
|
||||
|
||||
if (readMarkerInMels) {
|
||||
ret.push(this._getReadMarkerTile(visible));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -407,8 +419,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_getTilesForEvent: function(prevEvent, mxEv, last) {
|
||||
var EventTile = sdk.getComponent('rooms.EventTile');
|
||||
var DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
var ret = [];
|
||||
|
||||
// is this a continuation of the previous message?
|
||||
|
@ -462,11 +474,10 @@ module.exports = React.createClass({
|
|||
if (this.props.manageReadReceipts) {
|
||||
readReceipts = this._getReadReceiptsForEvent(mxEv);
|
||||
}
|
||||
|
||||
ret.push(
|
||||
<li key={eventId}
|
||||
ref={this._collectEventNode.bind(this, eventId)}
|
||||
data-scroll-token={scrollToken}>
|
||||
data-scroll-tokens={scrollToken}>
|
||||
<EventTile mxEvent={mxEv} continuation={continuation}
|
||||
isRedacted={mxEv.isRedacted()}
|
||||
onWidgetLoad={this._onWidgetLoad}
|
||||
|
@ -476,6 +487,7 @@ module.exports = React.createClass({
|
|||
checkUnmounting={this._isUnmounting}
|
||||
eventSendStatus={mxEv.status}
|
||||
tileShape={this.props.tileShape}
|
||||
isTwelveHour={this.props.isTwelveHour}
|
||||
last={last} isSelectedEvent={highlight}/>
|
||||
</li>
|
||||
);
|
||||
|
@ -609,8 +621,13 @@ module.exports = React.createClass({
|
|||
var style = this.props.hidden ? { display: 'none' } : {};
|
||||
style.opacity = this.props.opacity;
|
||||
|
||||
var className = this.props.className + " mx_fadable";
|
||||
if (this.props.alwaysShowTimestamps) {
|
||||
className += " mx_MessagePanel_alwaysShowTimestamps";
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollPanel ref="scrollPanel" className={ this.props.className + " mx_fadable" }
|
||||
<ScrollPanel ref="scrollPanel" className={ className }
|
||||
onScroll={ this.props.onScroll }
|
||||
onResize={ this.onResize }
|
||||
onFillRequest={ this.props.onFillRequest }
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
var React = require('react');
|
||||
var ReactDOM = require("react-dom");
|
||||
|
||||
import { _t } from '../../languageHandler';
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var sdk = require('../../index');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
@ -37,7 +37,6 @@ var NotificationPanel = React.createClass({
|
|||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
var timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
|
||||
|
||||
if (timelineSet) {
|
||||
return (
|
||||
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
|
||||
|
@ -48,7 +47,7 @@ var NotificationPanel = React.createClass({
|
|||
showUrlPreview = { false }
|
||||
opacity={ this.props.opacity }
|
||||
tileShape="notif"
|
||||
empty="You have no visible notifications"
|
||||
empty={ _t('You have no visible notifications') }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('../../index');
|
||||
var dis = require("../../dispatcher");
|
||||
var WhoIsTyping = require("../../WhoIsTyping");
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
const MemberAvatar = require("../views/avatars/MemberAvatar");
|
||||
import React from 'react';
|
||||
import { _t } from '../../languageHandler';
|
||||
import sdk from '../../index';
|
||||
import dis from '../../dispatcher';
|
||||
import WhoIsTyping from '../../WhoIsTyping';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import MemberAvatar from '../views/avatars/MemberAvatar';
|
||||
|
||||
const HIDE_DEBOUNCE_MS = 10000;
|
||||
const STATUS_BAR_HIDDEN = 0;
|
||||
|
@ -175,8 +176,8 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomStatusBar_scrollDownIndicator"
|
||||
onClick={ this.props.onScrollToBottomClick }>
|
||||
<img src="img/scrolldown.svg" width="24" height="24"
|
||||
alt="Scroll to bottom of page"
|
||||
title="Scroll to bottom of page"/>
|
||||
alt={ _t("Scroll to bottom of page") }
|
||||
title={ _t("Scroll to bottom of page") }/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -250,10 +251,10 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||
Connectivity to the server has been lost.
|
||||
{_t('Connectivity to the server has been lost.')}
|
||||
</div>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
||||
Sent messages will be stored until your connection has returned.
|
||||
{_t('Sent messages will be stored until your connection has returned.')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -266,7 +267,7 @@ module.exports = React.createClass({
|
|||
<TabCompleteBar tabComplete={this.props.tabComplete} />
|
||||
<div className="mx_RoomStatusBar_tabCompleteEol" title="->|">
|
||||
<TintableSvg src="img/eol.svg" width="22" height="16"/>
|
||||
Auto-complete
|
||||
{_t('Auto-complete')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -283,13 +284,12 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
||||
<a className="mx_RoomStatusBar_resend_link"
|
||||
onClick={ this.props.onResendAllClick }>
|
||||
Resend all
|
||||
</a> or <a
|
||||
{_t('Resend all')}
|
||||
</a> {_t('or')} <a
|
||||
className="mx_RoomStatusBar_resend_link"
|
||||
onClick={ this.props.onCancelAllClick }>
|
||||
cancel all
|
||||
</a> now. You can also select individual messages to
|
||||
resend or cancel.
|
||||
{_t('cancel all')}
|
||||
</a> {_t('now. You can also select individual messages to resend or cancel.')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -324,7 +324,7 @@ module.exports = React.createClass({
|
|||
if (this.props.hasActiveCall) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_callBar">
|
||||
<b>Active call</b>
|
||||
<b>{_t('Active call')}</b>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ var ReactDOM = require("react-dom");
|
|||
var q = require("q");
|
||||
var classNames = require("classnames");
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
var UserSettingsStore = require('../../UserSettingsStore');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
@ -124,6 +125,8 @@ module.exports = React.createClass({
|
|||
room: null,
|
||||
roomId: null,
|
||||
roomLoading: true,
|
||||
|
||||
forwardingEvent: null,
|
||||
editingRoomSettings: false,
|
||||
uploadingRoomSettings: false,
|
||||
numUnreadMessages: 0,
|
||||
|
@ -271,6 +274,7 @@ module.exports = React.createClass({
|
|||
|
||||
this._updateConfCallNotification();
|
||||
|
||||
window.addEventListener('beforeunload', this.onPageUnload);
|
||||
window.addEventListener('resize', this.onResize);
|
||||
this.onResize();
|
||||
|
||||
|
@ -295,7 +299,7 @@ module.exports = React.createClass({
|
|||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
if (newProps.roomAddress != this.props.roomAddress) {
|
||||
throw new Error("changing room on a RoomView is not supported");
|
||||
throw new Error(_t("changing room on a RoomView is not supported"));
|
||||
}
|
||||
|
||||
if (newProps.eventId != this.props.eventId) {
|
||||
|
@ -353,6 +357,7 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
|
||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
|
@ -365,6 +370,17 @@ module.exports = React.createClass({
|
|||
// Tinter.tint(); // reset colourscheme
|
||||
},
|
||||
|
||||
onPageUnload(event) {
|
||||
if (ContentMessages.getCurrentUploads().length > 0) {
|
||||
return event.returnValue =
|
||||
_t("You seem to be uploading files, are you sure you want to quit?");
|
||||
} else if (this._getCallForRoom() && this.state.callState !== 'ended') {
|
||||
return event.returnValue =
|
||||
_t("You seem to be in a call, are you sure you want to quit?");
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
let handled = false;
|
||||
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||
|
@ -438,6 +454,11 @@ module.exports = React.createClass({
|
|||
callState: callState
|
||||
});
|
||||
|
||||
break;
|
||||
case 'forward_event':
|
||||
this.setState({
|
||||
forwardingEvent: payload.content,
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -517,14 +538,14 @@ module.exports = React.createClass({
|
|||
if (!userHasUsedEncryption) {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning!",
|
||||
title: _t("Warning!"),
|
||||
hasCancelButton: false,
|
||||
description: (
|
||||
<div>
|
||||
<p>End-to-end encryption is in beta and may not be reliable.</p>
|
||||
<p>You should <b>not</b> yet trust it to secure data.</p>
|
||||
<p>Devices will <b>not</b> yet be able to decrypt history from before they joined the room.</p>
|
||||
<p>Encrypted messages will not be visible on clients that do not yet implement encryption.</p>
|
||||
<p>{ _t("End-to-end encryption is in beta and may not be reliable") }.</p>
|
||||
<p>{ _t("You should not yet trust it to secure data") }.</p>
|
||||
<p>{ _t("Devices will not yet be able to decrypt history from before they joined the room") }.</p>
|
||||
<p>{ _t("Encrypted messages will not be visible on clients that do not yet implement encryption") }.</p>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
@ -695,10 +716,10 @@ module.exports = React.createClass({
|
|||
if (!unsentMessages.length) return "";
|
||||
for (const event of unsentMessages) {
|
||||
if (!event.error || event.error.name !== "UnknownDeviceError") {
|
||||
return "Some of your messages have not been sent.";
|
||||
return _t("Some of your messages have not been sent") + ".";
|
||||
}
|
||||
}
|
||||
return "Message not sent due to unknown devices being present";
|
||||
return _t("Message not sent due to unknown devices being present");
|
||||
},
|
||||
|
||||
_getUnsentMessages: function(room) {
|
||||
|
@ -858,15 +879,15 @@ module.exports = React.createClass({
|
|||
) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Failed to join the room",
|
||||
description: "This room is private or inaccessible to guests. You may be able to join if you register."
|
||||
title: _t("Failed to join the room"),
|
||||
description: _t("This room is private or inaccessible to guests. You may be able to join if you register") + "."
|
||||
});
|
||||
} else {
|
||||
var msg = error.message ? error.message : JSON.stringify(error);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to join room",
|
||||
description: msg
|
||||
title: _t("Failed to join room"),
|
||||
description: msg,
|
||||
});
|
||||
}
|
||||
}).done();
|
||||
|
@ -926,8 +947,8 @@ module.exports = React.createClass({
|
|||
if (MatrixClientPeg.get().isGuest()) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guest users can't upload files. Please register to upload."
|
||||
title: _t("Please Register"),
|
||||
description: _t("Guest users can't upload files. Please register to upload") + "."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -946,8 +967,8 @@ module.exports = React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to upload file " + file + " " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to upload file",
|
||||
description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or the file too big"),
|
||||
title: _t('Failed to upload file'),
|
||||
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or the file too big")),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1033,8 +1054,8 @@ module.exports = React.createClass({
|
|||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Search failed: " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Search failed",
|
||||
description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or search timed out :("),
|
||||
title: _t("Search failed"),
|
||||
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or search timed out :(")),
|
||||
});
|
||||
}).finally(function() {
|
||||
self.setState({
|
||||
|
@ -1069,12 +1090,12 @@ module.exports = React.createClass({
|
|||
if (!this.state.searchResults.next_batch) {
|
||||
if (this.state.searchResults.results.length == 0) {
|
||||
ret.push(<li key="search-top-marker">
|
||||
<h2 className="mx_RoomView_topMarker">No results</h2>
|
||||
<h2 className="mx_RoomView_topMarker">{ _t("No results") }</h2>
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
ret.push(<li key="search-top-marker">
|
||||
<h2 className="mx_RoomView_topMarker">No more results</h2>
|
||||
<h2 className="mx_RoomView_topMarker">{ _t("No more results") }</h2>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
@ -1111,10 +1132,10 @@ module.exports = React.createClass({
|
|||
// it. We should tell the js sdk to go and find out about
|
||||
// it. But that's not an issue currently, as synapse only
|
||||
// returns results for rooms we're joined to.
|
||||
var roomName = room ? room.name : "Unknown room "+roomId;
|
||||
var roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
|
||||
|
||||
ret.push(<li key={mxEv.getId() + "-room"}>
|
||||
<h1>Room: { roomName }</h1>
|
||||
<h1>{ _t("Room") }: { roomName }</h1>
|
||||
</li>);
|
||||
lastRoomId = roomId;
|
||||
}
|
||||
|
@ -1160,7 +1181,7 @@ module.exports = React.createClass({
|
|||
});
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to save settings",
|
||||
title: _t("Failed to save settings"),
|
||||
description: fails.map(function(result) { return result.reason; }).join("\n"),
|
||||
});
|
||||
// still editing room settings
|
||||
|
@ -1181,7 +1202,10 @@ module.exports = React.createClass({
|
|||
onCancelClick: function() {
|
||||
console.log("updateTint from onCancelClick");
|
||||
this.updateTint();
|
||||
this.setState({editingRoomSettings: false});
|
||||
this.setState({
|
||||
editingRoomSettings: false,
|
||||
forwardingEvent: null,
|
||||
});
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
|
@ -1196,11 +1220,11 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
}, function(err) {
|
||||
var errCode = err.errcode || "unknown error code";
|
||||
var errCode = err.errcode || _t("unknown error code");
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: `Failed to forget room (${errCode})`
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1221,8 +1245,8 @@ module.exports = React.createClass({
|
|||
var msg = error.message ? error.message : JSON.stringify(error);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to reject invite",
|
||||
description: msg
|
||||
title: _t("Failed to reject invite"),
|
||||
description: msg,
|
||||
});
|
||||
|
||||
self.setState({
|
||||
|
@ -1276,12 +1300,7 @@ module.exports = React.createClass({
|
|||
return;
|
||||
}
|
||||
|
||||
var pos = this.refs.messagePanel.getReadMarkerPosition();
|
||||
|
||||
// we want to show the bar if the read-marker is off the top of the
|
||||
// screen.
|
||||
var showBar = (pos < 0);
|
||||
|
||||
const showBar = this.refs.messagePanel.canJumpToReadMarker();
|
||||
if (this.state.showTopUnreadMessagesBar != showBar) {
|
||||
this.setState({showTopUnreadMessagesBar: showBar},
|
||||
this.onChildResize);
|
||||
|
@ -1464,16 +1483,17 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||
var MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||
var RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
||||
var AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||
var SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
||||
const RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
|
||||
if (!this.state.room) {
|
||||
if (this.state.roomLoading) {
|
||||
|
@ -1601,17 +1621,16 @@ module.exports = React.createClass({
|
|||
/>;
|
||||
}
|
||||
|
||||
var aux = null;
|
||||
if (this.state.editingRoomSettings) {
|
||||
let aux = null;
|
||||
if (this.state.forwardingEvent !== null) {
|
||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} currentRoomId={this.state.room.roomId} mxEvent={this.state.forwardingEvent} />;
|
||||
} else if (this.state.editingRoomSettings) {
|
||||
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />;
|
||||
}
|
||||
else if (this.state.uploadingRoomSettings) {
|
||||
} else if (this.state.uploadingRoomSettings) {
|
||||
aux = <Loader/>;
|
||||
}
|
||||
else if (this.state.searching) {
|
||||
} else if (this.state.searching) {
|
||||
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
|
||||
}
|
||||
else if (!myMember || myMember.membership !== "join") {
|
||||
} else if (!myMember || myMember.membership !== "join") {
|
||||
// We do have a room object for this room, but we're not currently in it.
|
||||
// We may have a 3rd party invite to it.
|
||||
var inviterName = undefined;
|
||||
|
@ -1673,7 +1692,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (call.type === "video") {
|
||||
zoomButton = (
|
||||
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title="Fill screen">
|
||||
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title={ _t("Fill screen") }>
|
||||
<TintableSvg src="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }}/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1681,14 +1700,14 @@ module.exports = React.createClass({
|
|||
videoMuteButton =
|
||||
<div className="mx_RoomView_voipButton" onClick={this.onMuteVideoClick}>
|
||||
<TintableSvg src={call.isLocalVideoMuted() ? "img/video-unmute.svg" : "img/video-mute.svg"}
|
||||
alt={call.isLocalVideoMuted() ? "Click to unmute video" : "Click to mute video"}
|
||||
alt={call.isLocalVideoMuted() ? _t("Click to unmute video") : _t("Click to mute video")}
|
||||
width="31" height="27"/>
|
||||
</div>;
|
||||
}
|
||||
voiceMuteButton =
|
||||
<div className="mx_RoomView_voipButton" onClick={this.onMuteAudioClick}>
|
||||
<TintableSvg src={call.isMicrophoneMuted() ? "img/voice-unmute.svg" : "img/voice-mute.svg"}
|
||||
alt={call.isMicrophoneMuted() ? "Click to unmute audio" : "Click to mute audio"}
|
||||
alt={call.isMicrophoneMuted() ? _t("Click to unmute audio") : _t("Click to mute audio")}
|
||||
width="21" height="26"/>
|
||||
</div>;
|
||||
|
||||
|
@ -1724,14 +1743,13 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
// console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
|
||||
|
||||
var messagePanel = (
|
||||
<TimelinePanel ref={this._gatherTimelinePanelRef}
|
||||
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
||||
manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
|
||||
manageReadMarkers={true}
|
||||
hidden={hideMessagePanel}
|
||||
highlightedEventId={this.props.highlightedEventId}
|
||||
highlightedEventId={this.state.forwardingEvent ? this.state.forwardingEvent.getId() : this.props.highlightedEventId}
|
||||
eventId={this.props.eventId}
|
||||
eventPixelOffset={this.props.eventPixelOffset}
|
||||
onScroll={ this.onMessageListScroll }
|
||||
|
@ -1764,11 +1782,12 @@ module.exports = React.createClass({
|
|||
oobData={this.props.oobData}
|
||||
editing={this.state.editingRoomSettings}
|
||||
saving={this.state.uploadingRoomSettings}
|
||||
inRoom={myMember && myMember.membership === 'join'}
|
||||
collapsedRhs={ this.props.collapsedRhs }
|
||||
onSearchClick={this.onSearchClick}
|
||||
onSettingsClick={this.onSettingsClick}
|
||||
onSaveClick={this.onSettingsSaveClick}
|
||||
onCancelClick={this.onCancelClick}
|
||||
onCancelClick={aux ? this.onCancelClick : null}
|
||||
onForgetClick={
|
||||
(myMember && myMember.membership === "leave") ? this.onForgetClick : null
|
||||
}
|
||||
|
|
|
@ -46,9 +46,13 @@ if (DEBUG_SCROLL) {
|
|||
* It also provides a hook which allows parents to provide more list elements
|
||||
* when we get close to the start or end of the list.
|
||||
*
|
||||
* Each child element should have a 'data-scroll-token'. This token is used to
|
||||
* serialise the scroll state, and returned as the 'trackedScrollToken'
|
||||
* attribute by getScrollState().
|
||||
* Each child element should have a 'data-scroll-tokens'. This string of
|
||||
* comma-separated tokens may contain a single token or many, where many indicates
|
||||
* that the element contains elements that have scroll tokens themselves. The first
|
||||
* token in 'data-scroll-tokens' is used to serialise the scroll state, and returned
|
||||
* as the 'trackedScrollToken' attribute by getScrollState().
|
||||
*
|
||||
* IMPORTANT: INDIVIDUAL TOKENS WITHIN 'data-scroll-tokens' MUST NOT CONTAIN COMMAS.
|
||||
*
|
||||
* Some notes about the implementation:
|
||||
*
|
||||
|
@ -349,8 +353,8 @@ module.exports = React.createClass({
|
|||
// Subtract height of tile as if it were unpaginated
|
||||
excessHeight -= tile.clientHeight;
|
||||
// The tile may not have a scroll token, so guard it
|
||||
if (tile.dataset.scrollToken) {
|
||||
markerScrollToken = tile.dataset.scrollToken;
|
||||
if (tile.dataset.scrollTokens) {
|
||||
markerScrollToken = tile.dataset.scrollTokens.split(',')[0];
|
||||
}
|
||||
if (tile.clientHeight > excessHeight) {
|
||||
break;
|
||||
|
@ -419,7 +423,8 @@ module.exports = React.createClass({
|
|||
* scroll. false if we are tracking a particular child.
|
||||
*
|
||||
* string trackedScrollToken: undefined if stuckAtBottom is true; if it is
|
||||
* false, the data-scroll-token of the child which we are tracking.
|
||||
* false, the first token in data-scroll-tokens of the child which we are
|
||||
* tracking.
|
||||
*
|
||||
* number pixelOffset: undefined if stuckAtBottom is true; if it is false,
|
||||
* the number of pixels the bottom of the tracked child is above the
|
||||
|
@ -551,8 +556,10 @@ module.exports = React.createClass({
|
|||
var messages = this.refs.itemlist.children;
|
||||
for (var i = messages.length-1; i >= 0; --i) {
|
||||
var m = messages[i];
|
||||
if (!m.dataset.scrollToken) continue;
|
||||
if (m.dataset.scrollToken == scrollToken) {
|
||||
// 'data-scroll-tokens' is a DOMString of comma-separated scroll tokens
|
||||
// There might only be one scroll token
|
||||
if (m.dataset.scrollTokens &&
|
||||
m.dataset.scrollTokens.split(',').indexOf(scrollToken) !== -1) {
|
||||
node = m;
|
||||
break;
|
||||
}
|
||||
|
@ -568,7 +575,7 @@ module.exports = React.createClass({
|
|||
var boundingRect = node.getBoundingClientRect();
|
||||
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
|
||||
|
||||
debuglog("ScrollPanel: scrolling to token '" + node.dataset.scrollToken + "'+" +
|
||||
debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
|
||||
pixelOffset + " (delta: "+scrollDelta+")");
|
||||
|
||||
if(scrollDelta != 0) {
|
||||
|
@ -591,12 +598,12 @@ module.exports = React.createClass({
|
|||
|
||||
for (var i = messages.length-1; i >= 0; --i) {
|
||||
var node = messages[i];
|
||||
if (!node.dataset.scrollToken) continue;
|
||||
if (!node.dataset.scrollTokens) continue;
|
||||
|
||||
var boundingRect = node.getBoundingClientRect();
|
||||
newScrollState = {
|
||||
stuckAtBottom: false,
|
||||
trackedScrollToken: node.dataset.scrollToken,
|
||||
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
|
||||
pixelOffset: wrapperRect.bottom - boundingRect.bottom,
|
||||
};
|
||||
// If the bottom of the panel intersects the ClientRect of node, use this node
|
||||
|
@ -608,7 +615,7 @@ module.exports = React.createClass({
|
|||
break;
|
||||
}
|
||||
}
|
||||
// This is only false if there were no nodes with `node.dataset.scrollToken` set.
|
||||
// This is only false if there were no nodes with `node.dataset.scrollTokens` set.
|
||||
if (newScrollState) {
|
||||
this.scrollState = newScrollState;
|
||||
debuglog("ScrollPanel: saved scroll state", this.scrollState);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -22,12 +23,14 @@ var Matrix = require("matrix-js-sdk");
|
|||
var EventTimeline = Matrix.EventTimeline;
|
||||
|
||||
var sdk = require('../../index');
|
||||
import { _t } from '../../languageHandler';
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var dis = require("../../dispatcher");
|
||||
var ObjectUtils = require('../../ObjectUtils');
|
||||
var Modal = require("../../Modal");
|
||||
var UserActivity = require("../../UserActivity");
|
||||
var KeyCode = require('../../KeyCode');
|
||||
import UserSettingsStore from '../../UserSettingsStore';
|
||||
|
||||
var PAGINATE_SIZE = 20;
|
||||
var INITIAL_SIZE = 20;
|
||||
|
@ -121,7 +124,7 @@ var TimelinePanel = React.createClass({
|
|||
let initialReadMarker = null;
|
||||
if (this.props.manageReadMarkers) {
|
||||
const readmarker = this.props.timelineSet.room.getAccountData('m.fully_read');
|
||||
if (readmarker){
|
||||
if (readmarker) {
|
||||
initialReadMarker = readmarker.getContent().event_id;
|
||||
} else {
|
||||
initialReadMarker = this._getCurrentReadReceipt();
|
||||
|
@ -167,14 +170,23 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
backPaginating: false,
|
||||
forwardPaginating: false,
|
||||
|
||||
// cache of matrixClient.getSyncState() (but from the 'sync' event)
|
||||
clientSyncState: MatrixClientPeg.get().getSyncState(),
|
||||
|
||||
// should the event tiles have twelve hour times
|
||||
isTwelveHour: UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps'),
|
||||
|
||||
// always show timestamps on event tiles?
|
||||
alwaysShowTimestamps: UserSettingsStore.getSyncedSetting('alwaysShowTimestamps'),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
debuglog("TimelinePanel: mounting");
|
||||
|
||||
this.last_rr_sent_event_id = undefined;
|
||||
this.last_rm_sent_event_id = undefined;
|
||||
this.lastRRSentEventId = undefined;
|
||||
this.lastRMSentEventId = undefined;
|
||||
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||
|
@ -183,6 +195,7 @@ var TimelinePanel = React.createClass({
|
|||
MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt);
|
||||
MatrixClientPeg.get().on("Room.localEchoUpdated", this.onLocalEchoUpdated);
|
||||
MatrixClientPeg.get().on("Room.accountData", this.onAccountData);
|
||||
MatrixClientPeg.get().on("sync", this.onSync);
|
||||
|
||||
this._initTimeline(this.props);
|
||||
},
|
||||
|
@ -251,6 +264,7 @@ var TimelinePanel = React.createClass({
|
|||
client.removeListener("Room.receipt", this.onRoomReceipt);
|
||||
client.removeListener("Room.localEchoUpdated", this.onLocalEchoUpdated);
|
||||
client.removeListener("Room.accountData", this.onAccountData);
|
||||
client.removeListener("sync", this.onSync);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -487,17 +501,24 @@ var TimelinePanel = React.createClass({
|
|||
}, this.props.onReadMarkerUpdated);
|
||||
},
|
||||
|
||||
onSync: function(state, prevState, data) {
|
||||
this.setState({clientSyncState: state});
|
||||
},
|
||||
|
||||
sendReadReceipt: function() {
|
||||
if (!this.refs.messagePanel) return;
|
||||
if (!this.props.manageReadReceipts) return;
|
||||
// This happens on user_activity_end which is delayed, and it's
|
||||
// very possible have logged out within that timeframe, so check
|
||||
// we still have a client.
|
||||
if (!MatrixClientPeg.get()) return;
|
||||
const cli = MatrixClientPeg.get();
|
||||
// if no client or client is guest don't send RR or RM
|
||||
if (!cli || cli.isGuest()) return;
|
||||
|
||||
var currentReadUpToEventId = this._getCurrentReadReceipt(true);
|
||||
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
|
||||
let shouldSendRR = true;
|
||||
|
||||
const currentRREventId = this._getCurrentReadReceipt(true);
|
||||
const currentRREventIndex = this._indexForEventId(currentRREventId);
|
||||
// We want to avoid sending out read receipts when we are looking at
|
||||
// events in the past which are before the latest RR.
|
||||
//
|
||||
|
@ -511,43 +532,60 @@ var TimelinePanel = React.createClass({
|
|||
// RRs) - but that is a bit of a niche case. It will sort itself out when
|
||||
// the user eventually hits the live timeline.
|
||||
//
|
||||
if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
|
||||
if (currentRREventId && currentRREventIndex === null &&
|
||||
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
|
||||
return;
|
||||
shouldSendRR = false;
|
||||
}
|
||||
|
||||
var lastReadEventIndex = this._getLastDisplayedEventIndex({
|
||||
ignoreOwn: true
|
||||
const lastReadEventIndex = this._getLastDisplayedEventIndex({
|
||||
ignoreOwn: true,
|
||||
});
|
||||
if (lastReadEventIndex === null) return;
|
||||
if (lastReadEventIndex === null) {
|
||||
shouldSendRR = false;
|
||||
}
|
||||
let lastReadEvent = this.state.events[lastReadEventIndex];
|
||||
shouldSendRR = shouldSendRR &&
|
||||
// Only send a RR if the last read event is ahead in the timeline relative to
|
||||
// the current RR event.
|
||||
lastReadEventIndex > currentRREventIndex &&
|
||||
// Only send a RR if the last RR set != the one we would send
|
||||
this.lastRRSentEventId != lastReadEvent.getId();
|
||||
|
||||
var lastReadEvent = this.state.events[lastReadEventIndex];
|
||||
// Only send a RM if the last RM sent != the one we would send
|
||||
const shouldSendRM =
|
||||
this.lastRMSentEventId != this.state.readMarkerEventId;
|
||||
|
||||
// we also remember the last read receipt we sent to avoid spamming the
|
||||
// same one at the server repeatedly
|
||||
if ((lastReadEventIndex > currentReadUpToEventIndex &&
|
||||
this.last_rr_sent_event_id != lastReadEvent.getId()) ||
|
||||
this.last_rm_sent_event_id != this.state.readMarkerEventId) {
|
||||
|
||||
this.last_rr_sent_event_id = lastReadEvent.getId();
|
||||
this.last_rm_sent_event_id = this.state.readMarkerEventId;
|
||||
if (shouldSendRR || shouldSendRM) {
|
||||
if (shouldSendRR) {
|
||||
this.lastRRSentEventId = lastReadEvent.getId();
|
||||
} else {
|
||||
lastReadEvent = null;
|
||||
}
|
||||
this.lastRMSentEventId = this.state.readMarkerEventId;
|
||||
|
||||
debuglog('TimelinePanel: Sending Read Markers for ',
|
||||
this.props.timelineSet.room.roomId,
|
||||
'rm', this.state.readMarkerEventId,
|
||||
lastReadEvent ? 'rr ' + lastReadEvent.getId() : '',
|
||||
);
|
||||
MatrixClientPeg.get().setRoomReadMarkers(
|
||||
this.props.timelineSet.room.roomId,
|
||||
this.state.readMarkerEventId,
|
||||
lastReadEvent
|
||||
lastReadEvent, // Could be null, in which case no RR is sent
|
||||
).catch((e) => {
|
||||
// /read_markers API is not implemented on this HS, fallback to just RR
|
||||
if (e.errcode === 'M_UNRECOGNIZED') {
|
||||
if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) {
|
||||
return MatrixClientPeg.get().sendReadReceipt(
|
||||
lastReadEvent
|
||||
lastReadEvent,
|
||||
).catch(() => {
|
||||
this.last_rr_sent_event_id = undefined;
|
||||
this.lastRRSentEventId = undefined;
|
||||
});
|
||||
}
|
||||
// it failed, so allow retries next time the user is active
|
||||
this.last_rr_sent_event_id = undefined;
|
||||
this.last_rm_sent_event_id = undefined;
|
||||
this.lastRRSentEventId = undefined;
|
||||
this.lastRMSentEventId = undefined;
|
||||
});
|
||||
|
||||
// do a quick-reset of our unreadNotificationCount to avoid having
|
||||
|
@ -560,7 +598,6 @@ var TimelinePanel = React.createClass({
|
|||
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
|
||||
dis.dispatch({
|
||||
action: 'on_room_read',
|
||||
room: this.props.timelineSet.room,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -756,6 +793,19 @@ var TimelinePanel = React.createClass({
|
|||
return null;
|
||||
},
|
||||
|
||||
canJumpToReadMarker: function() {
|
||||
// 1. Do not show jump bar if neither the RM nor the RR are set.
|
||||
// 2. Only show jump bar if RR !== RM. If they are the same, there are only fully
|
||||
// read messages and unread messages. We already have a badge count and the bottom
|
||||
// bar to jump to "live" when we have unread messages.
|
||||
// 3. We want to show the bar if the read-marker is off the top of the screen.
|
||||
// 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.
|
||||
(pos < 0 || pos === null); // 3., 4.
|
||||
},
|
||||
|
||||
/**
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
|
@ -865,14 +915,11 @@ var TimelinePanel = React.createClass({
|
|||
});
|
||||
};
|
||||
}
|
||||
var message = "Tried to load a specific point in this room's timeline, but ";
|
||||
if (error.errcode == 'M_FORBIDDEN') {
|
||||
message += "you do not have permission to view the message in question.";
|
||||
} else {
|
||||
message += "was unable to find it.";
|
||||
}
|
||||
var message = (error.errcode == 'M_FORBIDDEN')
|
||||
? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question") + "."
|
||||
: _t("Tried to load a specific point in this room's timeline, but was unable to find it") + ".";
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to load timeline position",
|
||||
title: _t("Failed to load timeline position"),
|
||||
description: message,
|
||||
onFinished: onFinished,
|
||||
});
|
||||
|
@ -1058,11 +1105,17 @@ var TimelinePanel = React.createClass({
|
|||
// events when viewing historical messages, we get stuck in a loop
|
||||
// of paginating our way through the entire history of the room.
|
||||
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
||||
|
||||
// If the state is PREPARED, we're still waiting for the js-sdk to sync with
|
||||
// the HS and fetch the latest events, so we are effectively forward paginating.
|
||||
const forwardPaginating = (
|
||||
this.state.forwardPaginating || this.state.clientSyncState == 'PREPARED'
|
||||
);
|
||||
return (
|
||||
<MessagePanel ref="messagePanel"
|
||||
hidden={ this.props.hidden }
|
||||
backPaginating={ this.state.backPaginating }
|
||||
forwardPaginating={ this.state.forwardPaginating }
|
||||
forwardPaginating={ forwardPaginating }
|
||||
events={ this.state.events }
|
||||
highlightedEventId={ this.props.highlightedEventId }
|
||||
readMarkerEventId={ this.state.readMarkerEventId }
|
||||
|
@ -1076,6 +1129,8 @@ var TimelinePanel = React.createClass({
|
|||
onFillRequest={ this.onMessageListFillRequest }
|
||||
onUnfillRequest={ this.onMessageListUnfillRequest }
|
||||
opacity={ this.props.opacity }
|
||||
isTwelveHour={ this.state.isTwelveHour }
|
||||
alwaysShowTimestamps={ this.state.alwaysShowTimestamps }
|
||||
className={ this.props.className }
|
||||
tileShape={ this.props.tileShape }
|
||||
/>
|
||||
|
|
|
@ -14,35 +14,49 @@ 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.
|
||||
*/
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var sdk = require('../../index');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var PlatformPeg = require("../../PlatformPeg");
|
||||
var Modal = require('../../Modal');
|
||||
var dis = require("../../dispatcher");
|
||||
var q = require('q');
|
||||
var package_json = require('../../../package.json');
|
||||
var UserSettingsStore = require('../../UserSettingsStore');
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var Email = require('../../email');
|
||||
var AddThreepid = require('../../AddThreepid');
|
||||
var SdkConfig = require('../../SdkConfig');
|
||||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
const sdk = require('../../index');
|
||||
const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
const PlatformPeg = require("../../PlatformPeg");
|
||||
const Modal = require('../../Modal');
|
||||
const dis = require("../../dispatcher");
|
||||
const q = require('q');
|
||||
const packageJson = require('../../../package.json');
|
||||
const UserSettingsStore = require('../../UserSettingsStore');
|
||||
const GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
const Email = require('../../email');
|
||||
const AddThreepid = require('../../AddThreepid');
|
||||
const SdkConfig = require('../../SdkConfig');
|
||||
import AccessibleButton from '../views/elements/AccessibleButton';
|
||||
import { _t } from '../../languageHandler';
|
||||
import * as languageHandler from '../../languageHandler';
|
||||
import * as FormattingUtils from '../../utils/FormattingUtils';
|
||||
|
||||
// if this looks like a release, use the 'version' from package.json; else use
|
||||
// the git sha. Prepend version with v, to look like riot-web version
|
||||
const REACT_SDK_VERSION = 'dist' in package_json ? `v${package_json.version}` : package_json.gitHead || '<local>';
|
||||
const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJson.gitHead || '<local>';
|
||||
|
||||
// Simple method to help prettify GH Release Tags and Commit Hashes.
|
||||
const GHVersionUrl = function(repo, token) {
|
||||
const uriTail = (token.startsWith('v') && token.includes('.')) ? `releases/tag/${token}` : `commit/${token}`;
|
||||
return `https://github.com/${repo}/${uriTail}`;
|
||||
}
|
||||
const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i;
|
||||
const gHVersionLabel = function(repo, token='') {
|
||||
const match = token.match(semVerRegex);
|
||||
let url;
|
||||
if (match && match[1]) { // basic semVer string possibly with commit hash
|
||||
url = (match.length > 1 && match[2])
|
||||
? `https://github.com/${repo}/commit/${match[2]}`
|
||||
: `https://github.com/${repo}/releases/tag/v${match[1]}`;
|
||||
} else {
|
||||
url = `https://github.com/${repo}/commit/${token.split('-')[0]}`;
|
||||
}
|
||||
return <a target="_blank" rel="noopener" href={url}>{token}</a>;
|
||||
};
|
||||
|
||||
// Enumerate some simple 'flip a bit' UI settings (if any).
|
||||
// 'id' gives the key name in the im.vector.web.settings account data event
|
||||
// 'label' is how we describe it in the UI.
|
||||
// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
|
||||
// since they will be translated when rendered.
|
||||
const SETTINGS_LABELS = [
|
||||
{
|
||||
id: 'autoplayGifsAndVideos',
|
||||
|
@ -50,13 +64,12 @@ const SETTINGS_LABELS = [
|
|||
},
|
||||
{
|
||||
id: 'hideReadReceipts',
|
||||
label: 'Hide read receipts'
|
||||
label: 'Hide read receipts',
|
||||
},
|
||||
{
|
||||
id: 'dontSendTypingNotifications',
|
||||
label: "Don't send typing notifications",
|
||||
},
|
||||
/*
|
||||
{
|
||||
id: 'alwaysShowTimestamps',
|
||||
label: 'Always show message timestamps',
|
||||
|
@ -65,6 +78,7 @@ const SETTINGS_LABELS = [
|
|||
id: 'showTwelveHourTimestamps',
|
||||
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
|
||||
},
|
||||
/*
|
||||
{
|
||||
id: 'useCompactLayout',
|
||||
label: 'Use compact timeline layout',
|
||||
|
@ -76,6 +90,8 @@ const SETTINGS_LABELS = [
|
|||
*/
|
||||
];
|
||||
|
||||
// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
|
||||
// since they will be translated when rendered.
|
||||
const CRYPTO_SETTINGS_LABELS = [
|
||||
{
|
||||
id: 'blacklistUnverifiedDevices',
|
||||
|
@ -106,10 +122,9 @@ const THEMES = [
|
|||
id: 'theme',
|
||||
label: 'Dark theme',
|
||||
value: 'dark',
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'UserSettings',
|
||||
|
||||
|
@ -142,10 +157,10 @@ module.exports = React.createClass({
|
|||
getInitialState: function() {
|
||||
return {
|
||||
avatarUrl: null,
|
||||
threePids: [],
|
||||
threepids: [],
|
||||
phase: "UserSettings.LOADING", // LOADING, DISPLAY
|
||||
email_add_pending: false,
|
||||
vectorVersion: null,
|
||||
vectorVersion: undefined,
|
||||
rejectingInvites: false,
|
||||
};
|
||||
},
|
||||
|
@ -180,13 +195,17 @@ module.exports = React.createClass({
|
|||
});
|
||||
this._refreshFromServer();
|
||||
|
||||
var syncedSettings = UserSettingsStore.getSyncedSettings();
|
||||
const syncedSettings = UserSettingsStore.getSyncedSettings();
|
||||
if (!syncedSettings.theme) {
|
||||
syncedSettings.theme = 'light';
|
||||
}
|
||||
this._syncedSettings = syncedSettings;
|
||||
|
||||
this._localSettings = UserSettingsStore.getLocalSettings();
|
||||
|
||||
this.setState({
|
||||
language: languageHandler.getCurrentLanguage(),
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
@ -202,16 +221,16 @@ module.exports = React.createClass({
|
|||
middleOpacity: 1.0,
|
||||
});
|
||||
dis.unregister(this.dispatcherRef);
|
||||
let cli = MatrixClientPeg.get();
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.removeListener("RoomMember.membership", this._onInviteStateChange);
|
||||
}
|
||||
},
|
||||
|
||||
_refreshFromServer: function() {
|
||||
var self = this;
|
||||
const self = this;
|
||||
q.all([
|
||||
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids()
|
||||
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(),
|
||||
]).done(function(resps) {
|
||||
self.setState({
|
||||
avatarUrl: resps[0].avatar_url,
|
||||
|
@ -219,11 +238,11 @@ module.exports = React.createClass({
|
|||
phase: "UserSettings.DISPLAY",
|
||||
});
|
||||
}, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to load user settings: " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Can't load user settings",
|
||||
description: ((error && error.message) ? error.message : "Server may be unavailable or overloaded"),
|
||||
title: _t("Can't load user settings"),
|
||||
description: ((error && error.message) ? error.message : _t("Server may be unavailable or overloaded")),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -236,10 +255,10 @@ module.exports = React.createClass({
|
|||
|
||||
onAvatarPickerClick: function(ev) {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guests can't set avatars. Please register.",
|
||||
title: _t("Please Register"),
|
||||
description: _t("Guests can't set avatars. Please register."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -250,8 +269,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onAvatarSelected: function(ev) {
|
||||
var self = this;
|
||||
var changeAvatar = this.refs.changeAvatar;
|
||||
const self = this;
|
||||
const changeAvatar = this.refs.changeAvatar;
|
||||
if (!changeAvatar) {
|
||||
console.error("No ChangeAvatar found to upload image to!");
|
||||
return;
|
||||
|
@ -260,33 +279,33 @@ module.exports = React.createClass({
|
|||
// dunno if the avatar changed, re-check it.
|
||||
self._refreshFromServer();
|
||||
}, function(err) {
|
||||
var errMsg = (typeof err === "string") ? err : (err.error || "");
|
||||
// const errMsg = (typeof err === "string") ? err : (err.error || "");
|
||||
console.error("Failed to set avatar: " + err);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to set avatar",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
title: _t("Failed to set avatar."),
|
||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onLogoutClicked: function(ev) {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Sign out?",
|
||||
title: _t("Sign out"),
|
||||
description:
|
||||
<div>
|
||||
For security, logging out will delete any end-to-end encryption keys from this browser.
|
||||
|
||||
If you want to be able to decrypt your conversation history from future Riot sessions,
|
||||
please export your room keys for safe-keeping.
|
||||
{ _t("For security, logging out will delete any end-to-end " +
|
||||
"encryption keys from this browser. If you want to be able " +
|
||||
"to decrypt your conversation history from future Riot sessions, " +
|
||||
"please export your room keys for safe-keeping.") }.
|
||||
</div>,
|
||||
button: "Sign out",
|
||||
button: _t("Sign out"),
|
||||
extraButtons: [
|
||||
<button key="export" className="mx_Dialog_primary"
|
||||
onClick={this._onExportE2eKeysClicked}>
|
||||
Export E2E room keys
|
||||
</button>
|
||||
{ _t("Export E2E room keys") }
|
||||
</button>,
|
||||
],
|
||||
onFinished: (confirmed) => {
|
||||
if (confirmed) {
|
||||
|
@ -300,34 +319,31 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onPasswordChangeError: function(err) {
|
||||
var errMsg = err.error || "";
|
||||
let errMsg = err.error || "";
|
||||
if (err.httpStatus === 403) {
|
||||
errMsg = "Failed to change password. Is your password correct?";
|
||||
}
|
||||
else if (err.httpStatus) {
|
||||
errMsg = _t("Failed to change password. Is your password correct?");
|
||||
} else if (err.httpStatus) {
|
||||
errMsg += ` (HTTP status ${err.httpStatus})`;
|
||||
}
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to change password: " + errMsg);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: errMsg
|
||||
title: _t("Error"),
|
||||
description: errMsg,
|
||||
});
|
||||
},
|
||||
|
||||
onPasswordChanged: function() {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Success",
|
||||
description: `Your password was successfully changed. You will not
|
||||
receive push notifications on other devices until you
|
||||
log back in to them.`
|
||||
title: _t("Success"),
|
||||
description: _t("Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them") + ".",
|
||||
});
|
||||
},
|
||||
|
||||
onUpgradeClicked: function() {
|
||||
dis.dispatch({
|
||||
action: "start_upgrade_registration"
|
||||
action: "start_upgrade_registration",
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -341,33 +357,33 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_addEmail: function() {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
|
||||
var email_address = this.refs.add_email_input.value;
|
||||
if (!Email.looksValid(email_address)) {
|
||||
const emailAddress = this.refs.add_email_input.value;
|
||||
if (!Email.looksValid(emailAddress)) {
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Invalid Email Address",
|
||||
description: "This doesn't appear to be a valid email address",
|
||||
title: _t("Invalid Email Address"),
|
||||
description: _t("This doesn't appear to be a valid email address"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._addThreepid = new AddThreepid();
|
||||
// we always bind emails when registering, so let's do the
|
||||
// same here.
|
||||
this._addThreepid.addEmailAddress(email_address, true).done(() => {
|
||||
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Verification Pending",
|
||||
description: "Please check your email and click on the link it contains. Once this is done, click continue.",
|
||||
button: 'Continue',
|
||||
title: _t("Verification Pending"),
|
||||
description: _t("Please check your email and click on the link it contains. Once this is done, click continue."),
|
||||
button: _t('Continue'),
|
||||
onFinished: this.onEmailDialogFinished,
|
||||
});
|
||||
}, (err) => {
|
||||
this.setState({email_add_pending: false});
|
||||
console.error("Unable to add email address " + email_address + " " + err);
|
||||
console.error("Unable to add email address " + emailAddress + " " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Unable to add email address",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
title: _t("Unable to add email address"),
|
||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||
});
|
||||
});
|
||||
ReactDOM.findDOMNode(this.refs.add_email_input).blur();
|
||||
|
@ -377,9 +393,9 @@ module.exports = React.createClass({
|
|||
onRemoveThreepidClicked: function(threepid) {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Remove Contact Information?",
|
||||
description: "Remove " + threepid.address + "?",
|
||||
button: 'Remove',
|
||||
title: _t("Remove Contact Information?"),
|
||||
description: _t("Remove %(threePid)s?", { threePid : threepid.address }),
|
||||
button: _t('Remove'),
|
||||
onFinished: (submit) => {
|
||||
if (submit) {
|
||||
this.setState({
|
||||
|
@ -391,8 +407,8 @@ module.exports = React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Unable to remove contact information: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Unable to remove contact information",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
title: _t("Unable to remove contact information"),
|
||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||
});
|
||||
}).done();
|
||||
}
|
||||
|
@ -419,21 +435,21 @@ module.exports = React.createClass({
|
|||
}, (err) => {
|
||||
this.setState({email_add_pending: false});
|
||||
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
var message = "Unable to verify email address. ";
|
||||
message += "Please check your email and click on the link it contains. Once this is done, click continue.";
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
let message = _t("Unable to verify email address.") + " " +
|
||||
_t("Please check your email and click on the link it contains. Once this is done, click continue.");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Verification Pending",
|
||||
title: _t("Verification Pending"),
|
||||
description: message,
|
||||
button: 'Continue',
|
||||
button: _t('Continue'),
|
||||
onFinished: this.onEmailDialogFinished,
|
||||
});
|
||||
} else {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Unable to verify email address: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Unable to verify email address",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
title: _t("Unable to verify email address."),
|
||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -469,17 +485,17 @@ module.exports = React.createClass({
|
|||
|
||||
_onRejectAllInvitesClicked: function(rooms, ev) {
|
||||
this.setState({
|
||||
rejectingInvites: true
|
||||
rejectingInvites: true,
|
||||
});
|
||||
// reject the invites
|
||||
let promises = rooms.map((room) => {
|
||||
const promises = rooms.map((room) => {
|
||||
return MatrixClientPeg.get().leave(room.roomId);
|
||||
});
|
||||
// purposefully drop errors to the floor: we'll just have a non-zero number on the UI
|
||||
// after trying to reject all the invites.
|
||||
q.allSettled(promises).then(() => {
|
||||
this.setState({
|
||||
rejectingInvites: false
|
||||
rejectingInvites: false,
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
@ -492,7 +508,7 @@ module.exports = React.createClass({
|
|||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -504,7 +520,7 @@ module.exports = React.createClass({
|
|||
}, "e2e-export");
|
||||
}, {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -523,22 +539,43 @@ module.exports = React.createClass({
|
|||
<div>
|
||||
<h3>Referral</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
Refer a friend to Riot: <a href={href}>{href}</a>
|
||||
{_t("Refer a friend to Riot:")} <a href={href}>{href}</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
_renderUserInterfaceSettings: function() {
|
||||
var client = MatrixClientPeg.get();
|
||||
onLanguageChange: function(newLang) {
|
||||
if(this.state.language !== newLang) {
|
||||
UserSettingsStore.setLocalSetting('language', newLang);
|
||||
this.setState({
|
||||
language: newLang,
|
||||
});
|
||||
PlatformPeg.get().reload();
|
||||
}
|
||||
},
|
||||
|
||||
_renderLanguageSetting: function () {
|
||||
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
|
||||
return <div>
|
||||
<label htmlFor="languageSelector">{_t('Interface Language')}</label>
|
||||
<LanguageDropdown ref="language" onOptionChange={this.onLanguageChange}
|
||||
className="mx_UserSettings_language"
|
||||
value={this.state.language}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderUserInterfaceSettings: function() {
|
||||
return (
|
||||
<div>
|
||||
<h3>User Interface</h3>
|
||||
<h3>{ _t("User Interface") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
{ this._renderUrlPreviewSelector() }
|
||||
{ SETTINGS_LABELS.map( this._renderSyncedSetting ) }
|
||||
{ THEMES.map( this._renderThemeSelector ) }
|
||||
{ this._renderLanguageSetting() }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -549,10 +586,10 @@ module.exports = React.createClass({
|
|||
<input id="urlPreviewsDisabled"
|
||||
type="checkbox"
|
||||
defaultChecked={ UserSettingsStore.getUrlPreviewsDisabled() }
|
||||
onChange={ e => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
|
||||
onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
|
||||
/>
|
||||
<label htmlFor="urlPreviewsDisabled">
|
||||
Disable inline URL previews by default
|
||||
{ _t("Disable inline URL previews by default") }
|
||||
</label>
|
||||
</div>;
|
||||
},
|
||||
|
@ -562,10 +599,10 @@ module.exports = React.createClass({
|
|||
<input id={ setting.id }
|
||||
type="checkbox"
|
||||
defaultChecked={ this._syncedSettings[setting.id] }
|
||||
onChange={ e => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
|
||||
onChange={ (e) => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
|
||||
/>
|
||||
<label htmlFor={ setting.id }>
|
||||
{ setting.label }
|
||||
{ _t(setting.label) }
|
||||
</label>
|
||||
</div>;
|
||||
},
|
||||
|
@ -577,7 +614,7 @@ module.exports = React.createClass({
|
|||
name={ setting.id }
|
||||
value={ setting.value }
|
||||
defaultChecked={ this._syncedSettings[setting.id] === setting.value }
|
||||
onChange={ e => {
|
||||
onChange={ (e) => {
|
||||
if (e.target.checked) {
|
||||
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
|
||||
}
|
||||
|
@ -597,7 +634,12 @@ module.exports = React.createClass({
|
|||
_renderCryptoInfo: function() {
|
||||
const client = MatrixClientPeg.get();
|
||||
const deviceId = client.deviceId;
|
||||
const identityKey = client.getDeviceEd25519Key() || "<not supported>";
|
||||
let identityKey = client.getDeviceEd25519Key();
|
||||
if (!identityKey) {
|
||||
identityKey = _t("<not supported>");
|
||||
} else {
|
||||
identityKey = FormattingUtils.formatCryptoKey(identityKey);
|
||||
}
|
||||
|
||||
let importExportButtons = null;
|
||||
|
||||
|
@ -606,18 +648,18 @@ module.exports = React.createClass({
|
|||
<div className="mx_UserSettings_importExportButtons">
|
||||
<AccessibleButton className="mx_UserSettings_button"
|
||||
onClick={this._onExportE2eKeysClicked}>
|
||||
Export E2E room keys
|
||||
{ _t("Export E2E room keys") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_UserSettings_button"
|
||||
onClick={this._onImportE2eKeysClicked}>
|
||||
Import E2E room keys
|
||||
{ _t("Import E2E room keys") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h3>Cryptography</h3>
|
||||
<h3>{ _t("Cryptography") }</h3>
|
||||
<div className="mx_UserSettings_section mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>Device ID:</label> <span><code>{deviceId}</code></span></li>
|
||||
|
@ -639,8 +681,8 @@ module.exports = React.createClass({
|
|||
type="checkbox"
|
||||
defaultChecked={ this._localSettings[setting.id] }
|
||||
onChange={
|
||||
e => {
|
||||
UserSettingsStore.setLocalSetting(setting.id, e.target.checked)
|
||||
(e) => {
|
||||
UserSettingsStore.setLocalSetting(setting.id, e.target.checked);
|
||||
if (setting.id === 'blacklistUnverifiedDevices') { // XXX: this is a bit ugly
|
||||
client.setGlobalBlacklistUnverifiedDevices(e.target.checked);
|
||||
}
|
||||
|
@ -648,13 +690,13 @@ module.exports = React.createClass({
|
|||
}
|
||||
/>
|
||||
<label htmlFor={ setting.id }>
|
||||
{ setting.label }
|
||||
{ _t(setting.label) }
|
||||
</label>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderDevicesPanel: function() {
|
||||
var DevicesPanel = sdk.getComponent('settings.DevicesPanel');
|
||||
const DevicesPanel = sdk.getComponent('settings.DevicesPanel');
|
||||
return (
|
||||
<div>
|
||||
<h3>Devices</h3>
|
||||
|
@ -665,15 +707,15 @@ module.exports = React.createClass({
|
|||
|
||||
_renderBugReport: function() {
|
||||
if (!SdkConfig.get().bug_report_endpoint_url) {
|
||||
return <div />
|
||||
return <div />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h3>Bug Report</h3>
|
||||
<h3>{ _t("Bug Report") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
<p>Found a bug?</p>
|
||||
<p>{ _t("Found a bug?") }</p>
|
||||
<button className="mx_UserSettings_button danger"
|
||||
onClick={this._onBugReportClicked}>Report it
|
||||
onClick={this._onBugReportClicked}>{_t('Report it')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -684,20 +726,20 @@ module.exports = React.createClass({
|
|||
// default to enabled if undefined
|
||||
if (this.props.enableLabs === false) return null;
|
||||
|
||||
let features = UserSettingsStore.LABS_FEATURES.map(feature => (
|
||||
const features = UserSettingsStore.LABS_FEATURES.map((feature) => (
|
||||
<div key={feature.id} className="mx_UserSettings_toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={feature.id}
|
||||
name={feature.id}
|
||||
defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
e.target.checked = false;
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guests can't use labs features. Please register.",
|
||||
title: _t("Please Register"),
|
||||
description: _t("Guests can't use labs features. Please register."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -710,9 +752,9 @@ module.exports = React.createClass({
|
|||
));
|
||||
return (
|
||||
<div>
|
||||
<h3>Labs</h3>
|
||||
<h3>{ _t("Labs") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
<p>These are experimental features that may break in unexpected ways. Use with caution.</p>
|
||||
<p>{ _t("These are experimental features that may break in unexpected ways") }. { _t("Use with caution") }.</p>
|
||||
{features}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -724,10 +766,10 @@ module.exports = React.createClass({
|
|||
if (MatrixClientPeg.get().isGuest()) return null;
|
||||
|
||||
return <div>
|
||||
<h3>Deactivate Account</h3>
|
||||
<h3>{ _t("Deactivate Account") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
<AccessibleButton className="mx_UserSettings_button danger"
|
||||
onClick={this._onDeactivateAccountClicked}>Deactivate my account
|
||||
onClick={this._onDeactivateAccountClicked}> { _t("Deactivate my account") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>;
|
||||
|
@ -735,25 +777,25 @@ module.exports = React.createClass({
|
|||
|
||||
_renderClearCache: function() {
|
||||
return <div>
|
||||
<h3>Clear Cache</h3>
|
||||
<h3>{ _t("Clear Cache") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
<AccessibleButton className="mx_UserSettings_button danger"
|
||||
onClick={this._onClearCacheClicked}>
|
||||
Clear Cache and Reload
|
||||
{ _t("Clear Cache and Reload") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderBulkOptions: function() {
|
||||
let invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => {
|
||||
const invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => {
|
||||
return r.hasMembershipState(this._me, "invite");
|
||||
});
|
||||
if (invitedRooms.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let Spinner = sdk.getComponent("elements.Spinner");
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
let reject = <Spinner />;
|
||||
if (!this.state.rejectingInvites) {
|
||||
|
@ -768,7 +810,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return <div>
|
||||
<h3>Bulk Options</h3>
|
||||
<h3>{ _t("Bulk Options") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
{reject}
|
||||
</div>
|
||||
|
@ -777,9 +819,7 @@ module.exports = React.createClass({
|
|||
|
||||
_showSpoiler: function(event) {
|
||||
const target = event.target;
|
||||
const hidden = target.getAttribute('data-spoiler');
|
||||
|
||||
target.innerHTML = hidden;
|
||||
target.innerHTML = target.getAttribute('data-spoiler');
|
||||
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(target);
|
||||
|
@ -790,12 +830,13 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
nameForMedium: function(medium) {
|
||||
if (medium == 'msisdn') return 'Phone';
|
||||
if (medium === 'msisdn') return _t('Phone');
|
||||
if (medium === 'email') return _t('Email');
|
||||
return medium[0].toUpperCase() + medium.slice(1);
|
||||
},
|
||||
|
||||
presentableTextForThreepid: function(threepid) {
|
||||
if (threepid.medium == 'msisdn') {
|
||||
if (threepid.medium === 'msisdn') {
|
||||
return '+' + threepid.address;
|
||||
} else {
|
||||
return threepid.address;
|
||||
|
@ -803,7 +844,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
switch (this.state.phase) {
|
||||
case "UserSettings.LOADING":
|
||||
return (
|
||||
|
@ -815,18 +856,18 @@ module.exports = React.createClass({
|
|||
throw new Error("Unknown state.phase => " + this.state.phase);
|
||||
}
|
||||
// can only get here if phase is UserSettings.DISPLAY
|
||||
var SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
var ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName");
|
||||
var ChangePassword = sdk.getComponent("views.settings.ChangePassword");
|
||||
var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
||||
var Notifications = sdk.getComponent("settings.Notifications");
|
||||
var EditableText = sdk.getComponent('elements.EditableText');
|
||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
const ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName");
|
||||
const ChangePassword = sdk.getComponent("views.settings.ChangePassword");
|
||||
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
||||
const Notifications = sdk.getComponent("settings.Notifications");
|
||||
const EditableText = sdk.getComponent('elements.EditableText');
|
||||
|
||||
var avatarUrl = (
|
||||
const avatarUrl = (
|
||||
this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null
|
||||
);
|
||||
|
||||
var threepidsSection = this.state.threepids.map((val, pidIndex) => {
|
||||
const threepidsSection = this.state.threepids.map((val, pidIndex) => {
|
||||
const id = "3pid-" + val.address;
|
||||
return (
|
||||
<div className="mx_UserSettings_profileTableRow" key={pidIndex}>
|
||||
|
@ -839,7 +880,7 @@ module.exports = React.createClass({
|
|||
/>
|
||||
</div>
|
||||
<div className="mx_UserSettings_threepidButton mx_filterFlipColor">
|
||||
<img src="img/cancel-small.svg" width="14" height="14" alt="Remove" onClick={this.onRemoveThreepidClicked.bind(this, val)} />
|
||||
<img src="img/cancel-small.svg" width="14" height="14" alt={ _t("Remove") } onClick={this.onRemoveThreepidClicked.bind(this, val)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -851,13 +892,14 @@ module.exports = React.createClass({
|
|||
addEmailSection = (
|
||||
<div className="mx_UserSettings_profileTableRow" key="_newEmail">
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label>{_t('Email')}</label>
|
||||
</div>
|
||||
<div className="mx_UserSettings_profileInputCell">
|
||||
<EditableText
|
||||
ref="add_email_input"
|
||||
className="mx_UserSettings_editable"
|
||||
placeholderClassName="mx_UserSettings_threepidPlaceholder"
|
||||
placeholder={ "Add email address" }
|
||||
placeholder={ _t("Add email address") }
|
||||
blurToCancel={ false }
|
||||
onValueChanged={ this._onAddEmailEditFinished } />
|
||||
</div>
|
||||
|
@ -874,16 +916,15 @@ module.exports = React.createClass({
|
|||
threepidsSection.push(addEmailSection);
|
||||
threepidsSection.push(addMsisdnSection);
|
||||
|
||||
var accountJsx;
|
||||
let accountJsx;
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
accountJsx = (
|
||||
<div className="mx_UserSettings_button" onClick={this.onUpgradeClicked}>
|
||||
Create an account
|
||||
{ _t("Create an account") }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
accountJsx = (
|
||||
<ChangePassword
|
||||
className="mx_UserSettings_accountTable"
|
||||
|
@ -895,10 +936,10 @@ module.exports = React.createClass({
|
|||
onFinished={this.onPasswordChanged} />
|
||||
);
|
||||
}
|
||||
var notification_area;
|
||||
let notificationArea;
|
||||
if (!MatrixClientPeg.get().isGuest() && this.state.threepids !== undefined) {
|
||||
notification_area = (<div>
|
||||
<h3>Notifications</h3>
|
||||
notificationArea = (<div>
|
||||
<h3>{ _t("Notifications") }</h3>
|
||||
|
||||
<div className="mx_UserSettings_section">
|
||||
<Notifications threepids={this.state.threepids} brand={this.props.brand} />
|
||||
|
@ -911,13 +952,13 @@ module.exports = React.createClass({
|
|||
// we are using a version old version of olm. We assume the former.
|
||||
let olmVersionString = "<not-enabled>";
|
||||
if (olmVersion !== undefined) {
|
||||
olmVersionString = `v${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
|
||||
olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_UserSettings">
|
||||
<SimpleRoomHeader
|
||||
title="Settings"
|
||||
title={ _t("Settings") }
|
||||
collapsedRhs={ this.props.collapsedRhs }
|
||||
onCancelClick={ this.props.onClose }
|
||||
/>
|
||||
|
@ -925,13 +966,13 @@ module.exports = React.createClass({
|
|||
<GeminiScrollbar className="mx_UserSettings_body"
|
||||
autoshow={true}>
|
||||
|
||||
<h3>Profile</h3>
|
||||
<h3>{ _t("Profile") }</h3>
|
||||
|
||||
<div className="mx_UserSettings_section">
|
||||
<div className="mx_UserSettings_profileTable">
|
||||
<div className="mx_UserSettings_profileTableRow">
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="displayName">Display name</label>
|
||||
<label htmlFor="displayName">{ _t('Display name') }</label>
|
||||
</div>
|
||||
<div className="mx_UserSettings_profileInputCell">
|
||||
<ChangeDisplayName />
|
||||
|
@ -948,7 +989,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_UserSettings_avatarPicker_edit">
|
||||
<label htmlFor="avatarInput" ref="file_label">
|
||||
<img src="img/camera.svg" className="mx_filterFlipColor"
|
||||
alt="Upload avatar" title="Upload avatar"
|
||||
alt={ _t("Upload avatar") } title={ _t("Upload avatar") }
|
||||
width="17" height="15" />
|
||||
</label>
|
||||
<input id="avatarInput" type="file" onChange={this.onAvatarSelected}/>
|
||||
|
@ -956,12 +997,12 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Account</h3>
|
||||
<h3>{ _t("Account") }</h3>
|
||||
|
||||
<div className="mx_UserSettings_section">
|
||||
<div className="mx_UserSettings_section cadcampoHide">
|
||||
|
||||
<AccessibleButton className="mx_UserSettings_logout mx_UserSettings_button" onClick={this.onLogoutClicked}>
|
||||
Sign out
|
||||
{ _t("Sign out") }
|
||||
</AccessibleButton>
|
||||
|
||||
{accountJsx}
|
||||
|
@ -969,7 +1010,7 @@ module.exports = React.createClass({
|
|||
|
||||
{this._renderReferral()}
|
||||
|
||||
{notification_area}
|
||||
{notificationArea}
|
||||
|
||||
{this._renderUserInterfaceSettings()}
|
||||
{this._renderLabs()}
|
||||
|
@ -978,31 +1019,31 @@ module.exports = React.createClass({
|
|||
{this._renderBulkOptions()}
|
||||
{this._renderBugReport()}
|
||||
|
||||
<h3>Advanced</h3>
|
||||
<h3>{ _t("Advanced") }</h3>
|
||||
|
||||
<div className="mx_UserSettings_section">
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Logged in as {this._me}
|
||||
{ _t("Logged in as:") } {this._me}
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Access Token: <span className="mx_UserSettings_advanced_spoiler" onClick={this._showSpoiler} data-spoiler={ MatrixClientPeg.get().getAccessToken() }><click to reveal></span>
|
||||
{_t('Access Token:')} <span className="mx_UserSettings_advanced_spoiler" onClick={this._showSpoiler} data-spoiler={ MatrixClientPeg.get().getAccessToken() }><{ _t("click to reveal") }></span>
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Homeserver is { MatrixClientPeg.get().getHomeserverUrl() }
|
||||
{ _t("Homeserver is") } { MatrixClientPeg.get().getHomeserverUrl() }
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Identity Server is { MatrixClientPeg.get().getIdentityServerUrl() }
|
||||
{ _t("Identity Server is") } { MatrixClientPeg.get().getIdentityServerUrl() }
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
matrix-react-sdk version: {(REACT_SDK_VERSION !== '<local>')
|
||||
? <a href={ GHVersionUrl('matrix-org/matrix-react-sdk', REACT_SDK_VERSION) }>{REACT_SDK_VERSION}</a>
|
||||
{_t('matrix-react-sdk version:')} {(REACT_SDK_VERSION !== '<local>')
|
||||
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
|
||||
: REACT_SDK_VERSION
|
||||
}<br/>
|
||||
riot-web version: {(this.state.vectorVersion !== null)
|
||||
? <a href={ GHVersionUrl('vector-im/riot-web', this.state.vectorVersion.split('-')[0]) }>{this.state.vectorVersion}</a>
|
||||
{_t('riot-web version:')} {(this.state.vectorVersion !== undefined)
|
||||
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
|
||||
: 'unknown'
|
||||
}<br/>
|
||||
olm version: {olmVersionString}<br/>
|
||||
{ _t("olm version:") } {olmVersionString}<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1013,5 +1054,5 @@ module.exports = React.createClass({
|
|||
</GeminiScrollbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
import { _t } from '../../../languageHandler';
|
||||
var sdk = require('../../../index');
|
||||
var Modal = require("../../../Modal");
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
|
@ -54,7 +55,7 @@ module.exports = React.createClass({
|
|||
progress: "sent_email"
|
||||
});
|
||||
}, (err) => {
|
||||
this.showErrorDialog("Failed to send email: " + err.message);
|
||||
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
||||
this.setState({
|
||||
progress: null
|
||||
});
|
||||
|
@ -78,30 +79,33 @@ module.exports = React.createClass({
|
|||
ev.preventDefault();
|
||||
|
||||
if (!this.state.email) {
|
||||
this.showErrorDialog("The email address linked to your account must be entered.");
|
||||
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
||||
}
|
||||
else if (!this.state.password || !this.state.password2) {
|
||||
this.showErrorDialog("A new password must be entered.");
|
||||
this.showErrorDialog(_t('A new password must be entered.'));
|
||||
}
|
||||
else if (this.state.password !== this.state.password2) {
|
||||
this.showErrorDialog("New passwords must match each other.");
|
||||
this.showErrorDialog(_t('New passwords must match each other.'));
|
||||
}
|
||||
else {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning",
|
||||
title: _t('Warning!'),
|
||||
description:
|
||||
<div>
|
||||
Resetting password will currently reset any end-to-end encryption keys on all devices,
|
||||
making encrypted chat history unreadable, unless you first export your room keys
|
||||
and re-import them afterwards.
|
||||
In future this <a href="https://github.com/vector-im/riot-web/issues/2671">will be improved</a>.
|
||||
{ _t(
|
||||
'Resetting password will currently reset any ' +
|
||||
'end-to-end encryption keys on all devices, ' +
|
||||
'making encrypted chat history unreadable, ' +
|
||||
'unless you first export your room keys and re-import ' +
|
||||
'them afterwards. In future this will be improved.'
|
||||
) }
|
||||
</div>,
|
||||
button: "Continue",
|
||||
button: _t('Continue'),
|
||||
extraButtons: [
|
||||
<button className="mx_Dialog_primary"
|
||||
onClick={this._onExportE2eKeysClicked}>
|
||||
Export E2E room keys
|
||||
{ _t('Export E2E room keys') }
|
||||
</button>
|
||||
],
|
||||
onFinished: (confirmed) => {
|
||||
|
@ -150,7 +154,7 @@ module.exports = React.createClass({
|
|||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: title,
|
||||
description: body
|
||||
description: body,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -168,22 +172,20 @@ module.exports = React.createClass({
|
|||
else if (this.state.progress === "sent_email") {
|
||||
resetPasswordJsx = (
|
||||
<div>
|
||||
An email has been sent to {this.state.email}. Once you've followed
|
||||
the link it contains, click below.
|
||||
{ _t('An email has been sent to') } {this.state.email}. { _t('Once you've followed the link it contains, click below') }.
|
||||
<br />
|
||||
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
||||
value="I have verified my email address" />
|
||||
value={ _t('I have verified my email address') } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else if (this.state.progress === "complete") {
|
||||
resetPasswordJsx = (
|
||||
<div>
|
||||
<p>Your password has been reset.</p>
|
||||
<p>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.</p>
|
||||
<p>{ _t('Your password has been reset') }.</p>
|
||||
<p>{ _t('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') }.</p>
|
||||
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
||||
value="Return to login screen" />
|
||||
value={ _t('Return to login screen') } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -191,7 +193,7 @@ module.exports = React.createClass({
|
|||
resetPasswordJsx = (
|
||||
<div>
|
||||
<div className="mx_Login_prompt">
|
||||
To reset your password, enter the email address linked to your account:
|
||||
{ _t('To reset your password, enter the email address linked to your account') }:
|
||||
</div>
|
||||
<div>
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
|
@ -199,21 +201,21 @@ module.exports = React.createClass({
|
|||
name="reset_email" // define a name so browser's password autofill gets less confused
|
||||
value={this.state.email}
|
||||
onChange={this.onInputChanged.bind(this, "email")}
|
||||
placeholder="Email address" autoFocus />
|
||||
placeholder={ _t('Email address') } autoFocus />
|
||||
<br />
|
||||
<input className="mx_Login_field" ref="pass" type="password"
|
||||
name="reset_password"
|
||||
value={this.state.password}
|
||||
onChange={this.onInputChanged.bind(this, "password")}
|
||||
placeholder="New password" />
|
||||
placeholder={ _t('New password') } />
|
||||
<br />
|
||||
<input className="mx_Login_field" ref="pass" type="password"
|
||||
name="reset_password_confirm"
|
||||
value={this.state.password2}
|
||||
onChange={this.onInputChanged.bind(this, "password2")}
|
||||
placeholder="Confirm your new password" />
|
||||
placeholder={ _t('Confirm your new password') } />
|
||||
<br />
|
||||
<input className="mx_Login_submit" type="submit" value="Send Reset Email" />
|
||||
<input className="mx_Login_submit" type="submit" value={ _t('Send Reset Email') } />
|
||||
</form>
|
||||
<ServerConfig ref="serverConfig"
|
||||
withToggleButton={true}
|
||||
|
@ -230,7 +232,7 @@ module.exports = React.createClass({
|
|||
Return to login
|
||||
</a>
|
||||
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
|
||||
Create a new account
|
||||
{ _t('Create an account') }
|
||||
</a>
|
||||
<LoginFooter />
|
||||
</div>
|
||||
|
|
|
@ -18,11 +18,14 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import ReactDOM from 'react-dom';
|
||||
import url from 'url';
|
||||
import sdk from '../../../index';
|
||||
import Login from '../../../Login';
|
||||
|
||||
// For validating phone numbers without country codes
|
||||
const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/;
|
||||
|
||||
/**
|
||||
* A wire component which glues together login UI components and Login logic
|
||||
*/
|
||||
|
@ -125,7 +128,16 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onPhoneNumberChanged: function(phoneNumber) {
|
||||
this.setState({ phoneNumber: phoneNumber });
|
||||
// Validate the phone number entered
|
||||
if (!PHONE_NUMBER_REGEX.test(phoneNumber)) {
|
||||
this.setState({ errorText: 'The phone number entered looks invalid' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
phoneNumber: phoneNumber,
|
||||
errorText: null,
|
||||
});
|
||||
},
|
||||
|
||||
onServerConfigChange: function(config) {
|
||||
|
@ -210,15 +222,17 @@ module.exports = React.createClass({
|
|||
(this.state.enteredHomeserverUrl.startsWith("http:") ||
|
||||
!this.state.enteredHomeserverUrl.startsWith("http")))
|
||||
{
|
||||
const urlStart = <a href='https://www.google.com/search?&q=enable%20unsafe%20scripts'>;
|
||||
const urlEnd = </a>;
|
||||
errorText = <span>
|
||||
Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar.
|
||||
Either use HTTPS or <a href='https://www.google.com/search?&q=enable%20unsafe%20scripts'>enable unsafe scripts</a>
|
||||
{ _t('Can\'t connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s', {urlStart: urlStart, urlEnd: urlEnd})}
|
||||
</span>;
|
||||
}
|
||||
else {
|
||||
const urlStart = <a href={this.state.enteredHomeserverUrl}>;
|
||||
const urlEnd = </a>;
|
||||
errorText = <span>
|
||||
Can't connect to homeserver - please check your connectivity and ensure
|
||||
your <a href={ this.state.enteredHomeserverUrl }>homeserver's SSL certificate</a> is trusted.
|
||||
{ _t('Can\'t connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver\'s SSL certificate %(urlEnd)s is trusted', {urlStart: urlStart, urlEnd: urlEnd})}
|
||||
</span>;
|
||||
}
|
||||
}
|
||||
|
@ -230,12 +244,6 @@ module.exports = React.createClass({
|
|||
switch (step) {
|
||||
case 'm.login.password':
|
||||
const PasswordLogin = sdk.getComponent('login.PasswordLogin');
|
||||
// HSs that are not matrix.org may not be configured to have their
|
||||
// domain name === domain part.
|
||||
let hsDomain = url.parse(this.state.enteredHomeserverUrl).hostname;
|
||||
if (hsDomain !== 'matrix.org') {
|
||||
hsDomain = null;
|
||||
}
|
||||
return (
|
||||
<PasswordLogin
|
||||
onSubmit={this.onPasswordLogin}
|
||||
|
@ -247,7 +255,6 @@ module.exports = React.createClass({
|
|||
onPhoneNumberChanged={this.onPhoneNumberChanged}
|
||||
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
||||
loginIncorrect={this.state.loginIncorrect}
|
||||
hsDomain={hsDomain}
|
||||
/>
|
||||
);
|
||||
case 'm.login.cas':
|
||||
|
@ -261,8 +268,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
return (
|
||||
<div>
|
||||
Sorry, this homeserver is using a login which is not
|
||||
recognised ({step})
|
||||
{ _t('Sorry, this homeserver is using a login which is not recognised ')}({step})
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -279,7 +285,7 @@ module.exports = React.createClass({
|
|||
if (this.props.enableGuest) {
|
||||
loginAsGuestJsx =
|
||||
<a className="mx_Login_create" onClick={this._onLoginAsGuestClick} href="#">
|
||||
Login as guest
|
||||
{ _t('Login as guest')}
|
||||
</a>;
|
||||
}
|
||||
|
||||
|
@ -287,7 +293,7 @@ module.exports = React.createClass({
|
|||
if (this.props.onCancelClick) {
|
||||
returnToAppJsx =
|
||||
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
|
||||
Return to app
|
||||
{ _t('Return to app')}
|
||||
</a>;
|
||||
}
|
||||
|
||||
|
@ -296,7 +302,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_Login_box">
|
||||
<LoginHeader />
|
||||
<div>
|
||||
<h2>Sign in
|
||||
<h2>{ _t('Sign in')}
|
||||
{ loader }
|
||||
</h2>
|
||||
{ this.componentForStep(this.state.currentFlow) }
|
||||
|
@ -312,7 +318,7 @@ module.exports = React.createClass({
|
|||
{ this.state.errorText }
|
||||
</div>
|
||||
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
|
||||
Create a new account
|
||||
{ _t('Create an account')}
|
||||
</a>
|
||||
{ loginAsGuestJsx }
|
||||
{ returnToAppJsx }
|
||||
|
|
|
@ -16,9 +16,10 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('../../../index');
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'PostRegistration',
|
||||
|
@ -64,12 +65,12 @@ module.exports = React.createClass({
|
|||
<div className="mx_Login_box">
|
||||
<LoginHeader />
|
||||
<div className="mx_Login_profile">
|
||||
Set a display name:
|
||||
{ _t('Set a display name:') }
|
||||
<ChangeDisplayName />
|
||||
Upload an avatar:
|
||||
{ _t('Upload an avatar:') }
|
||||
<ChangeAvatar
|
||||
initialAvatarUrl={this.state.avatarUrl} />
|
||||
<button onClick={this.props.onComplete}>Continue</button>
|
||||
<button onClick={this.props.onComplete}>{ _t('Continue') }</button>
|
||||
{this.state.errorString}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -27,6 +27,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
|||
import RegistrationForm from '../../views/login/RegistrationForm';
|
||||
import CaptchaForm from '../../views/login/CaptchaForm';
|
||||
import RtsClient from '../../../RtsClient';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 6;
|
||||
|
||||
|
@ -123,18 +124,17 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onHsUrlChanged: function(newHsUrl) {
|
||||
this.setState({
|
||||
hsUrl: newHsUrl,
|
||||
onServerConfigChange: function(config) {
|
||||
let newState = {};
|
||||
if (config.hsUrl !== undefined) {
|
||||
newState.hsUrl = config.hsUrl;
|
||||
}
|
||||
if (config.isUrl !== undefined) {
|
||||
newState.isUrl = config.isUrl;
|
||||
}
|
||||
this.setState(newState, function() {
|
||||
this._replaceClient();
|
||||
});
|
||||
this._replaceClient();
|
||||
},
|
||||
|
||||
onIsUrlChanged: function(newIsUrl) {
|
||||
this.setState({
|
||||
isUrl: newIsUrl,
|
||||
});
|
||||
this._replaceClient();
|
||||
},
|
||||
|
||||
_replaceClient: function() {
|
||||
|
@ -163,7 +163,7 @@ module.exports = React.createClass({
|
|||
msisdn_available |= flow.stages.indexOf('m.login.msisdn') > -1;
|
||||
}
|
||||
if (!msisdn_available) {
|
||||
msg = "This server does not support authentication with a phone number";
|
||||
msg = _t('This server does not support authentication with a phone number.');
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
|
@ -261,29 +261,29 @@ module.exports = React.createClass({
|
|||
var errMsg;
|
||||
switch (errCode) {
|
||||
case "RegistrationForm.ERR_PASSWORD_MISSING":
|
||||
errMsg = "Missing password.";
|
||||
errMsg = _t('Missing password.');
|
||||
break;
|
||||
case "RegistrationForm.ERR_PASSWORD_MISMATCH":
|
||||
errMsg = "Passwords don't match.";
|
||||
errMsg = _t('Passwords don\'t match.');
|
||||
break;
|
||||
case "RegistrationForm.ERR_PASSWORD_LENGTH":
|
||||
errMsg = `Password too short (min ${MIN_PASSWORD_LENGTH}).`;
|
||||
errMsg = _t('Password too short (min %(MIN_PASSWORD_LENGTH)s).', {MIN_PASSWORD_LENGTH: $MIN_PASSWORD_LENGTH})
|
||||
break;
|
||||
case "RegistrationForm.ERR_EMAIL_INVALID":
|
||||
errMsg = "This doesn't look like a valid email address";
|
||||
errMsg = _t('This doesn\'t look like a valid email address.');
|
||||
break;
|
||||
case "RegistrationForm.ERR_PHONE_NUMBER_INVALID":
|
||||
errMsg = "This doesn't look like a valid phone number";
|
||||
errMsg = _t('This doesn\'t look like a valid phone number.');
|
||||
break;
|
||||
case "RegistrationForm.ERR_USERNAME_INVALID":
|
||||
errMsg = "User names may only contain letters, numbers, dots, hyphens and underscores.";
|
||||
errMsg = _t('User names may only contain letters, numbers, dots, hyphens and underscores.');
|
||||
break;
|
||||
case "RegistrationForm.ERR_USERNAME_BLANK":
|
||||
errMsg = "You need to enter a user name";
|
||||
errMsg = _t('You need to enter a user name.');
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown error code: %s", errCode);
|
||||
errMsg = "An unknown error occurred.";
|
||||
errMsg = _t('An unknown error occurred.');
|
||||
break;
|
||||
}
|
||||
this.setState({
|
||||
|
@ -390,8 +390,7 @@ module.exports = React.createClass({
|
|||
customIsUrl={this.props.customIsUrl}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
defaultIsUrl={this.props.defaultIsUrl}
|
||||
onHsUrlChanged={this.onHsUrlChanged}
|
||||
onIsUrlChanged={this.onIsUrlChanged}
|
||||
onServerConfigChange={this.onServerConfigChange}
|
||||
delayTimeMs={1000}
|
||||
/>
|
||||
</div>
|
||||
|
@ -402,7 +401,7 @@ module.exports = React.createClass({
|
|||
if (this.props.onCancelClick) {
|
||||
returnToAppJsx = (
|
||||
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
|
||||
Return to app
|
||||
{_t('Return to app')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -415,10 +414,10 @@ module.exports = React.createClass({
|
|||
this.state.teamSelected.domain + "/icon.png" :
|
||||
null}
|
||||
/>
|
||||
<h2>Create an account</h2>
|
||||
<h2>{_t('Create an account')}</h2>
|
||||
{registerBody}
|
||||
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
||||
I already have an account
|
||||
{_t('I already have an account')}
|
||||
</a>
|
||||
{returnToAppJsx}
|
||||
<LoginFooter />
|
||||
|
|
|
@ -59,7 +59,9 @@ module.exports = React.createClass({
|
|||
ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
props.oobData.avatarUrl,
|
||||
props.width, props.height, props.resizeMethod
|
||||
Math.floor(props.width * window.devicePixelRatio),
|
||||
Math.floor(props.height * window.devicePixelRatio),
|
||||
props.resizeMethod
|
||||
), // highest priority
|
||||
this.getRoomAvatarUrl(props),
|
||||
this.getOneToOneAvatar(props),
|
||||
|
@ -74,7 +76,9 @@ module.exports = React.createClass({
|
|||
|
||||
return props.room.getAvatarUrl(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
props.width, props.height, props.resizeMethod,
|
||||
Math.floor(props.width * window.devicePixelRatio),
|
||||
Math.floor(props.height * window.devicePixelRatio),
|
||||
props.resizeMethod,
|
||||
false
|
||||
);
|
||||
},
|
||||
|
@ -103,14 +107,18 @@ module.exports = React.createClass({
|
|||
}
|
||||
return theOtherGuy.getAvatarUrl(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
props.width, props.height, props.resizeMethod,
|
||||
Math.floor(props.width * window.devicePixelRatio),
|
||||
Math.floor(props.height * window.devicePixelRatio),
|
||||
props.resizeMethod,
|
||||
false
|
||||
);
|
||||
} else if (userIds.length == 1) {
|
||||
return mlist[userIds[0]].getAvatarUrl(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
props.width, props.height, props.resizeMethod,
|
||||
false
|
||||
Math.floor(props.width * window.devicePixelRatio),
|
||||
Math.floor(props.height * window.devicePixelRatio),
|
||||
props.resizeMethod,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
@ -47,16 +47,6 @@ export default React.createClass({
|
|||
children: React.PropTypes.node,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.priorActiveElement = document.activeElement;
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
if (this.priorActiveElement !== null) {
|
||||
this.priorActiveElement.focus();
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyDown: function(e) {
|
||||
if (e.keyCode === KeyCode.ESCAPE) {
|
||||
e.stopPropagation();
|
||||
|
@ -77,7 +67,7 @@ export default React.createClass({
|
|||
|
||||
render: function() {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
|
||||
|
||||
return (
|
||||
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
||||
<AccessibleButton onClick={this._onCancelClick}
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import { getAddressType, inviteMultipleToRoom } from '../../../Invite';
|
||||
import createRoom from '../../../createRoom';
|
||||
|
@ -48,11 +49,7 @@ module.exports = React.createClass({
|
|||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Start a chat",
|
||||
description: "Who would you like to communicate with?",
|
||||
value: "",
|
||||
placeholder: "Email, name or matrix ID",
|
||||
button: "Start Chat",
|
||||
focus: true
|
||||
};
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/*
|
||||
|
@ -69,7 +70,7 @@ export default React.createClass({
|
|||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
|
||||
|
||||
const title = this.props.action + " this person?";
|
||||
const title = _t("%(actionVerb)s this person?", { actionVerb: this.props.action});
|
||||
const confirmButtonClass = classnames({
|
||||
'mx_Dialog_primary': true,
|
||||
'danger': this.props.danger,
|
||||
|
@ -82,7 +83,7 @@ export default React.createClass({
|
|||
<form onSubmit={this.onOk}>
|
||||
<input className="mx_ConfirmUserActionDialog_reasonField"
|
||||
ref={this._collectReasonField}
|
||||
placeholder="Reason"
|
||||
placeholder={ _t("Reason") }
|
||||
autoFocus={true}
|
||||
/>
|
||||
</form>
|
||||
|
@ -111,7 +112,7 @@ export default React.createClass({
|
|||
</button>
|
||||
|
||||
<button onClick={this.onCancel}>
|
||||
Cancel
|
||||
{ _t("Cancel") }
|
||||
</button>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
|
|
76
src/components/views/dialogs/DeviceVerifyDialog.js
Normal file
76
src/components/views/dialogs/DeviceVerifyDialog.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import sdk from '../../../index';
|
||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||
|
||||
export default function DeviceVerifyDialog(props) {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
|
||||
const key = FormattingUtils.formatCryptoKey(props.device.getFingerprint());
|
||||
const body = (
|
||||
<div>
|
||||
<p>
|
||||
To verify that this device can be trusted, please contact its
|
||||
owner using some other means (e.g. in person or a phone call)
|
||||
and ask them whether the key they see in their User Settings
|
||||
for this device matches the key below:
|
||||
</p>
|
||||
<div className="mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>Device name:</label> <span>{ props.device.getDisplayName() }</span></li>
|
||||
<li><label>Device ID:</label> <span><code>{ props.device.deviceId}</code></span></li>
|
||||
<li><label>Device key:</label> <span><code><b>{ key }</b></code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
If it matches, press the verify button below.
|
||||
If it doesnt, then someone else is intercepting this device
|
||||
and you probably want to press the blacklist button instead.
|
||||
</p>
|
||||
<p>
|
||||
In future this verification process will be more sophisticated.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
function onFinished(confirm) {
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
props.userId, props.device.deviceId, true,
|
||||
);
|
||||
}
|
||||
props.onFinished(confirm);
|
||||
}
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title="Verify device"
|
||||
description={body}
|
||||
button="I verify that the keys match"
|
||||
onFinished={onFinished}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
DeviceVerifyDialog.propTypes = {
|
||||
userId: React.PropTypes.string.isRequired,
|
||||
device: React.PropTypes.object.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
|
@ -27,6 +27,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'ErrorDialog',
|
||||
|
@ -43,10 +44,10 @@ export default React.createClass({
|
|||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Error",
|
||||
description: "An error has occurred.",
|
||||
button: "OK",
|
||||
focus: true,
|
||||
title: null,
|
||||
description: null,
|
||||
button: null,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -60,13 +61,13 @@ export default React.createClass({
|
|||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
||||
title={this.props.title}>
|
||||
title={this.props.title || _t('Error')}>
|
||||
<div className="mx_Dialog_content">
|
||||
{this.props.description}
|
||||
{this.props.description || _t('An error has occurred.')}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button ref="button" className="mx_Dialog_primary" onClick={this.props.onFinished}>
|
||||
{this.props.button}
|
||||
{this.props.button || _t('OK')}
|
||||
</button>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
|
|
|
@ -20,6 +20,7 @@ import Matrix from 'matrix-js-sdk';
|
|||
import React from 'react';
|
||||
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
|
||||
|
@ -46,12 +47,6 @@ export default React.createClass({
|
|||
title: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Authentication",
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
authError: null,
|
||||
|
@ -105,7 +100,7 @@ export default React.createClass({
|
|||
return (
|
||||
<BaseDialog className="mx_InteractiveAuthDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={this.state.authError ? 'Error' : this.props.title}
|
||||
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
|
||||
>
|
||||
{content}
|
||||
</BaseDialog>
|
||||
|
|
|
@ -26,6 +26,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import dis from '../../../dispatcher';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'NeedToRegisterDialog',
|
||||
|
@ -38,13 +39,6 @@ module.exports = React.createClass({
|
|||
onFinished: React.PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Registration required",
|
||||
description: "A registered account is required for this action",
|
||||
};
|
||||
},
|
||||
|
||||
onRegisterClicked: function() {
|
||||
dis.dispatch({
|
||||
action: "start_upgrade_registration",
|
||||
|
@ -59,10 +53,10 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<BaseDialog className="mx_NeedToRegisterDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={this.props.title}
|
||||
title={this.props.title || _t('Registration required')}
|
||||
>
|
||||
<div className="mx_Dialog_content">
|
||||
{this.props.description}
|
||||
{this.props.description || _t('A registered account is required for this action')}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button className="mx_Dialog_primary" onClick={this.props.onFinished} autoFocus={true}>
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'QuestionDialog',
|
||||
|
@ -33,7 +34,6 @@ export default React.createClass({
|
|||
title: "",
|
||||
description: "",
|
||||
extraButtons: null,
|
||||
button: "OK",
|
||||
focus: true,
|
||||
hasCancelButton: true,
|
||||
};
|
||||
|
@ -47,12 +47,6 @@ export default React.createClass({
|
|||
this.props.onFinished(false);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
if (this.props.focus) {
|
||||
this.refs.button.focus();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const cancelButton = this.props.hasCancelButton ? (
|
||||
|
@ -69,8 +63,8 @@ export default React.createClass({
|
|||
{this.props.description}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button ref="button" className="mx_Dialog_primary" onClick={this.onOk}>
|
||||
{this.props.button}
|
||||
<button className="mx_Dialog_primary" onClick={this.onOk} autoFocus={this.props.focus}>
|
||||
{this.props.button || _t('OK')}
|
||||
</button>
|
||||
{this.props.extraButtons}
|
||||
{cancelButton}
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'TextInputDialog',
|
||||
|
@ -36,7 +37,6 @@ export default React.createClass({
|
|||
title: "",
|
||||
value: "",
|
||||
description: "",
|
||||
button: "OK",
|
||||
focus: true,
|
||||
};
|
||||
},
|
||||
|
@ -73,7 +73,7 @@ export default React.createClass({
|
|||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onCancel}>
|
||||
Cancel
|
||||
{ _t("Cancel") }
|
||||
</button>
|
||||
<button className="mx_Dialog_primary" onClick={this.onOk}>
|
||||
{this.props.button}
|
||||
|
|
|
@ -149,7 +149,7 @@ export default React.createClass({
|
|||
>
|
||||
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
||||
<h4>
|
||||
This room contains devices that you haven't seen before.
|
||||
"{this.props.room.name}" contains devices that you haven't seen before.
|
||||
</h4>
|
||||
{ warning }
|
||||
Unknown devices:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -138,7 +139,7 @@ export default React.createClass({
|
|||
onClick={this.onClick.bind(this, i)}
|
||||
onMouseEnter={this.onMouseEnter.bind(this, i)}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
key={this.props.addressList[i].userId}
|
||||
key={this.props.addressList[i].addressType + "/" + this.props.addressList[i].address}
|
||||
ref={(ref) => { this.addressListElement = ref; }}
|
||||
>
|
||||
<AddressTile address={this.props.addressList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />
|
||||
|
|
|
@ -50,42 +50,10 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
onVerifyClick: function() {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Verify device",
|
||||
description: (
|
||||
<div>
|
||||
<p>
|
||||
To verify that this device can be trusted, please contact its
|
||||
owner using some other means (e.g. in person or a phone call)
|
||||
and ask them whether the key they see in their User Settings
|
||||
for this device matches the key below:
|
||||
</p>
|
||||
<div className="mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>Device name:</label> <span>{ this.state.device.getDisplayName() }</span></li>
|
||||
<li><label>Device ID:</label> <span><code>{ this.state.device.deviceId}</code></span></li>
|
||||
<li><label>Device key:</label> <span><code><b>{ this.state.device.getFingerprint() }</b></code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
If it matches, press the verify button below.
|
||||
If it doesnt, then someone else is intercepting this device
|
||||
and you probably want to press the blacklist button instead.
|
||||
</p>
|
||||
<p>
|
||||
In future this verification process will be more sophisticated.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
button: "I verify that the keys match",
|
||||
onFinished: confirm=>{
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
this.props.userId, this.state.device.deviceId, true
|
||||
);
|
||||
}
|
||||
},
|
||||
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||
Modal.createDialog(DeviceVerifyDialog, {
|
||||
userId: this.props.userId,
|
||||
device: this.state.device,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ export default class DirectorySearchBox extends React.Component {
|
|||
className="mx_DirectorySearchBox_input"
|
||||
ref={this._collectInput}
|
||||
onChange={this._onChange} onKeyUp={this._onKeyUp}
|
||||
placeholder={this.props.placeholder}
|
||||
placeholder={this.props.placeholder} autoFocus
|
||||
/>
|
||||
{join_button}
|
||||
<span className="mx_DirectorySearchBox_clear_wrapper">
|
||||
|
|
|
@ -114,8 +114,11 @@ export default class Dropdown extends React.Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!nextProps.children || nextProps.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
this._reindexChildren(nextProps.children);
|
||||
const firstChild = React.Children.toArray(nextProps.children)[0];
|
||||
const firstChild = nextProps.children[0];
|
||||
this.setState({
|
||||
highlightedOption: firstChild ? firstChild.key : null,
|
||||
});
|
||||
|
@ -149,10 +152,12 @@ export default class Dropdown extends React.Component {
|
|||
}
|
||||
|
||||
_onInputClick(ev) {
|
||||
this.setState({
|
||||
expanded: !this.state.expanded,
|
||||
});
|
||||
ev.preventDefault();
|
||||
if (!this.state.expanded) {
|
||||
this.setState({
|
||||
expanded: true,
|
||||
});
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
_onMenuOptionClick(dropdownKey) {
|
||||
|
@ -249,7 +254,7 @@ export default class Dropdown extends React.Component {
|
|||
);
|
||||
});
|
||||
if (options.length === 0) {
|
||||
return [<div className="mx_Dropdown_option">
|
||||
return [<div key="0" className="mx_Dropdown_option">
|
||||
No results
|
||||
</div>];
|
||||
}
|
||||
|
|
128
src/components/views/elements/LanguageDropdown.js
Normal file
128
src/components/views/elements/LanguageDropdown.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
Copyright 2017 Marcel Radzio (MTRNord)
|
||||
Copyright 2017 Vector Creations Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import sdk from '../../../index';
|
||||
import UserSettingsStore from '../../../UserSettingsStore';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as languageHandler from '../../../languageHandler';
|
||||
|
||||
function languageMatchesSearchQuery(query, language) {
|
||||
if (language.label.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
|
||||
if (language.value.toUpperCase() == query.toUpperCase()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export default class LanguageDropdown extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._onSearchChange = this._onSearchChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
langs: null,
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
languageHandler.getAllLanguageKeysFromJson().then((langKeys) => {
|
||||
const langs = [];
|
||||
langKeys.forEach((languageKey) => {
|
||||
langs.push({
|
||||
value: languageKey,
|
||||
label: _t(languageKey)
|
||||
});
|
||||
});
|
||||
langs.sort(function(a, b){
|
||||
if(a.label < b.label) return -1;
|
||||
if(a.label > b.label) return 1;
|
||||
return 0;
|
||||
});
|
||||
this.setState({langs});
|
||||
}).catch(() => {
|
||||
this.setState({langs: ['en']});
|
||||
}).done();
|
||||
|
||||
if (!this.props.value) {
|
||||
// If no value is given, we start with the first
|
||||
// country selected, but our parent component
|
||||
// doesn't know this, therefore we do this.
|
||||
const _localSettings = UserSettingsStore.getLocalSettings();
|
||||
if (_localSettings.hasOwnProperty('language')) {
|
||||
this.props.onOptionChange(_localSettings.language);
|
||||
}else {
|
||||
const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser());
|
||||
this.props.onOptionChange(language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onSearchChange(search) {
|
||||
this.setState({
|
||||
searchQuery: search,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.langs === null) {
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const Dropdown = sdk.getComponent('elements.Dropdown');
|
||||
|
||||
let displayedLanguages;
|
||||
if (this.state.searchQuery) {
|
||||
displayedLanguages = this.state.langs.filter((lang) => {
|
||||
return languageMatchesSearchQuery(this.state.searchQuery, lang);
|
||||
});
|
||||
} else {
|
||||
displayedLanguages = this.state.langs;
|
||||
}
|
||||
|
||||
const options = displayedLanguages.map((language) => {
|
||||
return <div key={language.value}>
|
||||
{language.label}
|
||||
</div>;
|
||||
});
|
||||
|
||||
// default value here too, otherwise we need to handle null / undefined
|
||||
// values between mounting and the initial value propgating
|
||||
let value = null;
|
||||
const _localSettings = UserSettingsStore.getLocalSettings();
|
||||
if (_localSettings.hasOwnProperty('language')) {
|
||||
value = this.props.value || _localSettings.language;
|
||||
} else {
|
||||
const language = navigator.language || navigator.userLanguage;
|
||||
value = this.props.value || language;
|
||||
}
|
||||
|
||||
return <Dropdown className={this.props.className}
|
||||
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
|
||||
searchEnabled={true} value={value}
|
||||
>
|
||||
{options}
|
||||
</Dropdown>
|
||||
}
|
||||
}
|
||||
|
||||
LanguageDropdown.propTypes = {
|
||||
className: React.PropTypes.string,
|
||||
onOptionChange: React.PropTypes.func.isRequired,
|
||||
value: React.PropTypes.string,
|
||||
};
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
import React from 'react';
|
||||
const MemberAvatar = require('../avatars/MemberAvatar.js');
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberEventListSummary',
|
||||
|
@ -203,28 +204,146 @@ module.exports = React.createClass({
|
|||
* @param {boolean} plural whether there were multiple users undergoing the same
|
||||
* transition.
|
||||
* @param {number} repeats the number of times the transition was repeated in a row.
|
||||
* @returns {string} the written English equivalent of the transition.
|
||||
* @returns {string} the written Human Readable equivalent of the transition.
|
||||
*/
|
||||
_getDescriptionForTransition(t, plural, repeats) {
|
||||
const beConjugated = plural ? "were" : "was";
|
||||
const invitation = "their invitation" + (plural || (repeats > 1) ? "s" : "");
|
||||
|
||||
// The empty interpolations 'severalUsers' and 'oneUser'
|
||||
// are there only to show translators to non-English languages
|
||||
// that the verb is conjugated to plural or singular Subject.
|
||||
let res = null;
|
||||
const map = {
|
||||
"joined": "joined",
|
||||
"left": "left",
|
||||
"joined_and_left": "joined and left",
|
||||
"left_and_joined": "left and rejoined",
|
||||
"invite_reject": "rejected " + invitation,
|
||||
"invite_withdrawal": "had " + invitation + " withdrawn",
|
||||
"invited": beConjugated + " invited",
|
||||
"banned": beConjugated + " banned",
|
||||
"unbanned": beConjugated + " unbanned",
|
||||
"kicked": beConjugated + " kicked",
|
||||
};
|
||||
switch(t) {
|
||||
case "joined":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sjoined %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)sjoined %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sjoined", { severalUsers: "" })
|
||||
: _t("%(oneUser)sjoined", { oneUser: "" });
|
||||
}
|
||||
|
||||
if (Object.keys(map).includes(t)) {
|
||||
res = map[t] + (repeats > 1 ? " " + repeats + " times" : "" );
|
||||
break;
|
||||
case "left":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sleft %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)sleft %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sleft", { severalUsers: "" })
|
||||
: _t("%(oneUser)sleft", { oneUser: "" });
|
||||
} break;
|
||||
case "joined_and_left":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sjoined and left %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)sjoined and left %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sjoined and left", { severalUsers: "" })
|
||||
: _t("%(oneUser)sjoined and left", { oneUser: "" });
|
||||
}
|
||||
break;
|
||||
case "left_and_joined":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sleft and rejoined %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)sleft and rejoined %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)sleft and rejoined", { severalUsers: "" })
|
||||
: _t("%(oneUser)sleft and rejoined", { oneUser: "" });
|
||||
} break;
|
||||
break;
|
||||
case "invite_reject":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)srejected their invitations %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)srejected their invitation %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)srejected their invitations", { severalUsers: "" })
|
||||
: _t("%(oneUser)srejected their invitation", { oneUser: "" });
|
||||
}
|
||||
break;
|
||||
case "invite_withdrawal":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)shad their invitations withdrawn %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)shad their invitation withdrawn %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)shad their invitations withdrawn", { severalUsers: "" })
|
||||
: _t("%(oneUser)shad their invitation withdrawn", { oneUser: "" });
|
||||
}
|
||||
break;
|
||||
case "invited":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("were invited %(repeats)s times", { repeats: repeats })
|
||||
: _t("was invited %(repeats)s times", { repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("were invited")
|
||||
: _t("was invited");
|
||||
}
|
||||
break;
|
||||
case "banned":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("were banned %(repeats)s times", { repeats: repeats })
|
||||
: _t("was banned %(repeats)s times", { repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("were banned")
|
||||
: _t("was banned");
|
||||
}
|
||||
break;
|
||||
case "unbanned":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("were unbanned %(repeats)s times", { repeats: repeats })
|
||||
: _t("was unbanned %(repeats)s times", { repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("were unbanned")
|
||||
: _t("was unbanned");
|
||||
}
|
||||
break;
|
||||
case "kicked":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("were kicked %(repeats)s times", { repeats: repeats })
|
||||
: _t("was kicked %(repeats)s times", { repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("were kicked")
|
||||
: _t("was kicked");
|
||||
}
|
||||
break;
|
||||
case "changed_name":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)schanged their name %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)schanged their name %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)schanged their name", { severalUsers: "" })
|
||||
: _t("%(oneUser)schanged their name", { oneUser: "" });
|
||||
}
|
||||
break;
|
||||
case "changed_avatar":
|
||||
if (repeats > 1) {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)schanged their avatar %(repeats)s times", { severalUsers: "", repeats: repeats })
|
||||
: _t("%(oneUser)schanged their avatar %(repeats)s times", { oneUser: "", repeats: repeats });
|
||||
} else {
|
||||
res = (plural)
|
||||
? _t("%(severalUsers)schanged their avatar", { severalUsers: "" })
|
||||
: _t("%(oneUser)schanged their avatar", { oneUser: "" });
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -252,11 +371,12 @@ module.exports = React.createClass({
|
|||
return items[0];
|
||||
} else if (remaining) {
|
||||
items = items.slice(0, itemLimit);
|
||||
const other = " other" + (remaining > 1 ? "s" : "");
|
||||
return items.join(', ') + ' and ' + remaining + other;
|
||||
return (remaining > 1)
|
||||
? _t("%(items)s and %(remaining)s others", { items: items.join(', '), remaining: remaining } )
|
||||
: _t("%(items)s and one other", { items: items.join(', ') });
|
||||
} else {
|
||||
const lastItem = items.pop();
|
||||
return items.join(', ') + ' and ' + lastItem;
|
||||
return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -267,7 +387,7 @@ module.exports = React.createClass({
|
|||
);
|
||||
});
|
||||
return (
|
||||
<span className="mx_MemberEventListSummary_avatars">
|
||||
<span className="mx_MemberEventListSummary_avatars" onClick={ this._toggleSummary }>
|
||||
{avatars}
|
||||
</span>
|
||||
);
|
||||
|
@ -289,7 +409,24 @@ module.exports = React.createClass({
|
|||
switch (e.mxEvent.getContent().membership) {
|
||||
case 'invite': return 'invited';
|
||||
case 'ban': return 'banned';
|
||||
case 'join': return 'joined';
|
||||
case 'join':
|
||||
if (e.mxEvent.getPrevContent().membership === 'join') {
|
||||
if (e.mxEvent.getContent().displayname !==
|
||||
e.mxEvent.getPrevContent().displayname)
|
||||
{
|
||||
return 'changed_name';
|
||||
}
|
||||
else if (e.mxEvent.getContent().avatar_url !==
|
||||
e.mxEvent.getPrevContent().avatar_url)
|
||||
{
|
||||
return 'changed_avatar';
|
||||
}
|
||||
// console.log("MELS ignoring duplicate membership join event");
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return 'joined';
|
||||
}
|
||||
case 'leave':
|
||||
if (e.mxEvent.getSender() === e.mxEvent.getStateKey()) {
|
||||
switch (e.mxEvent.getPrevContent().membership) {
|
||||
|
@ -350,6 +487,7 @@ module.exports = React.createClass({
|
|||
|
||||
render: function() {
|
||||
const eventsToRender = this.props.events;
|
||||
const eventIds = eventsToRender.map(e => e.getId()).join(',');
|
||||
const fewEvents = eventsToRender.length < this.props.threshold;
|
||||
const expanded = this.state.expanded || fewEvents;
|
||||
|
||||
|
@ -360,7 +498,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (fewEvents) {
|
||||
return (
|
||||
<div className="mx_MemberEventListSummary">
|
||||
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
|
||||
{expandedEvents}
|
||||
</div>
|
||||
);
|
||||
|
@ -418,7 +556,7 @@ module.exports = React.createClass({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="mx_MemberEventListSummary">
|
||||
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
|
||||
{toggleButton}
|
||||
{summaryContainer}
|
||||
{expanded ? <div className="mx_MemberEventListSummary_line"> </div> : null}
|
||||
|
|
|
@ -19,10 +19,8 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import * as Roles from '../../../Roles';
|
||||
|
||||
var LEVEL_ROLE_MAP = {};
|
||||
var reverseRoles = {};
|
||||
Object.keys(Roles.LEVEL_ROLE_MAP).forEach(function(key) {
|
||||
reverseRoles[Roles.LEVEL_ROLE_MAP[key]] = key;
|
||||
});
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'PowerSelector',
|
||||
|
@ -44,9 +42,16 @@ module.exports = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
custom: (Roles.LEVEL_ROLE_MAP[this.props.value] === undefined),
|
||||
custom: (LEVEL_ROLE_MAP[this.props.value] === undefined),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
LEVEL_ROLE_MAP = Roles.levelRoleMap();
|
||||
Object.keys(LEVEL_ROLE_MAP).forEach(function(key) {
|
||||
reverseRoles[LEVEL_ROLE_MAP[key]] = key;
|
||||
});
|
||||
},
|
||||
|
||||
onSelectChange: function(event) {
|
||||
this.setState({ custom: event.target.value === "Custom" });
|
||||
|
@ -94,7 +99,7 @@ module.exports = React.createClass({
|
|||
selectValue = "Custom";
|
||||
}
|
||||
else {
|
||||
selectValue = Roles.LEVEL_ROLE_MAP[this.props.value] || "Custom";
|
||||
selectValue = LEVEL_ROLE_MAP[this.props.value] || "Custom";
|
||||
}
|
||||
var select;
|
||||
if (this.props.disabled) {
|
||||
|
@ -105,7 +110,7 @@ module.exports = React.createClass({
|
|||
const levels = [0, 50, 100];
|
||||
let options = levels.map((level) => {
|
||||
return {
|
||||
value: Roles.LEVEL_ROLE_MAP[level],
|
||||
value: LEVEL_ROLE_MAP[level],
|
||||
// Give a userDefault (users_default in the power event) of 0 but
|
||||
// because level !== undefined, this should never be used.
|
||||
text: Roles.textualPowerLevel(level, 0),
|
||||
|
|
|
@ -19,7 +19,6 @@ import React from 'react';
|
|||
import sdk from '../../../index';
|
||||
|
||||
import { COUNTRIES } from '../../../phonenumber';
|
||||
import { charactersToImageNode } from '../../../HtmlUtils';
|
||||
|
||||
const COUNTRIES_BY_ISO2 = new Object(null);
|
||||
for (const c of COUNTRIES) {
|
||||
|
@ -27,9 +26,14 @@ for (const c of COUNTRIES) {
|
|||
}
|
||||
|
||||
function countryMatchesSearchQuery(query, country) {
|
||||
// Remove '+' if present (when searching for a prefix)
|
||||
if (query[0] === '+') {
|
||||
query = query.slice(1);
|
||||
}
|
||||
|
||||
if (country.name.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
|
||||
if (country.iso2 == query.toUpperCase()) return true;
|
||||
if (country.prefix == query) return true;
|
||||
if (country.prefix.indexOf(query) !== -1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -37,10 +41,12 @@ export default class CountryDropdown extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this._onSearchChange = this._onSearchChange.bind(this);
|
||||
this._onOptionChange = this._onOptionChange.bind(this);
|
||||
this._getShortOption = this._getShortOption.bind(this);
|
||||
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
|
@ -48,7 +54,7 @@ export default class CountryDropdown extends React.Component {
|
|||
// If no value is given, we start with the first
|
||||
// country selected, but our parent component
|
||||
// doesn't know this, therefore we do this.
|
||||
this.props.onOptionChange(COUNTRIES[0].iso2);
|
||||
this.props.onOptionChange(COUNTRIES[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,14 +64,26 @@ export default class CountryDropdown extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
_onOptionChange(iso2) {
|
||||
this.props.onOptionChange(COUNTRIES_BY_ISO2[iso2]);
|
||||
}
|
||||
|
||||
_flagImgForIso2(iso2) {
|
||||
// Unicode Regional Indicator Symbol letter 'A'
|
||||
const RIS_A = 0x1F1E6;
|
||||
const ASCII_A = 65;
|
||||
return charactersToImageNode(iso2, true,
|
||||
RIS_A + (iso2.charCodeAt(0) - ASCII_A),
|
||||
RIS_A + (iso2.charCodeAt(1) - ASCII_A),
|
||||
);
|
||||
return <img src={`flags/${iso2}.png`}/>;
|
||||
}
|
||||
|
||||
_getShortOption(iso2) {
|
||||
if (!this.props.isSmall) {
|
||||
return undefined;
|
||||
}
|
||||
let countryPrefix;
|
||||
if (this.props.showPrefix) {
|
||||
countryPrefix = '+' + COUNTRIES_BY_ISO2[iso2].prefix;
|
||||
}
|
||||
return <span>
|
||||
{ this._flagImgForIso2(iso2) }
|
||||
{ countryPrefix }
|
||||
</span>;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -94,7 +112,7 @@ export default class CountryDropdown extends React.Component {
|
|||
const options = displayedCountries.map((country) => {
|
||||
return <div key={country.iso2}>
|
||||
{this._flagImgForIso2(country.iso2)}
|
||||
{country.name}
|
||||
{country.name} <span>(+{country.prefix})</span>
|
||||
</div>;
|
||||
});
|
||||
|
||||
|
@ -102,18 +120,21 @@ export default class CountryDropdown extends React.Component {
|
|||
// values between mounting and the initial value propgating
|
||||
const value = this.props.value || COUNTRIES[0].iso2;
|
||||
|
||||
return <Dropdown className={this.props.className}
|
||||
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
|
||||
menuWidth={298} getShortOption={this._flagImgForIso2}
|
||||
return <Dropdown className={this.props.className + " left_aligned"}
|
||||
onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange}
|
||||
menuWidth={298} getShortOption={this._getShortOption}
|
||||
value={value} searchEnabled={true}
|
||||
>
|
||||
{options}
|
||||
</Dropdown>
|
||||
</Dropdown>;
|
||||
}
|
||||
}
|
||||
|
||||
CountryDropdown.propTypes = {
|
||||
className: React.PropTypes.string,
|
||||
isSmall: React.PropTypes.bool,
|
||||
// if isSmall, show +44 in the selected value
|
||||
showPrefix: React.PropTypes.bool,
|
||||
onOptionChange: React.PropTypes.func.isRequired,
|
||||
value: React.PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {field_input_incorrect} from '../../../UiEffects';
|
||||
|
||||
|
||||
|
@ -90,8 +91,11 @@ class PasswordLogin extends React.Component {
|
|||
}
|
||||
|
||||
onPhoneCountryChanged(country) {
|
||||
this.setState({phoneCountry: country});
|
||||
this.props.onPhoneCountryChanged(country);
|
||||
this.setState({
|
||||
phoneCountry: country.iso2,
|
||||
phonePrefix: country.prefix,
|
||||
});
|
||||
this.props.onPhoneCountryChanged(country.iso2);
|
||||
}
|
||||
|
||||
onPhoneNumberChanged(ev) {
|
||||
|
@ -118,42 +122,29 @@ class PasswordLogin extends React.Component {
|
|||
autoFocus
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_MXID:
|
||||
const mxidInputClasses = classNames({
|
||||
"mx_Login_field": true,
|
||||
"mx_Login_username": true,
|
||||
"mx_Login_field_has_suffix": Boolean(this.props.hsDomain),
|
||||
});
|
||||
let suffix = null;
|
||||
if (this.props.hsDomain) {
|
||||
suffix = <div className="mx_Login_username_suffix">
|
||||
:{this.props.hsDomain}
|
||||
</div>;
|
||||
}
|
||||
return <div className="mx_Login_username_group">
|
||||
<div className="mx_Login_username_prefix">@</div>
|
||||
<input
|
||||
className={mxidInputClasses}
|
||||
key="username_input"
|
||||
type="text"
|
||||
name="username" // make it a little easier for browser's remember-password
|
||||
onChange={this.onUsernameChanged}
|
||||
placeholder="username"
|
||||
value={this.state.username}
|
||||
autoFocus
|
||||
/>
|
||||
{suffix}
|
||||
</div>;
|
||||
return <input
|
||||
className="mx_Login_field mx_Login_username"
|
||||
key="username_input"
|
||||
type="text"
|
||||
name="username" // make it a little easier for browser's remember-password
|
||||
onChange={this.onUsernameChanged}
|
||||
placeholder={_t('User name')}
|
||||
value={this.state.username}
|
||||
autoFocus
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_PHONE:
|
||||
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
|
||||
return <div className="mx_Login_phoneSection">
|
||||
<CountryDropdown
|
||||
className="mx_Login_phoneCountry"
|
||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
||||
ref="phone_country"
|
||||
onOptionChange={this.onPhoneCountryChanged}
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
/>
|
||||
<input
|
||||
className="mx_Login_phoneNumberField mx_Login_field"
|
||||
className="mx_Login_phoneNumberField mx_Login_field mx_Login_field_has_prefix"
|
||||
ref="phoneNumber"
|
||||
key="phone_input"
|
||||
type="text"
|
||||
|
@ -173,7 +164,7 @@ class PasswordLogin extends React.Component {
|
|||
if (this.props.onForgotPasswordClick) {
|
||||
forgotPasswordJsx = (
|
||||
<a className="mx_Login_forgot" onClick={this.props.onForgotPasswordClick} href="#">
|
||||
Forgot your password?
|
||||
{ _t('Forgot your password?') }
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -191,24 +182,24 @@ class PasswordLogin extends React.Component {
|
|||
<div>
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
<div className="mx_Login_type_container">
|
||||
<label className="mx_Login_type_label">I want to sign in with my</label>
|
||||
<label className="mx_Login_type_label">{ _t('I want to sign in with') }</label>
|
||||
<Dropdown
|
||||
className="mx_Login_type_dropdown"
|
||||
value={this.state.loginType}
|
||||
onOptionChange={this.onLoginTypeChange}>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>Matrix ID</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>Email Address</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>Phone</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('my Matrix ID') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
{loginField}
|
||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||
name="password"
|
||||
value={this.state.password} onChange={this.onPasswordChanged}
|
||||
placeholder="Password" />
|
||||
placeholder={ _t('Password') } />
|
||||
<br />
|
||||
{forgotPasswordJsx}
|
||||
<input className="mx_Login_submit" type="submit" value="Sign in" />
|
||||
<input className="mx_Login_submit" type="submit" value={ _t('Sign in') } />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
@ -231,7 +222,6 @@ PasswordLogin.propTypes = {
|
|||
onPhoneNumberChanged: React.PropTypes.func,
|
||||
onPasswordChanged: React.PropTypes.func,
|
||||
loginIncorrect: React.PropTypes.bool,
|
||||
hsDomain: React.PropTypes.string,
|
||||
};
|
||||
|
||||
module.exports = PasswordLogin;
|
||||
|
|
|
@ -100,7 +100,7 @@ module.exports = React.createClass({
|
|||
if (this.refs.email.value == '') {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning",
|
||||
title: "Warning!",
|
||||
description:
|
||||
<div>
|
||||
If you don't specify an email address, you won't be able to reset your password.<br/>
|
||||
|
@ -270,7 +270,8 @@ module.exports = React.createClass({
|
|||
|
||||
_onPhoneCountryChange(newVal) {
|
||||
this.setState({
|
||||
phoneCountry: newVal,
|
||||
phoneCountry: newVal.iso2,
|
||||
phonePrefix: newVal.prefix,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -313,14 +314,19 @@ module.exports = React.createClass({
|
|||
const phoneSection = (
|
||||
<div className="mx_Login_phoneSection">
|
||||
<CountryDropdown ref="phone_country" onOptionChange={this._onPhoneCountryChange}
|
||||
className="mx_Login_phoneCountry"
|
||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
/>
|
||||
<input type="text" ref="phoneNumber"
|
||||
placeholder="Mobile phone number (optional)"
|
||||
defaultValue={this.props.defaultPhoneNumber}
|
||||
className={this._classForField(
|
||||
FIELD_PHONE_NUMBER, 'mx_Login_phoneNumberField', 'mx_Login_field'
|
||||
FIELD_PHONE_NUMBER,
|
||||
'mx_Login_phoneNumberField',
|
||||
'mx_Login_field',
|
||||
'mx_Login_field_has_prefix'
|
||||
)}
|
||||
onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}}
|
||||
value={self.state.phoneNumber}
|
||||
|
|
|
@ -20,6 +20,7 @@ import React from 'react';
|
|||
import filesize from 'filesize';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {decryptFile} from '../../../utils/DecryptFile';
|
||||
import Tinter from '../../../Tinter';
|
||||
import request from 'browser-request';
|
||||
|
@ -202,7 +203,7 @@ module.exports = React.createClass({
|
|||
* @return {string} the human readable link text for the attachment.
|
||||
*/
|
||||
presentableTextForFile: function(content) {
|
||||
var linkText = 'Attachment';
|
||||
var linkText = _t("Attachment");
|
||||
if (content.body && content.body.length > 0) {
|
||||
// The content body should be the name of the file including a
|
||||
// file extension.
|
||||
|
@ -261,7 +262,7 @@ module.exports = React.createClass({
|
|||
const content = this.props.mxEvent.getContent();
|
||||
const text = this.presentableTextForFile(content);
|
||||
const isEncrypted = content.file !== undefined;
|
||||
const fileName = content.body && content.body.length > 0 ? content.body : "Attachment";
|
||||
const fileName = content.body && content.body.length > 0 ? content.body : _t("Attachment");
|
||||
const contentUrl = this._getContentUrl();
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
|
@ -283,7 +284,8 @@ module.exports = React.createClass({
|
|||
}).catch((err) => {
|
||||
console.warn("Unable to decrypt attachment: ", err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
description: "Error decrypting attachment"
|
||||
title: _t("Error"),
|
||||
description: _t("Error decrypting attachment"),
|
||||
});
|
||||
}).finally(() => {
|
||||
decrypting = false;
|
||||
|
@ -295,7 +297,7 @@ module.exports = React.createClass({
|
|||
<span className="mx_MFileBody" ref="body">
|
||||
<div className="mx_MImageBody_download">
|
||||
<a href="javascript:void(0)" onClick={decrypt}>
|
||||
Decrypt {text}
|
||||
{ _t("Decrypt %(text)s", { text: text }) }
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
|
@ -314,7 +316,7 @@ module.exports = React.createClass({
|
|||
// We can't provide a Content-Disposition header like we would for HTTP.
|
||||
download: fileName,
|
||||
target: "_blank",
|
||||
textContent: "Download " + text,
|
||||
textContent: _t("Download %(text)s", { text: text }),
|
||||
}, "*");
|
||||
};
|
||||
|
||||
|
@ -362,7 +364,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_MImageBody_download">
|
||||
<a href={contentUrl} download={fileName} target="_blank" rel="noopener">
|
||||
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage"/>
|
||||
Download {text}
|
||||
{ _t("Download %(text)s", { text: text }) }
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
|
@ -371,7 +373,7 @@ module.exports = React.createClass({
|
|||
} else {
|
||||
var extra = text ? (': ' + text) : '';
|
||||
return <span className="mx_MFileBody">
|
||||
Invalid file{extra}
|
||||
{ _t("Invalid file%(extra)s", { extra: extra }) }
|
||||
</span>;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -19,6 +19,7 @@ var React = require('react');
|
|||
var ObjectUtils = require("../../../ObjectUtils");
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
var sdk = require("../../../index");
|
||||
import { _t } from '../../../languageHandler';
|
||||
var Modal = require("../../../Modal");
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -154,8 +155,8 @@ module.exports = React.createClass({
|
|||
else {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Invalid alias format",
|
||||
description: "'" + alias + "' is not a valid format for an alias",
|
||||
title: _t('Invalid alias format'),
|
||||
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -170,8 +171,8 @@ module.exports = React.createClass({
|
|||
else {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Invalid address format",
|
||||
description: "'" + alias + "' is not a valid format for an address",
|
||||
title: _t('Invalid address format'),
|
||||
description: _t('\'%(alias)s\' is not a valid format for an address', { alias: alias }),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -203,7 +204,7 @@ module.exports = React.createClass({
|
|||
if (this.props.canSetCanonicalAlias) {
|
||||
canonical_alias_section = (
|
||||
<select onChange={this.onCanonicalAliasChange} defaultValue={ this.state.canonicalAlias }>
|
||||
<option value="" key="unset">not specified</option>
|
||||
<option value="" key="unset">{ _t('not specified') }</option>
|
||||
{
|
||||
Object.keys(self.state.domainToAliases).map(function(domain, i) {
|
||||
return self.state.domainToAliases[domain].map(function(alias, j) {
|
||||
|
@ -220,7 +221,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
else {
|
||||
canonical_alias_section = (
|
||||
<b>{ this.state.canonicalAlias || "not set" }</b>
|
||||
<b>{ this.state.canonicalAlias || _t('not set') }</b>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -254,13 +255,13 @@ module.exports = React.createClass({
|
|||
<div>
|
||||
<h3>Addresses</h3>
|
||||
<div className="mx_RoomSettings_aliasLabel">
|
||||
The main address for this room is: { canonical_alias_section }
|
||||
{ _t('The main address for this room is') }: { canonical_alias_section }
|
||||
</div>
|
||||
<div className="mx_RoomSettings_aliasLabel">
|
||||
{ (this.state.domainToAliases[localDomain] &&
|
||||
this.state.domainToAliases[localDomain].length > 0)
|
||||
? "Local addresses for this room:"
|
||||
: "This room has no local addresses" }
|
||||
? _t('Local addresses for this room:')
|
||||
: _t('This room has no local addresses') }
|
||||
</div>
|
||||
<div className="mx_RoomSettings_aliasesTable">
|
||||
{ (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
|
||||
|
@ -268,7 +269,7 @@ module.exports = React.createClass({
|
|||
if (this.props.canSetAliases) {
|
||||
deleteButton = (
|
||||
<img src="img/cancel-small.svg" width="14" height="14"
|
||||
alt="Delete" onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
|
||||
alt={ _t('Delete') } onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
|
||||
);
|
||||
}
|
||||
return (
|
||||
|
@ -276,7 +277,7 @@ module.exports = React.createClass({
|
|||
<EditableText
|
||||
className="mx_RoomSettings_alias mx_RoomSettings_editable"
|
||||
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
|
||||
placeholder={ "New address (e.g. #foo:" + localDomain + ")" }
|
||||
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
|
||||
blurToCancel={ false }
|
||||
onValueChanged={ self.onAliasChanged.bind(self, localDomain, i) }
|
||||
editable={ self.props.canSetAliases }
|
||||
|
@ -294,7 +295,7 @@ module.exports = React.createClass({
|
|||
ref="add_alias"
|
||||
className="mx_RoomSettings_alias mx_RoomSettings_editable"
|
||||
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
|
||||
placeholder={ "New address (e.g. #foo:" + localDomain + ")" }
|
||||
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
|
||||
blurToCancel={ false }
|
||||
onValueChanged={ self.onAliasAdded } />
|
||||
<div className="mx_RoomSettings_addAlias mx_filterFlipColor">
|
||||
|
|
|
@ -16,8 +16,10 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
|
||||
var React = require('react');
|
||||
var classNames = require("classnames");
|
||||
import { _t } from '../../../languageHandler';
|
||||
var Modal = require('../../../Modal');
|
||||
|
||||
var sdk = require('../../../index');
|
||||
|
@ -129,6 +131,9 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
* for now.
|
||||
*/
|
||||
tileShape: React.PropTypes.string,
|
||||
|
||||
// show twelve hour timestamps
|
||||
isTwelveHour: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -295,16 +300,6 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
const receiptOffset = 15;
|
||||
let left = 0;
|
||||
|
||||
// It's possible that the receipt was sent several days AFTER the event.
|
||||
// If it is, we want to display the complete date along with the HH:MM:SS,
|
||||
// rather than just HH:MM:SS.
|
||||
let dayAfterEvent = new Date(this.props.mxEvent.getTs());
|
||||
dayAfterEvent.setDate(dayAfterEvent.getDate() + 1);
|
||||
dayAfterEvent.setHours(0);
|
||||
dayAfterEvent.setMinutes(0);
|
||||
dayAfterEvent.setSeconds(0);
|
||||
let dayAfterEventTime = dayAfterEvent.getTime();
|
||||
|
||||
var receipts = this.props.readReceipts || [];
|
||||
for (var i = 0; i < receipts.length; ++i) {
|
||||
var receipt = receipts[i];
|
||||
|
@ -340,7 +335,6 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
suppressAnimation={this._suppressReadReceiptAnimation}
|
||||
onClick={this.toggleAllReadAvatars}
|
||||
timestamp={receipt.ts}
|
||||
showFullTimestamp={receipt.ts >= dayAfterEventTime}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -401,8 +395,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
var msgtype = content.msgtype;
|
||||
var eventType = this.props.mxEvent.getType();
|
||||
|
||||
// Info messages are basically information about commands processed on a
|
||||
// room, or emote messages
|
||||
// Info messages are basically information about commands processed on a room
|
||||
var isInfoMessage = (eventType !== 'm.room.message');
|
||||
|
||||
var EventTileType = sdk.getComponent(eventTileTypes[eventType]);
|
||||
|
@ -416,9 +409,10 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
|
||||
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
|
||||
|
||||
var classes = classNames({
|
||||
const classes = classNames({
|
||||
mx_EventTile: true,
|
||||
mx_EventTile_info: isInfoMessage,
|
||||
mx_EventTile_12hr: this.props.isTwelveHour,
|
||||
mx_EventTile_encrypting: this.props.eventSendStatus == 'encrypting',
|
||||
mx_EventTile_sending: isSending,
|
||||
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
|
||||
|
@ -430,7 +424,8 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
menu: this.state.menu,
|
||||
mx_EventTile_verified: this.state.verified == true,
|
||||
mx_EventTile_unverified: this.state.verified == false,
|
||||
mx_EventTile_bad: this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted',
|
||||
mx_EventTile_bad: msgtype === 'm.bad.encrypted',
|
||||
mx_EventTile_emote: msgtype === 'm.emote',
|
||||
mx_EventTile_redacted: isRedacted,
|
||||
});
|
||||
|
||||
|
@ -475,9 +470,9 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
if (needsSenderProfile) {
|
||||
let aux = null;
|
||||
if (!this.props.tileShape) {
|
||||
if (msgtype === 'm.image') aux = "sent an image";
|
||||
else if (msgtype === 'm.video') aux = "sent a video";
|
||||
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||
if (msgtype === 'm.image') aux = _t('sent an image');
|
||||
else if (msgtype === 'm.video') aux = _t('sent a video');
|
||||
else if (msgtype === 'm.file') aux = _t('uploaded a file');
|
||||
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />;
|
||||
}
|
||||
else {
|
||||
|
@ -485,36 +480,34 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
var editButton = (
|
||||
const editButton = (
|
||||
<span className="mx_EventTile_editButton" title="Options" onClick={this.onEditClicked} />
|
||||
);
|
||||
|
||||
var e2e;
|
||||
let e2e;
|
||||
// cosmetic padlocks:
|
||||
if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') {
|
||||
e2e = <img style={{ cursor: 'initial', marginLeft: '-1px' }} className="mx_EventTile_e2eIcon" src="img/e2e-verified.svg" width="10" height="12" />;
|
||||
e2e = <img style={{ cursor: 'initial', marginLeft: '-1px' }} className="mx_EventTile_e2eIcon" alt="Encrypted by verified device" src="img/e2e-verified.svg" width="10" height="12" />;
|
||||
}
|
||||
// real padlocks
|
||||
else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) {
|
||||
if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') {
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" src="img/e2e-blocked.svg" width="12" height="12" style={{ marginLeft: "-1px" }} />;
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" alt="Undecryptable" src="img/e2e-blocked.svg" width="12" height="12" style={{ marginLeft: "-1px" }} />;
|
||||
}
|
||||
else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) {
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" src="img/e2e-verified.svg" width="10" height="12"/>;
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" alt="Encrypted by verified device" src="img/e2e-verified.svg" width="10" height="12"/>;
|
||||
}
|
||||
else {
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" src="img/e2e-warning.svg" width="15" height="12" style={{ marginLeft: "-2px" }}/>;
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" alt="Encrypted by unverified device" src="img/e2e-warning.svg" width="15" height="12" style={{ marginLeft: "-2px" }}/>;
|
||||
}
|
||||
}
|
||||
else if (e2eEnabled) {
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" src="img/e2e-unencrypted.svg" width="12" height="12"/>;
|
||||
e2e = <img onClick={ this.onCryptoClicked } className="mx_EventTile_e2eIcon" alt="Unencrypted message" src="img/e2e-unencrypted.svg" width="12" height="12"/>;
|
||||
}
|
||||
const timestamp = this.props.mxEvent.getTs() ?
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
|
||||
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;
|
||||
|
||||
if (this.props.tileShape === "notif") {
|
||||
var room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
||||
|
||||
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="mx_EventTile_roomName">
|
||||
|
|
96
src/components/views/rooms/ForwardMessage.js
Normal file
96
src/components/views/rooms/ForwardMessage.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 Michael Telatynski
|
||||
|
||||
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 { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import dis from '../../../dispatcher';
|
||||
import KeyCode from '../../../KeyCode';
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ForwardMessage',
|
||||
|
||||
propTypes: {
|
||||
currentRoomId: React.PropTypes.string.isRequired,
|
||||
|
||||
/* the MatrixEvent to be forwarded */
|
||||
mxEvent: React.PropTypes.object.isRequired,
|
||||
|
||||
onCancelClick: React.PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
dis.dispatch({
|
||||
action: 'ui_opacity',
|
||||
leftOpacity: 1.0,
|
||||
rightOpacity: 0.3,
|
||||
middleOpacity: 0.5,
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
document.addEventListener('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.dispatch({
|
||||
action: 'ui_opacity',
|
||||
sideOpacity: 1.0,
|
||||
middleOpacity: 1.0,
|
||||
});
|
||||
dis.unregister(this.dispatcherRef);
|
||||
document.removeEventListener('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
if (payload.action === 'view_room') {
|
||||
const event = this.props.mxEvent;
|
||||
const Client = MatrixClientPeg.get();
|
||||
Client.sendEvent(payload.room_id, event.getType(), event.getContent()).done(() => {
|
||||
dis.dispatch({action: 'message_sent'});
|
||||
}, (err) => {
|
||||
if (err.name === "UnknownDeviceError") {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: Client.getRoom(payload.room_id),
|
||||
});
|
||||
}
|
||||
dis.dispatch({action: 'message_send_failed'});
|
||||
});
|
||||
if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick();
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyDown: function(ev) {
|
||||
switch (ev.keyCode) {
|
||||
case KeyCode.ESCAPE:
|
||||
this.props.onCancelClick();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ForwardMessage">
|
||||
<h1>{_t('Please select the destination room for this message')}</h1>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -100,7 +100,9 @@ module.exports = React.createClass({
|
|||
|
||||
render: function() {
|
||||
var p = this.state.preview;
|
||||
if (!p) return <div/>;
|
||||
if (!p || Object.keys(p).length === 0) {
|
||||
return <div/>;
|
||||
}
|
||||
|
||||
// FIXME: do we want to factor out all image displaying between this and MImageBody - especially for lightboxing?
|
||||
var image = p["og:image"];
|
||||
|
|
|
@ -31,6 +31,7 @@ import classNames from 'classnames';
|
|||
import dis from '../../../dispatcher';
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import createRoom from '../../../createRoom';
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
import Unread from '../../../Unread';
|
||||
|
@ -219,7 +220,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
|
||||
onKick: function() {
|
||||
const membership = this.props.member.membership;
|
||||
const kickLabel = membership === "invite" ? "Disinvite" : "Kick";
|
||||
const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
|
||||
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||
Modal.createDialog(ConfirmUserActionDialog, {
|
||||
member: this.props.member,
|
||||
|
@ -241,7 +242,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Kick error: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to kick",
|
||||
title: _t("Failed to kick"),
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
}
|
||||
|
@ -256,7 +257,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||
Modal.createDialog(ConfirmUserActionDialog, {
|
||||
member: this.props.member,
|
||||
action: this.props.member.membership == 'ban' ? 'Unban' : 'Ban',
|
||||
action: this.props.member.membership == 'ban' ? _t("Unban") : _t("Ban"),
|
||||
askReason: this.props.member.membership != 'ban',
|
||||
danger: this.props.member.membership != 'ban',
|
||||
onFinished: (proceed, reason) => {
|
||||
|
@ -283,8 +284,8 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Ban error: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to ban user",
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to ban user"),
|
||||
});
|
||||
}
|
||||
).finally(()=>{
|
||||
|
@ -333,8 +334,8 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
}, function(err) {
|
||||
console.error("Mute error: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to mute user",
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to mute user"),
|
||||
});
|
||||
}
|
||||
).finally(()=>{
|
||||
|
@ -376,14 +377,14 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "This action cannot be performed by a guest user. Please register to be able to do this."
|
||||
title: _t("Please Register"),
|
||||
description: _t("This action cannot be performed by a guest user. Please register to be able to do this") + ".",
|
||||
});
|
||||
} else {
|
||||
console.error("Toggle moderator error:" + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to toggle moderator status",
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to toggle moderator status"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -403,8 +404,8 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to change power level " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to change power level",
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to change power level"),
|
||||
});
|
||||
}
|
||||
).finally(()=>{
|
||||
|
@ -432,13 +433,13 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
if (parseInt(myPower) === parseInt(powerLevel)) {
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning",
|
||||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
You will not be able to undo this change as you are promoting the user to have the same power level as yourself.<br/>
|
||||
Are you sure?
|
||||
{ _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself") }.<br/>
|
||||
{ _t("Are you sure?") }
|
||||
</div>,
|
||||
button: "Continue",
|
||||
button: _t("Continue"),
|
||||
onFinished: function(confirmed) {
|
||||
if (confirmed) {
|
||||
self._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
||||
|
@ -581,9 +582,9 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
// still loading
|
||||
devComponents = <Spinner />;
|
||||
} else if (devices === null) {
|
||||
devComponents = "Unable to load device list";
|
||||
devComponents = _t("Unable to load device list");
|
||||
} else if (devices.length === 0) {
|
||||
devComponents = "No devices with registered encryption keys";
|
||||
devComponents = _t("No devices with registered encryption keys");
|
||||
} else {
|
||||
devComponents = [];
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
|
@ -595,7 +596,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h3>Devices</h3>
|
||||
<h3>{ _t("Devices") }</h3>
|
||||
<div className="mx_MemberInfo_devices">
|
||||
{devComponents}
|
||||
</div>
|
||||
|
@ -644,11 +645,11 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
<div className="mx_RoomTile_avatar">
|
||||
<img src="img/create-big.svg" width="26" height="26" />
|
||||
</div>
|
||||
<div className={labelClasses}><i>Start new chat</i></div>
|
||||
<div className={labelClasses}><i>{ _t("Start a chat") }</i></div>
|
||||
</AccessibleButton>;
|
||||
|
||||
startChat = <div>
|
||||
<h3>Direct chats</h3>
|
||||
<h3>{ _t("Direct chats") }</h3>
|
||||
{tiles}
|
||||
{startNewChat}
|
||||
</div>;
|
||||
|
@ -661,7 +662,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
|
||||
if (this.state.can.kick) {
|
||||
const membership = this.props.member.membership;
|
||||
const kickLabel = membership === "invite" ? "Disinvite" : "Kick";
|
||||
const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
|
||||
kickButton = (
|
||||
<AccessibleButton className="mx_MemberInfo_field"
|
||||
onClick={this.onKick}>
|
||||
|
@ -670,9 +671,9 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
);
|
||||
}
|
||||
if (this.state.can.ban) {
|
||||
let label = 'Ban';
|
||||
let label = _t("Ban");
|
||||
if (this.props.member.membership == 'ban') {
|
||||
label = 'Unban';
|
||||
label = _t("Unban");
|
||||
}
|
||||
banButton = (
|
||||
<AccessibleButton className="mx_MemberInfo_field"
|
||||
|
@ -682,7 +683,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
);
|
||||
}
|
||||
if (this.state.can.mute) {
|
||||
const muteLabel = this.state.muted ? "Unmute" : "Mute";
|
||||
const muteLabel = this.state.muted ? _t("Unmute") : _t("Mute");
|
||||
muteButton = (
|
||||
<AccessibleButton className="mx_MemberInfo_field"
|
||||
onClick={this.onMuteToggle}>
|
||||
|
@ -691,7 +692,7 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
);
|
||||
}
|
||||
if (this.state.can.toggleMod) {
|
||||
var giveOpLabel = this.state.isTargetMod ? "Revoke Moderator" : "Make Moderator";
|
||||
var giveOpLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
|
||||
giveModButton = <AccessibleButton className="mx_MemberInfo_field" onClick={this.onModToggle}>
|
||||
{giveOpLabel}
|
||||
</AccessibleButton>;
|
||||
|
@ -717,8 +718,16 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
|
||||
const memberName = this.props.member.name;
|
||||
|
||||
if (this.props.member.user) {
|
||||
var presenceState = this.props.member.user.presence;
|
||||
var presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
|
||||
var presenceLastTs = this.props.member.user.lastPresenceTs;
|
||||
var presenceCurrentlyActive = this.props.member.user.currentlyActive;
|
||||
}
|
||||
|
||||
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||
var PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||
var PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
return (
|
||||
<div className="mx_MemberInfo">
|
||||
|
@ -734,7 +743,12 @@ module.exports = WithMatrixClient(React.createClass({
|
|||
{ this.props.member.userId }
|
||||
</div>
|
||||
<div className="mx_MemberInfo_profileField">
|
||||
Level: <b><PowerSelector controlled={true} value={ parseInt(this.props.member.powerLevel) } disabled={ !this.state.can.modifyLevel } onChange={ this.onPowerChange }/></b>
|
||||
{ _t("Level") }: <b><PowerSelector controlled={true} value={ parseInt(this.props.member.powerLevel) } disabled={ !this.state.can.modifyLevel } onChange={ this.onPowerChange }/></b>
|
||||
</div>
|
||||
<div className="mx_MemberInfo_profileField">
|
||||
<PresenceLabel activeAgo={ presenceLastActiveAgo }
|
||||
currentlyActive={ presenceCurrentlyActive }
|
||||
presenceState={ presenceState } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
var React = require('react');
|
||||
import { _t } from '../../../languageHandler';
|
||||
var classNames = require('classnames');
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var q = require('q');
|
||||
|
@ -207,7 +208,9 @@ module.exports = React.createClass({
|
|||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||
var EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
var text = "and " + overflowCount + " other" + (overflowCount > 1 ? "s" : "") + "...";
|
||||
var text = (overflowCount > 1)
|
||||
? _t("and %(overflowCount)s others...", { overflowCount: overflowCount })
|
||||
: _t("and one other...");
|
||||
return (
|
||||
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
|
||||
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
|
||||
|
@ -363,8 +366,8 @@ module.exports = React.createClass({
|
|||
var inputBox = (
|
||||
<form autoComplete="off">
|
||||
<input className="mx_MemberList_query" id="mx_MemberList_query" type="text"
|
||||
onChange={this.onSearchQueryChanged} value={this.state.searchQuery}
|
||||
placeholder="Filter room members" />
|
||||
onChange={this.onSearchQueryChanged} value={this.state.searchQuery}
|
||||
placeholder={ _t('Filter room members') } />
|
||||
</form>
|
||||
);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
var React = require('react');
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
var CallHandler = require('../../../CallHandler');
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
var Modal = require('../../../Modal');
|
||||
|
@ -33,6 +33,7 @@ export default class MessageComposer extends React.Component {
|
|||
this.onHangupClick = this.onHangupClick.bind(this);
|
||||
this.onUploadClick = this.onUploadClick.bind(this);
|
||||
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
|
||||
this.uploadFiles = this.uploadFiles.bind(this);
|
||||
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
|
||||
this.onInputContentChanged = this.onInputContentChanged.bind(this);
|
||||
this.onUpArrow = this.onUpArrow.bind(this);
|
||||
|
@ -43,6 +44,7 @@ export default class MessageComposer extends React.Component {
|
|||
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
|
||||
this.onInputStateChanged = this.onInputStateChanged.bind(this);
|
||||
this.onEvent = this.onEvent.bind(this);
|
||||
this.onPageUnload = this.onPageUnload.bind(this);
|
||||
|
||||
this.state = {
|
||||
autocompleteQuery: '',
|
||||
|
@ -50,7 +52,7 @@ export default class MessageComposer extends React.Component {
|
|||
inputState: {
|
||||
style: [],
|
||||
blockType: null,
|
||||
isRichtextEnabled: UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', true),
|
||||
isRichtextEnabled: UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false),
|
||||
wordCount: 0,
|
||||
},
|
||||
showFormatting: UserSettingsStore.getSyncedSetting('MessageComposer.showFormatting', false),
|
||||
|
@ -64,12 +66,21 @@ export default class MessageComposer extends React.Component {
|
|||
// marked as encrypted.
|
||||
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
|
||||
MatrixClientPeg.get().on("event", this.onEvent);
|
||||
|
||||
window.addEventListener('beforeunload', this.onPageUnload);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("event", this.onEvent);
|
||||
}
|
||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||
}
|
||||
|
||||
onPageUnload(event) {
|
||||
if (this.messageComposerInput) {
|
||||
this.messageComposerInput.sentHistory.saveLastTextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
onEvent(event) {
|
||||
|
@ -82,8 +93,8 @@ export default class MessageComposer extends React.Component {
|
|||
if (MatrixClientPeg.get().isGuest()) {
|
||||
let NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guest users can't upload files. Please register to upload.",
|
||||
title: _t('Please Register'),
|
||||
description: _t('Guest users can\'t upload files. Please register to upload') + '.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -91,10 +102,11 @@ export default class MessageComposer extends React.Component {
|
|||
this.refs.uploadInput.click();
|
||||
}
|
||||
|
||||
onUploadFileSelected(files, isPasted) {
|
||||
if (!isPasted)
|
||||
files = files.target.files;
|
||||
onUploadFileSelected(files) {
|
||||
this.uploadFiles(files.target.files);
|
||||
}
|
||||
|
||||
uploadFiles(files) {
|
||||
let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
let TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
|
||||
|
@ -106,10 +118,10 @@ export default class MessageComposer extends React.Component {
|
|||
}
|
||||
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Upload Files",
|
||||
title: _t('Upload Files'),
|
||||
description: (
|
||||
<div>
|
||||
<p>Are you sure you want upload the following files?</p>
|
||||
<p>{ _t('Are you sure you want upload the following files?') }</p>
|
||||
<ul style={{listStyle: 'none', textAlign: 'left'}}>
|
||||
{fileList}
|
||||
</ul>
|
||||
|
@ -228,11 +240,11 @@ export default class MessageComposer extends React.Component {
|
|||
if (roomIsEncrypted) {
|
||||
// FIXME: show a /!\ if there are untrusted devices in the room...
|
||||
e2eImg = 'img/e2e-verified.svg';
|
||||
e2eTitle = 'Encrypted room';
|
||||
e2eTitle = _t('Encrypted room');
|
||||
e2eClass = 'mx_MessageComposer_e2eIcon';
|
||||
} else {
|
||||
e2eImg = 'img/e2e-unencrypted.svg';
|
||||
e2eTitle = 'Unencrypted room';
|
||||
e2eTitle = _t('Unencrypted room');
|
||||
e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
|
||||
}
|
||||
|
||||
|
@ -245,16 +257,16 @@ export default class MessageComposer extends React.Component {
|
|||
if (this.props.callState && this.props.callState !== 'ended') {
|
||||
hangupButton =
|
||||
<div key="controls_hangup" className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
||||
<img src="img/hangup.svg" alt="Hangup" title="Hangup" width="25" height="26"/>
|
||||
<img src="img/hangup.svg" alt={ _t('Hangup') } title={ _t('Hangup') } width="25" height="26"/>
|
||||
</div>;
|
||||
}
|
||||
else {
|
||||
callButton =
|
||||
<div key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title="Voice call">
|
||||
<div key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title={ _t('Voice call') }>
|
||||
<TintableSvg src="img/icon-call.svg" width="35" height="35"/>
|
||||
</div>;
|
||||
videoCallButton =
|
||||
<div key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title="Video call">
|
||||
<div key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title={ _t('Video call') }>
|
||||
<TintableSvg src="img/icons-video.svg" width="35" height="35"/>
|
||||
</div>;
|
||||
}
|
||||
|
@ -268,7 +280,7 @@ export default class MessageComposer extends React.Component {
|
|||
// complex because of conference calls.
|
||||
var uploadButton = (
|
||||
<div key="controls_upload" className="mx_MessageComposer_upload"
|
||||
onClick={this.onUploadClick} title="Upload file">
|
||||
onClick={this.onUploadClick} title={ _t('Upload file') }>
|
||||
<TintableSvg src="img/icons-upload.svg" width="35" height="35"/>
|
||||
<input ref="uploadInput" type="file"
|
||||
style={uploadInputStyle}
|
||||
|
@ -288,7 +300,7 @@ export default class MessageComposer extends React.Component {
|
|||
);
|
||||
|
||||
const placeholderText = roomIsEncrypted ?
|
||||
"Send an encrypted message…" : "Send a message (unencrypted)…";
|
||||
_t('Send an encrypted message') + '…' : _t('Send a message (unencrypted)') + '…';
|
||||
|
||||
controls.push(
|
||||
<MessageComposerInput
|
||||
|
@ -300,7 +312,7 @@ export default class MessageComposer extends React.Component {
|
|||
tryComplete={this._tryComplete}
|
||||
onUpArrow={this.onUpArrow}
|
||||
onDownArrow={this.onDownArrow}
|
||||
onUploadFileSelected={this.onUploadFileSelected}
|
||||
onFilesPasted={this.uploadFiles}
|
||||
tabComplete={this.props.tabComplete} // used for old messagecomposerinput/tabcomplete
|
||||
onContentChanged={this.onInputContentChanged}
|
||||
onInputStateChanged={this.onInputStateChanged} />,
|
||||
|
@ -313,7 +325,7 @@ export default class MessageComposer extends React.Component {
|
|||
} else {
|
||||
controls.push(
|
||||
<div key="controls_error" className="mx_MessageComposer_noperm_error">
|
||||
You do not have permission to post to this room
|
||||
{ _t('You do not have permission to post to this room') }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -342,7 +354,7 @@ export default class MessageComposer extends React.Component {
|
|||
mx_filterFlipColor: true,
|
||||
});
|
||||
return <img className={className}
|
||||
title={name}
|
||||
title={ _t(name) }
|
||||
onMouseDown={disabled ? null : onFormatButtonClicked}
|
||||
key={name}
|
||||
src={`img/button-text-${name}${suffix}.svg`}
|
||||
|
@ -362,11 +374,11 @@ export default class MessageComposer extends React.Component {
|
|||
<div className="mx_MessageComposer_formatbar" style={this.state.showFormatting ? {} : {display: 'none'}}>
|
||||
{formatButtons}
|
||||
<div style={{flex: 1}}></div>
|
||||
<img title={`Turn Markdown ${this.state.inputState.isRichtextEnabled ? 'on' : 'off'}`}
|
||||
<img title={ this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off") }
|
||||
onMouseDown={this.onToggleMarkdownClicked}
|
||||
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
|
||||
src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />
|
||||
<img title="Hide Text Formatting Toolbar"
|
||||
<img title={ _t("Hide Text Formatting Toolbar") }
|
||||
onClick={this.onToggleFormattingClicked}
|
||||
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
|
||||
src="img/icon-text-cancel.svg" />
|
||||
|
|
|
@ -30,6 +30,7 @@ import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
|
|||
import SlashCommands from '../../../SlashCommands';
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
import dis from '../../../dispatcher';
|
||||
import KeyCode from '../../../KeyCode';
|
||||
|
@ -84,7 +85,6 @@ export default class MessageComposerInput extends React.Component {
|
|||
this.onAction = this.onAction.bind(this);
|
||||
this.handleReturn = this.handleReturn.bind(this);
|
||||
this.handleKeyCommand = this.handleKeyCommand.bind(this);
|
||||
this.handlePastedFiles = this.handlePastedFiles.bind(this);
|
||||
this.onEditorContentChanged = this.onEditorContentChanged.bind(this);
|
||||
this.setEditorState = this.setEditorState.bind(this);
|
||||
this.onUpArrow = this.onUpArrow.bind(this);
|
||||
|
@ -94,7 +94,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
this.setDisplayedCompletion = this.setDisplayedCompletion.bind(this);
|
||||
this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this);
|
||||
|
||||
const isRichtextEnabled = UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', true);
|
||||
const isRichtextEnabled = UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false);
|
||||
|
||||
this.state = {
|
||||
// whether we're in rich text or markdown mode
|
||||
|
@ -477,10 +477,6 @@ export default class MessageComposerInput extends React.Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
handlePastedFiles(files) {
|
||||
this.props.onUploadFileSelected(files, true);
|
||||
}
|
||||
|
||||
handleReturn(ev) {
|
||||
if (ev.shiftKey) {
|
||||
this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState));
|
||||
|
@ -509,8 +505,8 @@ export default class MessageComposerInput extends React.Component {
|
|||
console.error("Command failure: %s", err);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Server error",
|
||||
description: ((err && err.message) ? err.message : "Server unavailable, overloaded, or something else went wrong."),
|
||||
title: _t("Server error"),
|
||||
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -518,8 +514,8 @@ export default class MessageComposerInput extends React.Component {
|
|||
console.error(cmd.error);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Command error",
|
||||
description: cmd.error
|
||||
title: _t("Command error"),
|
||||
description: cmd.error,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
@ -542,9 +538,9 @@ export default class MessageComposerInput extends React.Component {
|
|||
let sendTextFn = this.client.sendTextMessage;
|
||||
|
||||
if (contentText.startsWith('/me')) {
|
||||
contentText = contentText.replace('/me ', '');
|
||||
contentText = contentText.substring(4);
|
||||
// bit of a hack, but the alternative would be quite complicated
|
||||
if (contentHTML) contentHTML = contentHTML.replace('/me ', '');
|
||||
if (contentHTML) contentHTML = contentHTML.replace(/\/me ?/, '');
|
||||
sendHtmlFn = this.client.sendHtmlEmote;
|
||||
sendTextFn = this.client.sendEmoteMessage;
|
||||
}
|
||||
|
@ -724,7 +720,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
<div className={className}>
|
||||
<img className="mx_MessageComposer_input_markdownIndicator mx_filterFlipColor"
|
||||
onMouseDown={this.onMarkdownToggleClicked}
|
||||
title={`Markdown is ${this.state.isRichtextEnabled ? 'disabled' : 'enabled'}`}
|
||||
title={ this.state.isRichtextEnabled ? _t("Markdown is disabled") : _t("Markdown is enabled")}
|
||||
src={`img/button-md-${!this.state.isRichtextEnabled}.png`} />
|
||||
<Editor ref="editor"
|
||||
placeholder={this.props.placeholder}
|
||||
|
@ -734,7 +730,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
keyBindingFn={MessageComposerInput.getKeyBinding}
|
||||
handleKeyCommand={this.handleKeyCommand}
|
||||
handleReturn={this.handleReturn}
|
||||
handlePastedFiles={this.handlePastedFiles}
|
||||
handlePastedFiles={this.props.onFilesPasted}
|
||||
stripPastedStyles={!this.state.isRichtextEnabled}
|
||||
onTab={this.onTab}
|
||||
onUpArrow={this.onUpArrow}
|
||||
|
@ -764,7 +760,7 @@ MessageComposerInput.propTypes = {
|
|||
|
||||
onDownArrow: React.PropTypes.func,
|
||||
|
||||
onUploadFileSelected: React.PropTypes.func,
|
||||
onFilesPasted: React.PropTypes.func,
|
||||
|
||||
// attempts to confirm currently selected completion, returns whether actually confirmed
|
||||
tryComplete: React.PropTypes.func,
|
||||
|
|
|
@ -20,6 +20,7 @@ var SlashCommands = require("../../../SlashCommands");
|
|||
var Modal = require("../../../Modal");
|
||||
var MemberEntry = require("../../../TabCompleteEntries").MemberEntry;
|
||||
var sdk = require('../../../index');
|
||||
import { _t } from '../../../languageHandler';
|
||||
import UserSettingsStore from "../../../UserSettingsStore";
|
||||
|
||||
var dis = require("../../../dispatcher");
|
||||
|
@ -69,6 +70,9 @@ export default React.createClass({
|
|||
|
||||
// The text to use a placeholder in the input box
|
||||
placeholder: React.PropTypes.string.isRequired,
|
||||
|
||||
// callback to handle files pasted into the composer
|
||||
onFilesPasted: React.PropTypes.func,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -291,8 +295,8 @@ export default React.createClass({
|
|||
else {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Unknown command",
|
||||
description: "Usage: /markdown on|off"
|
||||
title: _t("Unknown command"),
|
||||
description: _t("Usage") + ": /markdown on|off",
|
||||
});
|
||||
}
|
||||
return;
|
||||
|
@ -311,8 +315,8 @@ export default React.createClass({
|
|||
console.error("Command failure: %s", err);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Server error",
|
||||
description: ((err && err.message) ? err.message : "Server unavailable, overloaded, or something else went wrong."),
|
||||
title: _t("Server error"),
|
||||
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -320,8 +324,8 @@ export default React.createClass({
|
|||
console.error(cmd.error);
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Command error",
|
||||
description: cmd.error
|
||||
title: _t("Command error"),
|
||||
description: cmd.error,
|
||||
});
|
||||
}
|
||||
return;
|
||||
|
@ -439,10 +443,27 @@ export default React.createClass({
|
|||
this.refs.textarea.focus();
|
||||
},
|
||||
|
||||
_onPaste: function(ev) {
|
||||
const items = ev.clipboardData.items;
|
||||
const files = [];
|
||||
for (const item of items) {
|
||||
if (item.kind === 'file') {
|
||||
files.push(item.getAsFile());
|
||||
}
|
||||
}
|
||||
if (files.length && this.props.onFilesPasted) {
|
||||
this.props.onFilesPasted(files);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MessageComposer_input" onClick={ this.onInputClick }>
|
||||
<textarea autoFocus ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder={this.props.placeholder} />
|
||||
<textarea autoFocus ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder={this.props.placeholder}
|
||||
onPaste={this._onPaste}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ var sdk = require('../../../index');
|
|||
var Velociraptor = require('../../../Velociraptor');
|
||||
require('../../../VelocityBounce');
|
||||
|
||||
import DateUtils from '../../../DateUtils';
|
||||
|
||||
var bounce = false;
|
||||
try {
|
||||
if (global.localStorage) {
|
||||
|
@ -63,9 +65,6 @@ module.exports = React.createClass({
|
|||
|
||||
// Timestamp when the receipt was read
|
||||
timestamp: React.PropTypes.number,
|
||||
|
||||
// True to show the full date/time rather than just the time
|
||||
showFullTimestamp: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -170,16 +169,8 @@ module.exports = React.createClass({
|
|||
|
||||
let title;
|
||||
if (this.props.timestamp) {
|
||||
const prefix = "Seen by " + this.props.member.userId + " at ";
|
||||
let ts = new Date(this.props.timestamp);
|
||||
if (this.props.showFullTimestamp) {
|
||||
// "15/12/2016, 7:05:45 PM (@alice:matrix.org)"
|
||||
title = prefix + ts.toLocaleString();
|
||||
}
|
||||
else {
|
||||
// "7:05:45 PM (@alice:matrix.org)"
|
||||
title = prefix + ts.toLocaleTimeString();
|
||||
}
|
||||
title = "Seen by " + this.props.member.userId + " at " +
|
||||
DateUtils.formatDate(new Date(this.props.timestamp));
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -17,7 +17,9 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var sdk = require('../../../index');
|
||||
import { _t } from '../../../languageHandler';
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
var Modal = require("../../../Modal");
|
||||
var dis = require("../../../dispatcher");
|
||||
|
@ -39,6 +41,7 @@ module.exports = React.createClass({
|
|||
oobData: React.PropTypes.object,
|
||||
editing: React.PropTypes.bool,
|
||||
saving: React.PropTypes.bool,
|
||||
inRoom: React.PropTypes.bool,
|
||||
collapsedRhs: React.PropTypes.bool,
|
||||
onSettingsClick: React.PropTypes.func,
|
||||
onSaveClick: React.PropTypes.func,
|
||||
|
@ -49,7 +52,7 @@ module.exports = React.createClass({
|
|||
getDefaultProps: function() {
|
||||
return {
|
||||
editing: false,
|
||||
onSettingsClick: function() {},
|
||||
inRoom: false,
|
||||
onSaveClick: function() {},
|
||||
};
|
||||
},
|
||||
|
@ -117,8 +120,8 @@ module.exports = React.createClass({
|
|||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to set avatar: " + errMsg);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to set avatar.",
|
||||
title: _t("Error"),
|
||||
description: _t("Failed to set avatar."),
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
@ -186,6 +189,9 @@ module.exports = React.createClass({
|
|||
);
|
||||
|
||||
save_button = <AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save</AccessibleButton>;
|
||||
}
|
||||
|
||||
if (this.props.onCancelClick) {
|
||||
cancel_button = <CancelButton onClick={this.props.onCancelClick}/>;
|
||||
}
|
||||
|
||||
|
@ -203,7 +209,7 @@ module.exports = React.createClass({
|
|||
// don't display the search count until the search completes and
|
||||
// gives us a valid (possibly zero) searchCount.
|
||||
if (this.props.searchInfo && this.props.searchInfo.searchCount !== undefined && this.props.searchInfo.searchCount !== null) {
|
||||
searchStatus = <div className="mx_RoomHeader_searchStatus"> (~{ this.props.searchInfo.searchCount } results)</div>;
|
||||
searchStatus = <div className="mx_RoomHeader_searchStatus"> { _t("(~%(searchCount)s results)", { searchCount: this.props.searchInfo.searchCount }) }</div>;
|
||||
}
|
||||
|
||||
// XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
|
||||
|
@ -218,17 +224,17 @@ module.exports = React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
var roomName = 'Join Room';
|
||||
var roomName = _t("Join Room");
|
||||
if (this.props.oobData && this.props.oobData.name) {
|
||||
roomName = this.props.oobData.name;
|
||||
} else if (this.props.room) {
|
||||
roomName = this.props.room.name;
|
||||
}
|
||||
|
||||
|
||||
const emojiTextClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint });
|
||||
name =
|
||||
<div className="mx_RoomHeader_name" onClick={this.props.onSettingsClick}>
|
||||
<EmojiText element="div" className={ "mx_RoomHeader_nametext " + (settingsHint ? "mx_RoomHeader_settingsHint" : "") } title={ roomName }>{roomName}</EmojiText>
|
||||
<EmojiText element="div" className={emojiTextClasses} title={roomName}>{ roomName }</EmojiText>
|
||||
{ searchStatus }
|
||||
</div>;
|
||||
}
|
||||
|
@ -259,7 +265,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomHeader_avatarPicker_edit">
|
||||
<label htmlFor="avatarInput" ref="file_label">
|
||||
<img src="img/camera.svg"
|
||||
alt="Upload avatar" title="Upload avatar"
|
||||
alt={ _t("Upload avatar") } title={ _t("Upload avatar") }
|
||||
width="17" height="15" />
|
||||
</label>
|
||||
<input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>
|
||||
|
@ -294,15 +300,23 @@ module.exports = React.createClass({
|
|||
var forget_button;
|
||||
if (this.props.onForgetClick) {
|
||||
forget_button =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onForgetClick} title="Forget room">
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onForgetClick} title={ _t("Forget room") }>
|
||||
<TintableSvg src="img/leave.svg" width="26" height="20"/>
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let search_button;
|
||||
if (this.props.onSearchClick && this.props.inRoom) {
|
||||
search_button =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title={ _t("Search") }>
|
||||
<TintableSvg src="img/icons-search.svg" width="35" height="35"/>
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
var rightPanel_buttons;
|
||||
if (this.props.collapsedRhs) {
|
||||
rightPanel_buttons =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShowRhsClick} title="Show panel">
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShowRhsClick} title={ _t('Show panel') }>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16"/>
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
@ -313,9 +327,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomHeader_rightRow">
|
||||
{ settings_button }
|
||||
{ forget_button }
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title="Search">
|
||||
<TintableSvg src="img/icons-search.svg" width="35" height="35"/>
|
||||
</AccessibleButton>
|
||||
{ search_button }
|
||||
{ rightPanel_buttons }
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -17,17 +17,18 @@ limitations under the License.
|
|||
'use strict';
|
||||
var React = require("react");
|
||||
var ReactDOM = require("react-dom");
|
||||
import { _t } from '../../../languageHandler';
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var CallHandler = require('../../../CallHandler');
|
||||
var RoomListSorter = require("../../../RoomListSorter");
|
||||
var Unread = require('../../../Unread');
|
||||
var dis = require("../../../dispatcher");
|
||||
var sdk = require('../../../index');
|
||||
var rate_limited_func = require('../../../ratelimitedfunc');
|
||||
var Rooms = require('../../../Rooms');
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
var Receipt = require('../../../utils/Receipt');
|
||||
var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
|
||||
|
||||
var HIDE_CONFERENCE_CHANS = true;
|
||||
|
||||
|
@ -37,19 +38,10 @@ module.exports = React.createClass({
|
|||
propTypes: {
|
||||
ConferenceHandler: React.PropTypes.any,
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
selectedRoom: React.PropTypes.string,
|
||||
currentRoom: React.PropTypes.string,
|
||||
searchFilter: React.PropTypes.string,
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
if (nextProps.collapsed !== this.props.collapsed) return true;
|
||||
if (nextProps.searchFilter !== this.props.searchFilter) return true;
|
||||
if (nextState.lists !== this.state.lists ||
|
||||
nextState.isLoadingLeftRooms !== this.state.isLoadingLeftRooms ||
|
||||
nextState.incomingCall !== this.state.incomingCall) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
isLoadingLeftRooms: false,
|
||||
|
@ -59,6 +51,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.mounted = false;
|
||||
|
||||
var cli = MatrixClientPeg.get();
|
||||
cli.on("Room", this.onRoom);
|
||||
cli.on("deleteRoom", this.onDeleteRoom);
|
||||
|
@ -66,46 +60,23 @@ module.exports = React.createClass({
|
|||
cli.on("Room.name", this.onRoomName);
|
||||
cli.on("Room.tags", this.onRoomTags);
|
||||
cli.on("Room.receipt", this.onRoomReceipt);
|
||||
cli.on("RoomState.members", this.onRoomStateMember);
|
||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||
cli.on("accountData", this.onAccountData);
|
||||
|
||||
// lookup for which lists a given roomId is currently in.
|
||||
this.listsForRoomId = {};
|
||||
|
||||
var s = this.getRoomLists();
|
||||
this.setState(s);
|
||||
|
||||
// order of the sublists
|
||||
//this.listOrder = [];
|
||||
|
||||
// loop count to stop a stack overflow if the user keeps waggling the
|
||||
// mouse for >30s in a row, or if running under mocha
|
||||
this._delayedRefreshRoomListLoopCount = 0
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
// Initialise the stickyHeaders when the component is created
|
||||
this._updateStickyHeaders(true);
|
||||
|
||||
this.mounted = true;
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
// short-circuit react when the room changes
|
||||
// to avoid rerendering all the sublists everywhere
|
||||
if (nextProps.selectedRoom !== this.props.selectedRoom) {
|
||||
if (this.props.selectedRoom) {
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.select", this.props.selectedRoom, {}
|
||||
);
|
||||
}
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.select", nextProps.selectedRoom, { selected: true }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
componentDidUpdate: function() {
|
||||
// Reinitialise the stickyHeaders when the component is updated
|
||||
this._updateStickyHeaders(true);
|
||||
this._repositionIncomingCallBox(undefined, false);
|
||||
|
@ -131,29 +102,17 @@ module.exports = React.createClass({
|
|||
}
|
||||
break;
|
||||
case 'on_room_read':
|
||||
// poke the right RoomTile to refresh, using the constantTimeDispatcher
|
||||
// to avoid each and every RoomTile registering to the 'on_room_read' event
|
||||
// XXX: if we like the constantTimeDispatcher we might want to dispatch
|
||||
// directly from TimelinePanel rather than needlessly bouncing via here.
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", payload.room.roomId, {}
|
||||
);
|
||||
|
||||
// also have to poke the right list(s)
|
||||
var lists = this.listsForRoomId[payload.room.roomId];
|
||||
if (lists) {
|
||||
lists.forEach(list=>{
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomSubList.refreshHeader", list, { room: payload.room }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Force an update because the notif count state is too deep to cause
|
||||
// an update. This forces the local echo of reading notifs to be
|
||||
// reflected by the RoomTiles.
|
||||
this.forceUpdate();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.mounted = false;
|
||||
|
||||
dis.unregister(this.dispatcherRef);
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
||||
|
@ -162,7 +121,7 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
||||
MatrixClientPeg.get().removeListener("Room.tags", this.onRoomTags);
|
||||
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
|
||||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
|
@ -171,14 +130,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onRoom: function(room) {
|
||||
// XXX: this happens rarely; ideally we should only update the correct
|
||||
// sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onDeleteRoom: function(roomId) {
|
||||
// XXX: this happens rarely; ideally we should only update the correct
|
||||
// sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
|
@ -201,10 +156,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_onMouseOver: function(ev) {
|
||||
this._lastMouseOverTs = Date.now();
|
||||
},
|
||||
|
||||
onSubListHeaderClick: function(isHidden, scrollToPosition) {
|
||||
// The scroll area has expanded or contracted, so re-calculate sticky headers positions
|
||||
this._updateStickyHeaders(true, scrollToPosition);
|
||||
|
@ -214,98 +165,41 @@ module.exports = React.createClass({
|
|||
if (toStartOfTimeline) return;
|
||||
if (!room) return;
|
||||
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
|
||||
|
||||
// rather than regenerate our full roomlists, which is very heavy, we poke the
|
||||
// correct sublists to just re-sort themselves. This isn't enormously reacty,
|
||||
// but is much faster than the default react reconciler, or having to do voodoo
|
||||
// with shouldComponentUpdate and a pleaseRefresh property or similar.
|
||||
var lists = this.listsForRoomId[room.roomId];
|
||||
if (lists) {
|
||||
lists.forEach(list=>{
|
||||
constantTimeDispatcher.dispatch("RoomSubList.sort", list, { room: room });
|
||||
});
|
||||
}
|
||||
|
||||
// we have to explicitly hit the roomtile which just changed
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", room.roomId, {}
|
||||
);
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomReceipt: function(receiptEvent, room) {
|
||||
// because if we read a notification, it will affect notification count
|
||||
// only bother updating if there's a receipt from us
|
||||
if (Receipt.findReadReceiptFromUserId(receiptEvent, MatrixClientPeg.get().credentials.userId)) {
|
||||
var lists = this.listsForRoomId[room.roomId];
|
||||
if (lists) {
|
||||
lists.forEach(list=>{
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomSubList.refreshHeader", list, { room: room }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// we have to explicitly hit the roomtile which just changed
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", room.roomId, {}
|
||||
);
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
},
|
||||
|
||||
onRoomName: function(room) {
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", room.roomId, {}
|
||||
);
|
||||
},
|
||||
|
||||
onRoomTags: function(event, room) {
|
||||
// XXX: this happens rarely; ideally we should only update the correct
|
||||
// sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomStateMember: function(ev, state, member) {
|
||||
if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId &&
|
||||
ev.getPrevContent() && ev.getPrevContent().membership === "invite")
|
||||
{
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
else {
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", member.roomId, {}
|
||||
);
|
||||
}
|
||||
onRoomTags: function(event, room) {
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomStateEvents: function(ev, state) {
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomMemberName: function(ev, member) {
|
||||
constantTimeDispatcher.dispatch(
|
||||
"RoomTile.refresh", member.roomId, {}
|
||||
);
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onAccountData: function(ev) {
|
||||
if (ev.getType() == 'm.direct') {
|
||||
// XXX: this happens rarely; ideally we should only update the correct
|
||||
// sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
else if (ev.getType() == 'm.push_rules') {
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
},
|
||||
|
||||
_delayedRefreshRoomList: new rate_limited_func(function() {
|
||||
// if the mouse has been moving over the RoomList in the last 500ms
|
||||
// then delay the refresh further to avoid bouncing around under the
|
||||
// cursor
|
||||
if (Date.now() - this._lastMouseOverTs > 500 || this._delayedRefreshRoomListLoopCount > 60) {
|
||||
this.refreshRoomList();
|
||||
this._delayedRefreshRoomListLoopCount = 0;
|
||||
}
|
||||
else {
|
||||
this._delayedRefreshRoomListLoopCount++;
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
this.refreshRoomList();
|
||||
}, 500),
|
||||
|
||||
refreshRoomList: function() {
|
||||
|
@ -313,12 +207,14 @@ module.exports = React.createClass({
|
|||
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
|
||||
// );
|
||||
|
||||
// TODO: ideally we'd calculate this once at start, and then maintain
|
||||
// any changes to it incrementally, updating the appropriate sublists
|
||||
// as needed.
|
||||
// Alternatively we'd do something magical with Immutable.js or similar.
|
||||
// TODO: rather than bluntly regenerating and re-sorting everything
|
||||
// every time we see any kind of room change from the JS SDK
|
||||
// we could do incremental updates on our copy of the state
|
||||
// based on the room which has actually changed. This would stop
|
||||
// us re-rendering all the sublists every time anything changes anywhere
|
||||
// in the state of the client.
|
||||
this.setState(this.getRoomLists());
|
||||
|
||||
|
||||
// this._lastRefreshRoomListTs = Date.now();
|
||||
},
|
||||
|
||||
|
@ -333,26 +229,18 @@ module.exports = React.createClass({
|
|||
s.lists["m.lowpriority"] = [];
|
||||
s.lists["im.vector.fake.archived"] = [];
|
||||
|
||||
this.listsForRoomId = {};
|
||||
var otherTagNames = {};
|
||||
|
||||
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
|
||||
|
||||
MatrixClientPeg.get().getRooms().forEach(function(room) {
|
||||
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||
if (!me) return;
|
||||
|
||||
|
||||
// console.log("room = " + room.name + ", me.membership = " + me.membership +
|
||||
// ", sender = " + me.events.member.getSender() +
|
||||
// ", target = " + me.events.member.getStateKey() +
|
||||
// ", prevMembership = " + me.events.member.getPrevContent().membership);
|
||||
|
||||
if (!self.listsForRoomId[room.roomId]) {
|
||||
self.listsForRoomId[room.roomId] = [];
|
||||
}
|
||||
|
||||
if (me.membership == "invite") {
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
|
||||
s.lists["im.vector.fake.invite"].push(room);
|
||||
}
|
||||
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
|
||||
|
@ -363,27 +251,23 @@ module.exports = React.createClass({
|
|||
{
|
||||
// Used to split rooms via tags
|
||||
var tagNames = Object.keys(room.tags);
|
||||
|
||||
if (tagNames.length) {
|
||||
for (var i = 0; i < tagNames.length; i++) {
|
||||
var tagName = tagNames[i];
|
||||
s.lists[tagName] = s.lists[tagName] || [];
|
||||
s.lists[tagName].push(room);
|
||||
self.listsForRoomId[room.roomId].push(tagName);
|
||||
otherTagNames[tagName] = 1;
|
||||
s.lists[tagNames[i]].push(room);
|
||||
}
|
||||
}
|
||||
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
|
||||
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
|
||||
s.lists["im.vector.fake.direct"].push(room);
|
||||
}
|
||||
else {
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
|
||||
s.lists["im.vector.fake.recent"].push(room);
|
||||
}
|
||||
}
|
||||
else if (me.membership === "leave") {
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
|
||||
s.lists["im.vector.fake.archived"].push(room);
|
||||
}
|
||||
else {
|
||||
|
@ -404,10 +288,8 @@ module.exports = React.createClass({
|
|||
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||
|
||||
if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
|
||||
s.lists["im.vector.fake.direct"].push(room);
|
||||
} else {
|
||||
self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
|
||||
s.lists["im.vector.fake.recent"].push(room);
|
||||
}
|
||||
}
|
||||
|
@ -424,8 +306,6 @@ module.exports = React.createClass({
|
|||
newMDirectEvent[otherPerson.userId] = roomList;
|
||||
}
|
||||
|
||||
console.warn("Resetting room DM state to be " + JSON.stringify(newMDirectEvent));
|
||||
|
||||
// if this fails, fine, we'll just do the same thing next time we get the room lists
|
||||
MatrixClientPeg.get().setAccountData('m.direct', newMDirectEvent).done();
|
||||
}
|
||||
|
@ -434,32 +314,19 @@ module.exports = React.createClass({
|
|||
|
||||
// we actually apply the sorting to this when receiving the prop in RoomSubLists.
|
||||
|
||||
// we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down
|
||||
/*
|
||||
this.listOrder = [
|
||||
"im.vector.fake.invite",
|
||||
"m.favourite",
|
||||
"im.vector.fake.recent",
|
||||
"im.vector.fake.direct",
|
||||
Object.keys(otherTagNames).filter(tagName=>{
|
||||
return (!tagName.match(/^m\.(favourite|lowpriority)$/));
|
||||
}).sort(),
|
||||
"m.lowpriority",
|
||||
"im.vector.fake.archived"
|
||||
];
|
||||
*/
|
||||
|
||||
return s;
|
||||
},
|
||||
|
||||
_getScrollNode: function() {
|
||||
if (!this.mounted) return null;
|
||||
var panel = ReactDOM.findDOMNode(this);
|
||||
if (!panel) return null;
|
||||
|
||||
// empirically, if we have gm-prevented for some reason, the scroll node
|
||||
// is still the 3rd child (i.e. the view child). This looks to be due
|
||||
// to vdh's improved resize updater logic...?
|
||||
return panel.children[2]; // XXX: Fragile!
|
||||
if (panel.classList.contains('gm-prevented')) {
|
||||
return panel;
|
||||
} else {
|
||||
return panel.children[2]; // XXX: Fragile!
|
||||
}
|
||||
},
|
||||
|
||||
_whenScrolling: function(e) {
|
||||
|
@ -479,6 +346,7 @@ module.exports = React.createClass({
|
|||
var incomingCallBox = document.getElementById("incomingCallBox");
|
||||
if (incomingCallBox && incomingCallBox.parentElement) {
|
||||
var scrollArea = this._getScrollNode();
|
||||
if (!scrollArea) return;
|
||||
// Use the offset of the top of the scroll area from the window
|
||||
// as this is used to calculate the CSS fixed top position for the stickies
|
||||
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
|
||||
|
@ -502,10 +370,11 @@ module.exports = React.createClass({
|
|||
// properly through React
|
||||
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
|
||||
var scrollArea = this._getScrollNode();
|
||||
if (!scrollArea) return;
|
||||
// Use the offset of the top of the scroll area from the window
|
||||
// as this is used to calculate the CSS fixed top position for the stickies
|
||||
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
|
||||
// Use the offset of the top of the component from the window
|
||||
// Use the offset of the top of the componet from the window
|
||||
// as this is used to calculate the CSS fixed top position for the stickies
|
||||
var scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
|
||||
|
||||
|
@ -602,75 +471,72 @@ module.exports = React.createClass({
|
|||
render: function() {
|
||||
var RoomSubList = sdk.getComponent('structures.RoomSubList');
|
||||
var self = this;
|
||||
|
||||
return (
|
||||
<GeminiScrollbar className="mx_RoomList_scrollbar"
|
||||
autoshow={true} onScroll={ self._whenScrolling } onResize={ self._whenScrolling } ref="gemscroll">
|
||||
<div className="mx_RoomList" onMouseOver={ this._onMouseOver }>
|
||||
autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll">
|
||||
<div className="mx_RoomList">
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.invite'] }
|
||||
label="Invites"
|
||||
tagName="im.vector.fake.invite"
|
||||
label={ _t('Invites') }
|
||||
editable={ false }
|
||||
order="recent"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||
label="Favourites"
|
||||
label={ _t('Favourites') }
|
||||
tagName="m.favourite"
|
||||
verb="favourite"
|
||||
verb={ _t('to favourite') }
|
||||
editable={ true }
|
||||
order="manual"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.direct'] }
|
||||
label="People"
|
||||
label={ _t('People') }
|
||||
tagName="im.vector.fake.direct"
|
||||
verb="tag direct chat"
|
||||
verb={ _t('to tag direct chat') }
|
||||
editable={ true }
|
||||
order="recent"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
alwaysShowHeader={ true }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
|
||||
label="Rooms"
|
||||
tagName="im.vector.fake.recent"
|
||||
label={ _t('Rooms') }
|
||||
editable={ true }
|
||||
verb="restore"
|
||||
verb={ _t('to restore') }
|
||||
order="recent"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
|
||||
{ Object.keys(self.state.lists).sort().map(function(tagName) {
|
||||
{ Object.keys(self.state.lists).map(function(tagName) {
|
||||
if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
|
||||
return <RoomSubList list={ self.state.lists[tagName] }
|
||||
key={ tagName }
|
||||
label={ tagName }
|
||||
tagName={ tagName }
|
||||
verb={ "tag as " + tagName }
|
||||
verb={ _t('to tag as %(tagName)s', {tagName: tagName}) }
|
||||
editable={ true }
|
||||
order="manual"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />;
|
||||
|
@ -679,25 +545,24 @@ module.exports = React.createClass({
|
|||
}) }
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.lowpriority'] }
|
||||
label="Low priority"
|
||||
label={ _t('Low priority') }
|
||||
tagName="m.lowpriority"
|
||||
verb="demote"
|
||||
verb={ _t('to demote') }
|
||||
editable={ true }
|
||||
order="recent"
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
|
||||
label="Historical"
|
||||
tagName="im.vector.fake.archived"
|
||||
label={ _t('Historical') }
|
||||
editable={ false }
|
||||
order="recent"
|
||||
collapsed={ self.props.collapsed }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed }
|
||||
alwaysShowHeader={ true }
|
||||
startAsHidden={ true }
|
||||
showSpinner={ self.state.isLoadingLeftRooms }
|
||||
|
|
|
@ -21,6 +21,8 @@ var React = require('react');
|
|||
var sdk = require('../../../index');
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomPreviewBar',
|
||||
|
||||
|
@ -47,7 +49,7 @@ module.exports = React.createClass({
|
|||
// 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).
|
||||
roomAlias: React.PropTypes.object,
|
||||
roomAlias: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -84,7 +86,7 @@ module.exports = React.createClass({
|
|||
_roomNameElement: function(fallback) {
|
||||
fallback = fallback || 'a room';
|
||||
const name = this.props.room ? this.props.room.name : (this.props.room_alias || "");
|
||||
return name ? <b>{ name }</b> : fallback;
|
||||
return name ? name : fallback;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -128,13 +130,14 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
}
|
||||
}
|
||||
// TODO: find a way to respect HTML in counterpart!
|
||||
joinBlock = (
|
||||
<div>
|
||||
<div className="mx_RoomPreviewBar_invite_text">
|
||||
You have been invited to join this room by <b>{ this.props.inviterName }</b>
|
||||
{ _t('You have been invited to join this room by %(inviterName)s', {inviterName: this.props.inviterName}) }
|
||||
</div>
|
||||
<div className="mx_RoomPreviewBar_join_text">
|
||||
Would you like to <a onClick={ this.props.onJoinClick }>accept</a> or <a onClick={ this.props.onRejectClick }>decline</a> this invitation?
|
||||
{ _t('Would you like to') } <a onClick={ this.props.onJoinClick }>{ _t('accept') }</a> { _t('or') } <a onClick={ this.props.onRejectClick }>{ _t('decline') }</a> { _t('this invitation?') }
|
||||
</div>
|
||||
{emailMatchBlock}
|
||||
</div>
|
||||
|
@ -186,8 +189,8 @@ module.exports = React.createClass({
|
|||
joinBlock = (
|
||||
<div>
|
||||
<div className="mx_RoomPreviewBar_join_text">
|
||||
You are trying to access { name }.<br/>
|
||||
<a onClick={ this.props.onJoinClick }><b>Click here</b></a> to join the discussion!
|
||||
{ _t('You are trying to access %(roomName)s', {roomName: name}) }.<br/>
|
||||
<a onClick={ this.props.onJoinClick }><b>{ _t('Click here') }</b></a> { _t('to join the discussion') }!
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -196,7 +199,7 @@ module.exports = React.createClass({
|
|||
if (this.props.canPreview) {
|
||||
previewBlock = (
|
||||
<div className="mx_RoomPreviewBar_preview_text">
|
||||
This is a preview of this room. Room interactions have been disabled.
|
||||
{ _t('This is a preview of this room. Room interactions have been disabled') }.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
|
||||
import q from 'q';
|
||||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import sdk from '../../../index';
|
||||
|
@ -56,8 +57,8 @@ const BannedUser = React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to unban: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to unban",
|
||||
title: _t('Error'),
|
||||
description: _t('Failed to unban'),
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
@ -70,7 +71,7 @@ const BannedUser = React.createClass({
|
|||
<AccessibleButton className="mx_RoomSettings_unbanButton"
|
||||
onClick={this._onUnbanClick}
|
||||
>
|
||||
Unban
|
||||
{ _t('Unban') }
|
||||
</AccessibleButton>
|
||||
{this.props.member.userId}
|
||||
</li>
|
||||
|
@ -400,13 +401,13 @@ module.exports = React.createClass({
|
|||
var value = ev.target.value;
|
||||
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Privacy warning",
|
||||
title: _t('Privacy warning'),
|
||||
description:
|
||||
<div>
|
||||
Changes to who can read history will only apply to future messages in this room.<br/>
|
||||
The visibility of existing history will be unchanged.
|
||||
{ _t('Changes to who can read history will only apply to future messages in this room') }.<br/>
|
||||
{ _t('The visibility of existing history will be unchanged') }.
|
||||
</div>,
|
||||
button: "Continue",
|
||||
button: _t('Continue'),
|
||||
onFinished: function(confirmed) {
|
||||
if (confirmed) {
|
||||
self.setState({
|
||||
|
@ -523,11 +524,11 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
}, function(err) {
|
||||
var errCode = err.errcode || "unknown error code";
|
||||
var errCode = err.errcode || _t('unknown error code');
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: `Failed to forget room (${errCode})`
|
||||
title: _t('Error'),
|
||||
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -537,14 +538,14 @@ module.exports = React.createClass({
|
|||
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning!",
|
||||
title: _t('Warning!'),
|
||||
description: (
|
||||
<div>
|
||||
<p>End-to-end encryption is in beta and may not be reliable.</p>
|
||||
<p>You should <b>not</b> yet trust it to secure data.</p>
|
||||
<p>Devices will <b>not</b> yet be able to decrypt history from before they joined the room.</p>
|
||||
<p>Once encryption is enabled for a room it <b>cannot</b> be turned off again (for now).</p>
|
||||
<p>Encrypted messages will not be visible on clients that do not yet implement encryption.</p>
|
||||
<p>{ _t('End-to-end encryption is in beta and may not be reliable') }.</p>
|
||||
<p>{ _t('You should not yet trust it to secure data') }.</p>
|
||||
<p>{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.</p>
|
||||
<p>{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.</p>
|
||||
<p>{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.</p>
|
||||
</div>
|
||||
),
|
||||
onFinished: confirm=>{
|
||||
|
@ -572,7 +573,7 @@ module.exports = React.createClass({
|
|||
<input type="checkbox" ref="blacklistUnverified"
|
||||
defaultChecked={ isGlobalBlacklistUnverified || isRoomBlacklistUnverified }
|
||||
disabled={ isGlobalBlacklistUnverified || (this.refs.encrypt && !this.refs.encrypt.checked) }/>
|
||||
Never send encrypted messages to unverified devices in this room from this device.
|
||||
{ _t('Never send encrypted messages to unverified devices in this room from this device') }.
|
||||
</label>;
|
||||
|
||||
if (!isEncrypted &&
|
||||
|
@ -582,7 +583,7 @@ module.exports = React.createClass({
|
|||
<label>
|
||||
<input type="checkbox" ref="encrypt" onClick={ this.onEnableEncryptionClick }/>
|
||||
<img className="mx_RoomSettings_e2eIcon" src="img/e2e-unencrypted.svg" width="12" height="12" />
|
||||
Enable encryption (warning: cannot be disabled again!)
|
||||
{ _t('Enable encryption') } { _t('(warning: cannot be disabled again!)') }
|
||||
</label>
|
||||
{ settings }
|
||||
</div>
|
||||
|
@ -596,7 +597,7 @@ module.exports = React.createClass({
|
|||
? <img className="mx_RoomSettings_e2eIcon" src="img/e2e-verified.svg" width="10" height="12" />
|
||||
: <img className="mx_RoomSettings_e2eIcon" src="img/e2e-unencrypted.svg" width="12" height="12" />
|
||||
}
|
||||
Encryption is { isEncrypted ? "" : "not " } enabled in this room.
|
||||
{ isEncrypted ? "Encryption is enabled in this room" : "Encryption is not enabled in this room" }.
|
||||
</label>
|
||||
{ settings }
|
||||
</div>
|
||||
|
@ -647,12 +648,12 @@ module.exports = React.createClass({
|
|||
if (Object.keys(user_levels).length) {
|
||||
userLevelsSection =
|
||||
<div>
|
||||
<h3>Privileged Users</h3>
|
||||
<h3>{ _t('Privileged Users') }</h3>
|
||||
<ul className="mx_RoomSettings_userLevels">
|
||||
{Object.keys(user_levels).map(function(user, i) {
|
||||
return (
|
||||
<li className="mx_RoomSettings_userLevel" key={user}>
|
||||
{ user } is a <PowerSelector value={ user_levels[user] } disabled={true}/>
|
||||
{ user } { _t('is a') } <PowerSelector value={ user_levels[user] } disabled={true}/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
@ -660,7 +661,7 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
}
|
||||
else {
|
||||
userLevelsSection = <div>No users have specific privileges in this room.</div>;
|
||||
userLevelsSection = <div>{ _t('No users have specific privileges in this room') }.</div>;
|
||||
}
|
||||
|
||||
var banned = this.props.room.getMembersWithMembership("ban");
|
||||
|
@ -668,7 +669,7 @@ module.exports = React.createClass({
|
|||
if (banned.length) {
|
||||
bannedUsersSection =
|
||||
<div>
|
||||
<h3>Banned users</h3>
|
||||
<h3>{ _t('Banned users') }</h3>
|
||||
<ul className="mx_RoomSettings_banned">
|
||||
{banned.map(function(member) {
|
||||
return (
|
||||
|
@ -683,7 +684,7 @@ module.exports = React.createClass({
|
|||
if (this._yankValueFromEvent("m.room.create", "m.federate") === false) {
|
||||
unfederatableSection = (
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
Ths room is not accessible by remote Matrix servers.
|
||||
{ _t('This room is not accessible by remote Matrix servers') }.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -694,14 +695,14 @@ module.exports = React.createClass({
|
|||
if (myMember.membership === "join") {
|
||||
leaveButton = (
|
||||
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onLeaveClick }>
|
||||
Leave room
|
||||
{ _t('Leave room') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
else if (myMember.membership === "leave") {
|
||||
leaveButton = (
|
||||
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onForgetClick }>
|
||||
Forget room
|
||||
{ _t('Forget room') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
@ -711,8 +712,8 @@ module.exports = React.createClass({
|
|||
// TODO: support editing custom user_levels
|
||||
|
||||
var tags = [
|
||||
{ name: "m.favourite", label: "Favourite", ref: "tag_favourite" },
|
||||
{ name: "m.lowpriority", label: "Low priority", ref: "tag_lowpriority" },
|
||||
{ name: "m.favourite", label: _t('Favourite'), ref: "tag_favourite" },
|
||||
{ name: "m.lowpriority", label: _t('Low priority'), ref: "tag_lowpriority" },
|
||||
];
|
||||
|
||||
Object.keys(this.state.tags).sort().forEach(function(tagName) {
|
||||
|
@ -753,7 +754,7 @@ module.exports = React.createClass({
|
|||
if (this.state.join_rule === "public" && aliasCount == 0) {
|
||||
addressWarning =
|
||||
<div className="mx_RoomSettings_warning">
|
||||
To link to a room it must have <a href="#addresses">an address</a>.
|
||||
{ _t('To link to a room it must have') } <a href="#addresses"> { _t('an address') }</a>.
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
@ -761,10 +762,10 @@ module.exports = React.createClass({
|
|||
if (this.state.join_rule !== "public" && this.state.guest_access === "forbidden") {
|
||||
inviteGuestWarning =
|
||||
<div className="mx_RoomSettings_warning">
|
||||
Guests cannot join this room even if explicitly invited. <a href="#" onClick={ (e) => {
|
||||
{ _t('Guests cannot join this room even if explicitly invited.') } <a href="#" onClick={ (e) => {
|
||||
this.setState({ join_rule: "invite", guest_access: "can_join" });
|
||||
e.preventDefault();
|
||||
}}>Click here to fix</a>.
|
||||
}}>{ _t('Click here to fix') }</a>.
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
@ -776,7 +777,7 @@ module.exports = React.createClass({
|
|||
console.error(this.state.scalar_error);
|
||||
integrationsError = (
|
||||
<span className="mx_RoomSettings_integrationsButton_errorPopup">
|
||||
Could not connect to the integration server
|
||||
{ _t('Could not connect to the integration server') }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -784,7 +785,7 @@ module.exports = React.createClass({
|
|||
if (this.scalarClient.hasCredentials()) {
|
||||
integrationsButton = (
|
||||
<div className="mx_RoomSettings_integrationsButton" onClick={ this.onManageIntegrations }>
|
||||
Manage Integrations
|
||||
{ _t('Manage Integrations') }
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.scalar_error) {
|
||||
|
@ -797,7 +798,7 @@ module.exports = React.createClass({
|
|||
} else {
|
||||
integrationsButton = (
|
||||
<div className="mx_RoomSettings_integrationsButton" style={{opacity: 0.5}}>
|
||||
Manage Integrations
|
||||
{ _t('Manage Integrations') }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -813,28 +814,28 @@ module.exports = React.createClass({
|
|||
|
||||
<div className="mx_RoomSettings_toggles">
|
||||
<div className="mx_RoomSettings_settings">
|
||||
<h3>Who can access this room?</h3>
|
||||
<h3>{ _t('Who can access this room?') }</h3>
|
||||
{ inviteGuestWarning }
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="invite_only"
|
||||
disabled={ !this.mayChangeRoomAccess() }
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={this.state.join_rule !== "public"}/>
|
||||
Only people who have been invited
|
||||
{ _t('Only people who have been invited') }
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="public_no_guests"
|
||||
disabled={ !this.mayChangeRoomAccess() }
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={this.state.join_rule === "public" && this.state.guest_access !== "can_join"}/>
|
||||
Anyone who knows the room's link, apart from guests
|
||||
{ _t('Anyone who knows the room\'s link, apart from guests') }
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="roomVis" value="public_with_guests"
|
||||
disabled={ !this.mayChangeRoomAccess() }
|
||||
onChange={this._onRoomAccessRadioToggle}
|
||||
checked={this.state.join_rule === "public" && this.state.guest_access === "can_join"}/>
|
||||
Anyone who knows the room's link, including guests
|
||||
{ _t('Anyone who knows the room\'s link, including guests') }
|
||||
</label>
|
||||
{ addressWarning }
|
||||
<br/>
|
||||
|
@ -847,7 +848,7 @@ module.exports = React.createClass({
|
|||
</label>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
<h3>Who can read history?</h3>
|
||||
<h3>{ _t('Who can read history?') }</h3>
|
||||
<label>
|
||||
<input type="radio" name="historyVis" value="world_readable"
|
||||
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
|
||||
|
@ -860,28 +861,28 @@ module.exports = React.createClass({
|
|||
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
|
||||
checked={historyVisibility === "shared"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
Members only (since the point in time of selecting this option)
|
||||
{ _t('Members only') } ({ _t('since the point in time of selecting this option') })
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="historyVis" value="invited"
|
||||
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
|
||||
checked={historyVisibility === "invited"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
Members only (since they were invited)
|
||||
{ _t('Members only') } ({ _t('since they were invited') })
|
||||
</label>
|
||||
<label >
|
||||
<input type="radio" name="historyVis" value="joined"
|
||||
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
|
||||
checked={historyVisibility === "joined"}
|
||||
onChange={this._onHistoryRadioToggle} />
|
||||
Members only (since they joined)
|
||||
{ _t('Members only') } ({ _t('since they joined') })
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<h3>Room Colour</h3>
|
||||
<h3>{ _t('Room Colour') }</h3>
|
||||
<ColorSettings ref="color_settings" room={this.props.room} />
|
||||
</div>
|
||||
|
||||
|
@ -899,41 +900,41 @@ module.exports = React.createClass({
|
|||
|
||||
<UrlPreviewSettings ref="url_preview_settings" room={this.props.room} />
|
||||
|
||||
<h3>Permissions</h3>
|
||||
<h3>{ _t('Permissions') }</h3>
|
||||
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">The default role for new room members is </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('The default role for new room members is') } </span>
|
||||
<PowerSelector ref="users_default" value={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < default_user_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To send messages, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="events_default" value={send_level} controlled={false} disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To invite users into the room, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="invite" value={invite_level} controlled={false} disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To configure the room, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="state_default" value={state_level} controlled={false} disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To kick users, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="kick" value={kick_level} controlled={false} disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To ban users, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="ban" value={ban_level} controlled={false} disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">To remove messages, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages') }, { _t('you must be a') } </span>
|
||||
<PowerSelector ref="redact" value={redact_level} controlled={false} disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
|
||||
{Object.keys(events_levels).map(function(event_type, i) {
|
||||
return (
|
||||
<div className="mx_RoomSettings_powerLevel" key={event_type}>
|
||||
<span className="mx_RoomSettings_powerLevelKey">To send events of type <code>{ event_type }</code>, you must be a </span>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send events of type') } <code>{ event_type }</code>, { _t('you must be a') } </span>
|
||||
<PowerSelector value={ events_levels[event_type] } controlled={false} disabled={true} onChange={self.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
);
|
||||
|
@ -946,9 +947,9 @@ module.exports = React.createClass({
|
|||
|
||||
{ bannedUsersSection }
|
||||
|
||||
<h3>Advanced</h3>
|
||||
<h3>{ _t('Advanced') }</h3>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
This room's internal ID is <code>{ this.props.room.roomId }</code>
|
||||
{ _t('This room\'s internal ID is') } <code>{ this.props.room.roomId }</code>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -27,8 +27,6 @@ var RoomNotifs = require('../../../RoomNotifs');
|
|||
var FormattingUtils = require('../../../utils/FormattingUtils');
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
||||
var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
|
||||
var Unread = require('../../../Unread');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
|
@ -38,10 +36,12 @@ module.exports = React.createClass({
|
|||
connectDropTarget: React.PropTypes.func,
|
||||
onClick: React.PropTypes.func,
|
||||
isDragging: React.PropTypes.bool,
|
||||
selectedRoom: React.PropTypes.string,
|
||||
|
||||
room: React.PropTypes.object.isRequired,
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
selected: React.PropTypes.bool.isRequired,
|
||||
unread: React.PropTypes.bool.isRequired,
|
||||
highlight: React.PropTypes.bool.isRequired,
|
||||
isInvite: React.PropTypes.bool.isRequired,
|
||||
incomingCall: React.PropTypes.object,
|
||||
},
|
||||
|
@ -54,11 +54,10 @@ module.exports = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return({
|
||||
hover: false,
|
||||
badgeHover: false,
|
||||
hover : false,
|
||||
badgeHover : false,
|
||||
menuDisplayed: false,
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
selected: this.props.room ? (this.props.selectedRoom === this.props.room.roomId) : false,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -80,32 +79,23 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onAccountData: function(accountDataEvent) {
|
||||
if (accountDataEvent.getType() == 'm.push_rules') {
|
||||
this.setState({
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
constantTimeDispatcher.register("RoomTile.refresh", this.props.room.roomId, this.onRefresh);
|
||||
constantTimeDispatcher.register("RoomTile.select", this.props.room.roomId, this.onSelect);
|
||||
this.onRefresh();
|
||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
constantTimeDispatcher.unregister("RoomTile.refresh", this.props.room.roomId, this.onRefresh);
|
||||
constantTimeDispatcher.unregister("RoomTile.select", this.props.room.roomId, this.onSelect);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
this.onRefresh();
|
||||
},
|
||||
|
||||
onRefresh: function(params) {
|
||||
this.setState({
|
||||
unread: Unread.doesRoomHaveUnreadMessages(this.props.room),
|
||||
highlight: this.props.room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite,
|
||||
});
|
||||
},
|
||||
|
||||
onSelect: function(params) {
|
||||
this.setState({
|
||||
selected: params.selected,
|
||||
});
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
|
@ -179,13 +169,13 @@ module.exports = React.createClass({
|
|||
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
||||
|
||||
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
|
||||
const mentionBadges = this.state.highlight && this._shouldShowMentionBadge();
|
||||
const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
|
||||
const badges = notifBadges || mentionBadges;
|
||||
|
||||
var classes = classNames({
|
||||
'mx_RoomTile': true,
|
||||
'mx_RoomTile_selected': this.state.selected,
|
||||
'mx_RoomTile_unread': this.state.unread,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_unreadNotify': notifBadges,
|
||||
'mx_RoomTile_highlight': mentionBadges,
|
||||
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
||||
|
@ -231,7 +221,7 @@ module.exports = React.createClass({
|
|||
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
|
||||
});
|
||||
|
||||
if (this.state.selected) {
|
||||
if (this.props.selected) {
|
||||
let nameSelected = <EmojiText>{name}</EmojiText>;
|
||||
|
||||
label = <div title={ name } className={ nameClasses }>{ nameSelected }</div>;
|
||||
|
@ -265,8 +255,7 @@ module.exports = React.createClass({
|
|||
|
||||
let ret = (
|
||||
<div> { /* Only native elements can be wrapped in a DnD object. */}
|
||||
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick}
|
||||
onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<div className={avatarClasses}>
|
||||
<div className="mx_RoomTile_avatar_container">
|
||||
<RoomAvatar room={this.props.room} width={24} height={24} />
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
}
|
||||
return (
|
||||
<li data-scroll-token={eventId+"+"+j}>
|
||||
<li data-scroll-tokens={eventId+"+"+j}>
|
||||
{ret}
|
||||
</li>);
|
||||
},
|
||||
|
|
|
@ -17,6 +17,7 @@ var React = require('react');
|
|||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var Modal = require("../../../Modal");
|
||||
var sdk = require("../../../index");
|
||||
import { _t } from '../../../languageHandler';
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
|
||||
// A list capable of displaying entities which conform to the SearchableEntity
|
||||
|
@ -25,7 +26,6 @@ var SearchableEntityList = React.createClass({
|
|||
displayName: 'SearchableEntityList',
|
||||
|
||||
propTypes: {
|
||||
searchPlaceholderText: React.PropTypes.string,
|
||||
emptyQueryShowsAll: React.PropTypes.bool,
|
||||
showInputBox: React.PropTypes.bool,
|
||||
onQueryChanged: React.PropTypes.func, // fn(inputText)
|
||||
|
@ -37,7 +37,6 @@ var SearchableEntityList = React.createClass({
|
|||
getDefaultProps: function() {
|
||||
return {
|
||||
showInputBox: true,
|
||||
searchPlaceholderText: "Search",
|
||||
entities: [],
|
||||
emptyQueryShowsAll: false,
|
||||
onSubmit: function() {},
|
||||
|
@ -118,7 +117,9 @@ var SearchableEntityList = React.createClass({
|
|||
_createOverflowEntity: function(overflowCount, totalCount) {
|
||||
var EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
var text = "and " + overflowCount + " other" + (overflowCount > 1 ? "s" : "") + "...";
|
||||
var text = (overflowCount > 1)
|
||||
? _t("and %(overflowCount)s others...", { overflowCount: overflowCount })
|
||||
: _t("and one other...");
|
||||
return (
|
||||
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
|
||||
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
|
||||
|
@ -137,7 +138,7 @@ var SearchableEntityList = React.createClass({
|
|||
onChange={this.onQueryChanged} value={this.state.query}
|
||||
onFocus= {() => { this.setState({ focused: true }); }}
|
||||
onBlur= {() => { this.setState({ focused: false }); }}
|
||||
placeholder={this.props.searchPlaceholderText} />
|
||||
placeholder={ _t("Search") } />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import dis from '../../../dispatcher';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import sdk from '../../../index';
|
||||
|
||||
// cancel button which is shared between room header and simple room header
|
||||
export function CancelButton(props) {
|
||||
|
@ -45,6 +46,9 @@ export default React.createClass({
|
|||
|
||||
// is the RightPanel collapsed?
|
||||
collapsedRhs: React.PropTypes.bool,
|
||||
|
||||
// `src` to a TintableSvg. Optional.
|
||||
icon: React.PropTypes.string,
|
||||
},
|
||||
|
||||
onShowRhsClick: function(ev) {
|
||||
|
@ -53,9 +57,17 @@ export default React.createClass({
|
|||
|
||||
render: function() {
|
||||
let cancelButton;
|
||||
let icon;
|
||||
if (this.props.onCancelClick) {
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
||||
}
|
||||
if (this.props.icon) {
|
||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
icon = <TintableSvg
|
||||
className="mx_RoomHeader_icon" src={this.props.icon}
|
||||
width="25" height="25"
|
||||
/>;
|
||||
}
|
||||
|
||||
let showRhsButton;
|
||||
/* // don't bother cluttering things up with this for now.
|
||||
|
@ -73,6 +85,7 @@ export default React.createClass({
|
|||
<div className="mx_RoomHeader" >
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
<div className="mx_RoomHeader_simpleHeader">
|
||||
{ icon }
|
||||
{ this.props.title }
|
||||
{ showRhsButton }
|
||||
{ cancelButton }
|
||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
import { _t } from '../../../languageHandler';
|
||||
var sdk = require('../../../index');
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -34,11 +35,11 @@ module.exports = React.createClass({
|
|||
<div className="mx_TopUnreadMessagesBar_scrollUp"
|
||||
onClick={this.props.onScrollUpClick}>
|
||||
<img src="img/scrollto.svg" width="24" height="24"
|
||||
alt="Scroll to unread messages"
|
||||
title="Scroll to unread messages"/>
|
||||
alt={ _t('Scroll to unread messages') }
|
||||
title={ _t('Scroll to unread messages') }/>
|
||||
Jump to first unread message.
|
||||
</div>
|
||||
<img className="mx_TopUnreadMessagesBar_close"
|
||||
<img className="mx_TopUnreadMessagesBar_close mx_filterFlipColor"
|
||||
src="img/cancel.svg" width="18" height="18"
|
||||
alt="Close" title="Close"
|
||||
onClick={this.props.onCloseClick} />
|
||||
|
@ -46,4 +47,3 @@ module.exports = React.createClass({
|
|||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
import sdk from '../../../index';
|
||||
import AddThreepid from '../../../AddThreepid';
|
||||
import WithMatrixClient from '../../../wrappers/WithMatrixClient';
|
||||
import Modal from '../../../Modal';
|
||||
|
||||
|
||||
export default WithMatrixClient(React.createClass({
|
||||
displayName: 'AddPhoneNumber',
|
||||
|
||||
|
@ -50,7 +50,7 @@ export default WithMatrixClient(React.createClass({
|
|||
},
|
||||
|
||||
_onPhoneCountryChange: function(phoneCountry) {
|
||||
this.setState({ phoneCountry: phoneCountry });
|
||||
this.setState({ phoneCountry: phoneCountry.iso2 });
|
||||
},
|
||||
|
||||
_onPhoneNumberChange: function(ev) {
|
||||
|
@ -83,7 +83,7 @@ export default WithMatrixClient(React.createClass({
|
|||
console.error("Unable to add phone number: " + err);
|
||||
let msg = err.message;
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
title: _t("Error"),
|
||||
description: msg,
|
||||
});
|
||||
}).finally(() => {
|
||||
|
@ -98,20 +98,19 @@ export default WithMatrixClient(React.createClass({
|
|||
if (this._unmounted) return;
|
||||
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
||||
let msgElements = [
|
||||
<div key="_static" >A text message has been sent to +{msisdn}.
|
||||
Please enter the verification code it contains</div>
|
||||
<div key="_static" >{ _t("A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", { msisdn: msisdn} ) }</div>
|
||||
];
|
||||
if (err) {
|
||||
let msg = err.error;
|
||||
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
|
||||
msg = "Incorrect verification code";
|
||||
msg = _t("Incorrect verification code");
|
||||
}
|
||||
msgElements.push(<div key="_error" className="error">{msg}</div>);
|
||||
}
|
||||
Modal.createDialog(TextInputDialog, {
|
||||
title: "Enter Code",
|
||||
title: _t("Enter Code"),
|
||||
description: <div>{msgElements}</div>,
|
||||
button: "Submit",
|
||||
button: _t("Submit"),
|
||||
onFinished: (should_verify, token) => {
|
||||
if (!should_verify) {
|
||||
this._addThreepid = null;
|
||||
|
@ -147,17 +146,19 @@ export default WithMatrixClient(React.createClass({
|
|||
return (
|
||||
<form className="mx_UserSettings_profileTableRow" onSubmit={this._onAddMsisdnSubmit}>
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label>{_t('Phone')}</label>
|
||||
</div>
|
||||
<div className="mx_UserSettings_profileInputCell">
|
||||
<div className="mx_Login_phoneSection">
|
||||
<div className="mx_UserSettings_phoneSection">
|
||||
<CountryDropdown onOptionChange={this._onPhoneCountryChange}
|
||||
className="mx_Login_phoneCountry"
|
||||
className="mx_UserSettings_phoneCountry"
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
/>
|
||||
<input type="text"
|
||||
ref={this._collectAddMsisdnInput}
|
||||
className="mx_UserSettings_phoneNumberField"
|
||||
placeholder="Add phone number"
|
||||
placeholder={ _t('Add phone number') }
|
||||
value={this.state.phoneNumber}
|
||||
onChange={this._onPhoneNumberChange}
|
||||
/>
|
||||
|
|
|
@ -21,6 +21,7 @@ var MatrixClientPeg = require("../../../MatrixClientPeg");
|
|||
var Modal = require("../../../Modal");
|
||||
var sdk = require("../../../index");
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangePassword',
|
||||
|
@ -47,11 +48,11 @@ module.exports = React.createClass({
|
|||
onCheckPassword: function(oldPass, newPass, confirmPass) {
|
||||
if (newPass !== confirmPass) {
|
||||
return {
|
||||
error: "New passwords don't match."
|
||||
error: _t("New passwords don't match") + "."
|
||||
};
|
||||
} else if (!newPass || newPass.length === 0) {
|
||||
return {
|
||||
error: "Passwords can't be empty"
|
||||
error: _t("Passwords can't be empty")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -69,19 +70,21 @@ module.exports = React.createClass({
|
|||
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Warning",
|
||||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
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 <a href="https://github.com/vector-im/riot-web/issues/2671">will be improved</a>.
|
||||
{ _t(
|
||||
'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.'
|
||||
) } (<a href="https://github.com/vector-im/riot-web/issues/2671">https://github.com/vector-im/riot-web/issues/2671</a>)
|
||||
</div>,
|
||||
button: "Continue",
|
||||
button: _t("Continue"),
|
||||
extraButtons: [
|
||||
<button className="mx_Dialog_primary"
|
||||
onClick={this._onExportE2eKeysClicked}>
|
||||
Export E2E room keys
|
||||
{ _t('Export E2E room keys') }
|
||||
</button>
|
||||
],
|
||||
onFinished: (confirmed) => {
|
||||
|
@ -150,7 +153,7 @@ module.exports = React.createClass({
|
|||
<div className={this.props.className}>
|
||||
<div className={rowClassName}>
|
||||
<div className={rowLabelClassName}>
|
||||
<label htmlFor="passwordold">Current password</label>
|
||||
<label htmlFor="passwordold">{ _t('Current password') }</label>
|
||||
</div>
|
||||
<div className={rowInputClassName}>
|
||||
<input id="passwordold" type="password" ref="old_input" />
|
||||
|
@ -158,7 +161,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
<div className={rowClassName}>
|
||||
<div className={rowLabelClassName}>
|
||||
<label htmlFor="password1">New password</label>
|
||||
<label htmlFor="password1">{ _t('New password') }</label>
|
||||
</div>
|
||||
<div className={rowInputClassName}>
|
||||
<input id="password1" type="password" ref="new_input" />
|
||||
|
@ -166,7 +169,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
<div className={rowClassName}>
|
||||
<div className={rowLabelClassName}>
|
||||
<label htmlFor="password2">Confirm password</label>
|
||||
<label htmlFor="password2">{ _t('Confirm password') }</label>
|
||||
</div>
|
||||
<div className={rowInputClassName}>
|
||||
<input id="password2" type="password" ref="confirm_input" />
|
||||
|
@ -174,7 +177,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
<AccessibleButton className={buttonClassName}
|
||||
onClick={this.onClickChange}>
|
||||
Change Password
|
||||
{ _t('Change Password') }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import Modal from '../../../Modal';
|
||||
import DateUtils from '../../../DateUtils';
|
||||
|
@ -48,7 +49,7 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
display_name: value,
|
||||
}).catch((e) => {
|
||||
console.error("Error setting device display name", e);
|
||||
throw new Error("Failed to set display name");
|
||||
throw new Error(_t("Failed to set display name"));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,6 +72,7 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
var InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
|
||||
|
||||
Modal.createDialog(InteractiveAuthDialog, {
|
||||
title: _t("Authentication"),
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
authData: error.data,
|
||||
makeRequest: this._makeDeleteRequest,
|
||||
|
@ -84,7 +86,7 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
if (this._unmounted) { return; }
|
||||
this.setState({
|
||||
deleting: false,
|
||||
deleteError: "Failed to delete device",
|
||||
deleteError: _t("Failed to delete device"),
|
||||
});
|
||||
}).done();
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
deleteButton = (
|
||||
<div className="mx_textButton"
|
||||
onClick={this._onDeleteClick}>
|
||||
Delete
|
||||
{ _t("Delete") }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||
var Modal = require('./Modal');
|
||||
var sdk = require('./index');
|
||||
import { _t } from './languageHandler';
|
||||
var dis = require("./dispatcher");
|
||||
var Rooms = require("./Rooms");
|
||||
|
||||
|
@ -43,8 +44,8 @@ function createRoom(opts) {
|
|||
if (client.isGuest()) {
|
||||
setTimeout(()=>{
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guest users can't create new rooms. Please register to create room and start a chat."
|
||||
title: _t('Please Register'),
|
||||
description: _t('Guest users can\'t create new rooms. Please register to create room and start a chat') + '.'
|
||||
});
|
||||
}, 0);
|
||||
return q(null);
|
||||
|
@ -104,8 +105,8 @@ function createRoom(opts) {
|
|||
}, function(err) {
|
||||
console.error("Failed to create room " + roomId + " " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failure to create room",
|
||||
description: "Server may be unavailable, overloaded, or you hit a bug.",
|
||||
title: _t("Failure to create room"),
|
||||
description: _t("Server may be unavailable, overloaded, or you hit a bug") + ".",
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -16,13 +16,13 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
var flux = require("flux");
|
||||
const flux = require("flux");
|
||||
|
||||
class MatrixDispatcher extends flux.Dispatcher {
|
||||
/**
|
||||
* @param {Object} payload Required. The payload to dispatch.
|
||||
* Must contain at least an 'action' key.
|
||||
* @param {boolean} sync Optional. Pass true to dispatch
|
||||
* @param {boolean=} sync Optional. Pass true to dispatch
|
||||
* synchronously. This is useful for anything triggering
|
||||
* an operation that the browser requires user interaction
|
||||
* for.
|
||||
|
|
1
src/i18n/strings/basefile.json
Normal file
1
src/i18n/strings/basefile.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
215
src/i18n/strings/da.json
Normal file
215
src/i18n/strings/da.json
Normal file
|
@ -0,0 +1,215 @@
|
|||
{
|
||||
"Filter room members": "Filter medlemmer",
|
||||
"You have no visible notifications": "Du har ingen synlige meddelelser",
|
||||
"Invites": "Invitationer",
|
||||
"Favourites": "Favoritter",
|
||||
"People": "Personilg chat",
|
||||
"Rooms": "Rum",
|
||||
"Low priority": "Lav prioritet",
|
||||
"Historical": "Historisk",
|
||||
"New passwords must match each other.": "Nye adgangskoder skal matche hinanden.",
|
||||
"A new password must be entered.": "Der skal indtastes en ny adgangskode.",
|
||||
"The email address linked to your account must be entered.": "Den emailadresse, der tilhører til din adgang, skal indtastes.",
|
||||
"Failed to send email: ": "Kunne ikke sende e-mail: ",
|
||||
"unknown device": "ukendt enhed",
|
||||
"NOT verified": "IKKE verificeret",
|
||||
"Blacklisted": "blokeret",
|
||||
"verified": "verificeret",
|
||||
"Name": "Navn",
|
||||
"Device ID": "Enheds-ID",
|
||||
"Verification": "Verifikation",
|
||||
"Ed25519 fingerprint": "Ed25519 fingerprint",
|
||||
"User ID": "Bruger ID",
|
||||
"Curve25519 identity key": "Curve25519 identitetsnøgle",
|
||||
"Claimed Ed25519 fingerprint key": "Påstået Ed25519 fingeraftryk nøgle",
|
||||
"none": "ingen",
|
||||
"Algorithm": "Algoritme",
|
||||
"unencrypted": "ukrypteret",
|
||||
"Decryption error": "Dekrypteringsfejl",
|
||||
"Session ID": "Sessions ID",
|
||||
"End-to-end encryption information": "End-to-end krypterings oplysninger",
|
||||
"Event information": "Hændelses information",
|
||||
"Sender device information": "Afsende enheds-oplysning",
|
||||
"Displays action": "Viser handling",
|
||||
"Bans user with given id": "Forbyder bruger med givet id",
|
||||
"Deops user with given id": "Fjerner OP af bruger med givet id",
|
||||
"Invites user with given id to current room": "Inviterer bruger med givet id til nuværende rum",
|
||||
"Joins room with given alias": "Kommer ind i rum med givet alias",
|
||||
"Kicks user with given id": "Smid bruger med givet id ud",
|
||||
"Changes your display nickname": "Ændrer dit visningsnavn",
|
||||
"Searches DuckDuckGo for results": "Søger DuckDuckGo for resultater",
|
||||
"Commands": "kommandoer",
|
||||
"Emoji": "Emoji",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar.": "Kan ikke oprette forbindelse til hjemmeserver via HTTP, når en HTTPS-URL er i din browserbjælke.",
|
||||
"Sorry, this homeserver is using a login which is not recognised ": "Beklager, denne homeerver bruger et login, der ikke kan genkendes ",
|
||||
"Login as guest": "Log ind som gæst",
|
||||
"Return to app": "Tilbage til app",
|
||||
"Sign in": "Log ind",
|
||||
"Create a new account": "Oprette en ny bruger",
|
||||
"Send an encrypted message": "Send en krypteret meddelelse",
|
||||
"Send a message (unencrypted)": "Send en meddelelse (ukrypteret)",
|
||||
"Warning!": "Advarsel!",
|
||||
"accept": "acceptere",
|
||||
"accepted an invitation": "Godkendt en invitation",
|
||||
"accepted the invitation for": "Accepteret invitationen til",
|
||||
"Account": "Konto",
|
||||
"Add email address": "Tilføj e-mail-adresse",
|
||||
"Add phone number": "Tilføj telefonnummer",
|
||||
"Admin": "Administrator",
|
||||
"Advanced": "Avanceret",
|
||||
"all room members": "Alle rum medlemmer",
|
||||
"all room members, from the point they are invited": "Alle rum medlemmer, siden invitations-tidspunkt",
|
||||
"all room members, from the point they joined": "Alle rum medlemmer, siden de deltog",
|
||||
"an address": "en adresse",
|
||||
"and": "og",
|
||||
"An email has been sent to": "En e-mail blev sendt til",
|
||||
"answered the call.": "svarede på kaldet",
|
||||
"anyone": "alle",
|
||||
"Anyone who knows the room's link, apart from guests": "Alle der kender link til rummet, bortset fra gæster",
|
||||
"Anyone who knows the room's link, including guests": "Alle der kender link til rummet, inklusiv gæster",
|
||||
"Are you sure you want to leave the room?": "Er du sikker på du vil forlade rummet?",
|
||||
"Are you sure you want to reject the invitation?": "Er du sikker på du vil afvise invitationen?",
|
||||
"Are you sure you want upload the following files?": "Er du sikker på du vil sende de følgende filer?",
|
||||
"banned": "bortvist",
|
||||
"Banned users": "Bortviste brugere",
|
||||
"Bug Report": "Fejlrapport",
|
||||
"Bulk Options": "Masseindstillinger",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your": "Kan ikke oprette forbindelse til homeserver - Kontroller din forbindelse og sørg for din ",
|
||||
"Can't load user settings": "Kan ikke indlæse brugerindstillinger",
|
||||
"changed avatar": "Ændret avatar",
|
||||
"changed name": "Ændret navn",
|
||||
"changed their display name from": "Ændret deres visningsnavn fra",
|
||||
"changed their profile picture": "Ændret deres profilbillede",
|
||||
"changed the power level of": "Ændret effektniveauet på",
|
||||
"changed the room name to": "Ændrede rumnavnet til",
|
||||
"changed the topic to": "Ændret emnet til",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Ændringer til hvem der kan læse historie gælder kun for fremtidige meddelelser i dette rum",
|
||||
"Clear Cache and Reload": "Ryd cache og genindlæs",
|
||||
"Clear Cache": "Ryd cache",
|
||||
"Click here": "Klik her",
|
||||
"Click here to fix": "Klik her for at rette",
|
||||
"*️⃣ Commands": "kommandoer",
|
||||
"Confirm your new password": "Bekræft din nye adgangskode",
|
||||
"Continue": "fortsætte",
|
||||
"Could not connect to the integration server": "Kunne ikke oprette forbindelse til integrationsserveren",
|
||||
"Create an account": "Opret en brugerkonto",
|
||||
"Create Room": "Opret rum",
|
||||
"Cryptography": "Kryptografi",
|
||||
"Deactivate Account": "Deaktiver brugerkonto",
|
||||
"Deactivate my account": "Deaktiver min brugerkonto",
|
||||
"decline": "nedgang",
|
||||
"Default": "Standard",
|
||||
"demote": "degradere",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Enhederne vil ikke være i stand til at dekryptere historikken fra, før de kom til rummet",
|
||||
"Direct Chat": "Personligt Chat",
|
||||
"Disable inline URL previews by default": "Deaktiver forrige weblinks forhåndsvisninger som standard",
|
||||
"Display name": "Visningsnavn",
|
||||
"Email Address": "Email adresse",
|
||||
"Email, name or matrix ID": "E-mail, navn eller matrix-id",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Krypterede meddelelser vil ikke være synlige på klienter, der endnu ikke implementerer kryptering",
|
||||
"Encrypted room": "Krypteret rummet",
|
||||
"Encryption is enabled in this room": "Kryptering er aktiveret i dette rum",
|
||||
"Encryption is not enabled in this room": "Kryptering er ikke aktiveret i dette rum",
|
||||
"ended the call.": "Afsluttede opkaldet.",
|
||||
"End-to-end encryption is in beta and may not be reliable": "End-to-end kryptering er i beta og kan ikke være pålidelig",
|
||||
"Error": "Fejl",
|
||||
"Export E2E room keys": "Eksporter E2E rum nøgler",
|
||||
"Failed to change password. Is your password correct?": "Kunne ikke ændre adgangskode. Er din adgangskode korrekt?",
|
||||
"Failed to forget room": "Kunne ikke glemme rummet",
|
||||
"Failed to leave room": "Kunne ikke forlade rum",
|
||||
"Failed to reject invitation": "Kunne ikke afvise invitationen",
|
||||
"Failed to send email": "Kunne ikke sende e-mail",
|
||||
"Failed to set avatar.": "Kunne ikke angive avatar.",
|
||||
"Failed to unban": "Var ikke i stand til at ophæve forbuddet",
|
||||
"Favourite": "Favorit",
|
||||
"Notifications": "Meddelser",
|
||||
"Please Register": "Vær venlig at registrere",
|
||||
"Remove": "Fjerne",
|
||||
"Settings": "Indstillinger",
|
||||
"unknown error code": "Ukendt fejlkode",
|
||||
"en": "Engelsk",
|
||||
"pt-br": "Brasiliansk Portugisisk",
|
||||
"de": "Tysk",
|
||||
"da": "Dansk",
|
||||
"ru": "Russisk",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s accepterede en invitation.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepteret invitationen til %(displayName)s.",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s og %(lastPerson)s er ved at skrive",
|
||||
"%(names)s and one other are typing": "%(names)s og den anden skriver",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s og %(count)s andre skriver",
|
||||
"%(senderName)s answered the call.": "%(senderName)s besvarede opkaldet.",
|
||||
"af": "Afrikaans",
|
||||
"ar-eg": "Arabisk (Egypten)",
|
||||
"ar-ma": "Arabisk (Marokko)",
|
||||
"ar-sa": "Arabisk (Saudiarabien",
|
||||
"ar-sy": "Arabisk (Syrien)",
|
||||
"be": "Hviderussisk",
|
||||
"bg": "Bulgarisk",
|
||||
"ca": "Katalansk",
|
||||
"cs": "Tjekkisk",
|
||||
"de-at": "Tysk (Østrig)",
|
||||
"de-ch": "Tysk (Schweitz)",
|
||||
"el": "Græsk",
|
||||
"en-au": "Engelsk (Australien)",
|
||||
"en-ca": "Engelsk (Canada)",
|
||||
"en-ie": "Engelsk (Irland)",
|
||||
"en-nz": "Engelsk (New Zealand)",
|
||||
"en-us": "Engelsk (USA)",
|
||||
"en-za": "Engelsk (Sydafrika)",
|
||||
"es-ar": "Spansk (Argentina)",
|
||||
"es-bo": "Spansk (Bolivia)",
|
||||
"es-cl": "Spansk (Chile)",
|
||||
"es-ec": "Spansk (Ecuador)",
|
||||
"es-hn": "Spansk (Honduras)",
|
||||
"es-mx": "Spansk (Mexico)",
|
||||
"es-ni": "Spansk (Nicaragua)",
|
||||
"es-py": "Spansk (Paraguay)",
|
||||
"es": "Spansk (Spanien)",
|
||||
"es-uy": "Spansk (Uruguay)",
|
||||
"es-ve": "Spansk (Venezuela)",
|
||||
"et": "Estonsk",
|
||||
"fa": "Farsi",
|
||||
"fi": "Finsk",
|
||||
"fr-be": "Fransk (Belgien)",
|
||||
"fr-ca": "Fransk (Canada)",
|
||||
"fr-ch": "Fransk (Schweitz)",
|
||||
"fr": "French",
|
||||
"ga": "Irsk",
|
||||
"he": "Hebræisk",
|
||||
"hi": "Hindi",
|
||||
"hr": "Kroatisk",
|
||||
"hu": "Ungarsk",
|
||||
"id": "Indonesisk",
|
||||
"is": "Islandsk",
|
||||
"it": "Italian",
|
||||
"ja": "Japansk",
|
||||
"ji": "Yiddish",
|
||||
"lt": "Littauisk",
|
||||
"lv": "Lettisk",
|
||||
"ms": "Malaysisk",
|
||||
"mt": "Maltesisk",
|
||||
"nl": "Dutch",
|
||||
"no": "Norsk",
|
||||
"pl": "Polsk",
|
||||
"pt": "Portuguese",
|
||||
"ro": "Rumænsk",
|
||||
"sb": "Sorbisk",
|
||||
"sk": "Slovakisk",
|
||||
"sl": "Slovensk",
|
||||
"sq": "Albansk",
|
||||
"sr": "Serbisk (Latin)",
|
||||
"sv": "Svensk",
|
||||
"th": "Thai",
|
||||
"tn": "Tswana",
|
||||
"tr": "Tyrkisk",
|
||||
"ts": "Tonga",
|
||||
"uk": "Ukrainsk",
|
||||
"ur": "Urdu",
|
||||
"ve": "Venda",
|
||||
"vi": "Vietnamesisk",
|
||||
"xh": "Xhosa",
|
||||
"zh-cn": "Kinesisk (Folkerepublikken Kina)",
|
||||
"zh-sg": "Kinesisk (Singapore)",
|
||||
"zh-tw": "Kinesisk (Taiwan)",
|
||||
"zu": "Zulu"
|
||||
}
|
730
src/i18n/strings/de_DE.json
Normal file
730
src/i18n/strings/de_DE.json
Normal file
|
@ -0,0 +1,730 @@
|
|||
{
|
||||
"Filter room members": "Raum Benutzer filtern",
|
||||
"You have no visible notifications": "Du hast keine sichtbaren Benachrichtigungen",
|
||||
"Invites": "Einladungen",
|
||||
"Favourites": "Favoriten",
|
||||
"People": "Direkt-Chats",
|
||||
"Rooms": "Räume",
|
||||
"Low priority": "Niedrige Priorität",
|
||||
"Historical": "Historisch",
|
||||
"New passwords must match each other.": "Die neuen Passwörter müssen identisch sein.",
|
||||
"A new password must be entered.": "Es muss ein neues Passwort eingegeben werden.",
|
||||
"The email address linked to your account must be entered.": "Es muss die Email-Adresse eingeben werden, welche zum Account gehört.",
|
||||
"Failed to send email: ": "Email konnte nicht versendet werden: ",
|
||||
"unknown device": "Unbekanntes Gerät",
|
||||
"NOT verified": "NICHT verifiziert",
|
||||
"Blacklisted": "Blockiert",
|
||||
"verified": "verifiziert",
|
||||
"Name": "Name",
|
||||
"Device ID": "Geräte ID",
|
||||
"Verification": "Verifizierung",
|
||||
"Ed25519 fingerprint": "Ed25519 Fingerprint",
|
||||
"User ID": "Benutzer ID",
|
||||
"Curve25519 identity key": "Curve25519 Identity Schlüssel",
|
||||
"Claimed Ed25519 fingerprint key": "Geforderter Ed25519 Fingerprint Schlüssel",
|
||||
"none": "keiner",
|
||||
"Algorithm": "Algorithmus",
|
||||
"unencrypted": "unverschlüsselt",
|
||||
"Decryption error": "Entschlüsselungs Fehler",
|
||||
"Session ID": "Sitzungs-ID",
|
||||
"End-to-end encryption information": "Ende-zu-Ende Verschlüsselungs Informationen",
|
||||
"Event information": "Ereignis Informationen",
|
||||
"Sender device information": "Absender Geräte Informationen",
|
||||
"Displays action": "Zeigt Aktionen an",
|
||||
"Bans user with given id": "Sperrt Benutzer mit der angegebenen ID",
|
||||
"Deops user with given id": "Entfernt OP beim Benutzer mit der angegebenen ID",
|
||||
"Invites user with given id to current room": "Lädt Benutzer mit der angegebenen ID in den aktuellen Raum ein",
|
||||
"Joins room with given alias": "Betrete Raum mit angegebenen Alias",
|
||||
"Kicks user with given id": "Kickt Benutzer mit angegebener ID",
|
||||
"Changes your display nickname": "Ändert deinen angezeigten Nicknamen",
|
||||
"Change Password": "Passwort ändern",
|
||||
"Searches DuckDuckGo for results": "Suche in DuckDuckGo nach Ergebnissen",
|
||||
"Commands": "Kommandos",
|
||||
"Emoji": "Smileys",
|
||||
"Sorry, this homeserver is using a login which is not recognised ": "Entschuldigung, dieser homeserver nutzt eine Anmeldetechnik die nicht bekannt ist ",
|
||||
"Login as guest": "Anmelden als Gast",
|
||||
"Return to app": "Zurück zur Anwendung",
|
||||
"Sign in": "Anmelden",
|
||||
"Create a new account": "Erstelle einen neuen Benutzer",
|
||||
"Send an encrypted message": "Sende eine verschlüsselte Nachricht",
|
||||
"Send a message (unencrypted)": "Sende eine Nachricht (unverschlüsselt)",
|
||||
"Warning!": "Warnung!",
|
||||
"Direct Chat": "Privater Chat",
|
||||
"Error": "Fehler",
|
||||
"accept": "akzeptiere",
|
||||
"accepted an invitation": "Einladung akzeptieren",
|
||||
"accepted the invitation for": "Akzeptierte die Einladung für",
|
||||
"Add email address": "Füge E-Mail-Adresse hinzu",
|
||||
"Advanced": "Erweitert",
|
||||
"all room members, from the point they joined": "Alle Raum-Mitglieder - seitdem sie beigetreten sind",
|
||||
"and": "und",
|
||||
"An email has been sent to": "Eine E-Mail wurde gesendet an",
|
||||
"anyone": "Jeder",
|
||||
"Anyone who knows the room's link, apart from guests": "Jeder der den Raum-Link kennt - abgesehen von Gästen",
|
||||
"Anyone who knows the room's link, including guests": "Jeder der den Raum-Link kennt - auch Gäste",
|
||||
"Are you sure you want to leave the room?": "Bist du sicher, dass du den Raum verlassen willst?",
|
||||
"Are you sure you want to reject the invitation?": "Bist du sicher, dass die die Einladung ablehnen willst?",
|
||||
"Are you sure you want upload the following files?": "Bist du sicher, dass du die folgenden Dateien hochladen willst?",
|
||||
"banned": "gebannt",
|
||||
"Banned users": "Gebannte Nutzer",
|
||||
"Bug Report": "Fehlerbericht",
|
||||
"changed avatar": "änderte Avatar",
|
||||
"changed their display name from": "änderte seinen Anzeigenamen von",
|
||||
"changed their profile picture": "änderte sein Profilbild",
|
||||
"changed the room name to": "änderte den Raumnamen zu",
|
||||
"changed the topic to": "änderte das Thema zu",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Änderungen bzgl. wer die Historie lesen kann betrifft nur zukünftige Nachrichten in diesem Raum",
|
||||
"Clear Cache and Reload": "Leere Cache und lade neu",
|
||||
"Click here": "Klicke hier",
|
||||
"Confirm your new password": "Bestätige dein neues Passwort",
|
||||
"Continue": "Fortfahren",
|
||||
"Create an account": "Erstelle einen Account",
|
||||
"Create Room": "Erstelle Raum",
|
||||
"Cryptography": "Kryptografie",
|
||||
"Deactivate Account": "Deaktiviere Account",
|
||||
"Deactivate my account": "Deaktiviere meinen Account",
|
||||
"decline": "Ablehnen",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Geräte werden nicht in der Lage sein, die Historie vor dem Beitritt in den Raum zu entschlüsseln",
|
||||
"Display name": "Anzeigename",
|
||||
"Email Address": "E-Mail-Adresse",
|
||||
"Email, name or matrix ID": "E-Mail, Name oder Matrix-ID",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Verschlüsselte Nachrichten werden an Clients nicht sichtbar sein, die Verschlüsselung noch nicht implementiert haben",
|
||||
"Encrypted room": "Verschlüsselter Raum",
|
||||
"Encryption is enabled in this room": "Verschlüsselung ist in diesem Raum aktiviert",
|
||||
"Encryption is not enabled in this room": "Verschlüsselung ist in diesem Raum nicht aktiviert",
|
||||
"ended the call.": "beendete den Anruf.",
|
||||
"End-to-end encryption is in beta and may not be reliable": "Ende-zu-Ende-Verschlüsselung ist im Beta-Status und ist evtl. nicht zuverlässig",
|
||||
"Failed to send email": "Fehler beim Senden der E-Mail",
|
||||
"Account": "Konto",
|
||||
"Add phone number": "Füge Telefonnummer hinzu",
|
||||
"an address": "an Adresse",
|
||||
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Dein Passwort wurde erfolgreich geändert. Du wirst keine Benachrichtigungen an anderen Geräten empfangen bis du dich dort erneut anmeldest",
|
||||
"all room members": "Alle Raum-Mitglieder",
|
||||
"all room members, from the point they are invited": "Alle Raum-Mitglieder, ab dem Zeitpunkt, an dem sie eingeladen werden",
|
||||
"answered the call.": "beantwortete den Anruf.",
|
||||
"Can't load user settings": "Kann Nutzereinstellungen nicht laden",
|
||||
"changed name": "änderte Namen",
|
||||
"changed the power level of": "änderte Berechtigungslevel von",
|
||||
"Clear Cache": "Leere Cache",
|
||||
"Click here to fix": "Klicke hier zum reparieren",
|
||||
"*️⃣ Commands": "*️⃣ Befehle",
|
||||
"Default": "Standard",
|
||||
"demote": "Zum zurückstufen",
|
||||
"Export E2E room keys": "Exportiere E2E-Raum-Schlüssel",
|
||||
"Failed to change password. Is your password correct?": "Passwort-Änderung schlug fehl. Ist dein Passwort korrekt?",
|
||||
"Failed to forget room": "Vergessen des Raums schlug fehl",
|
||||
"Failed to leave room": "Fehler beim Verlassen des Raums",
|
||||
"Failed to reject invitation": "Fehler beim Abweisen der Einladung",
|
||||
"Failed to set avatar.": "Fehler beim Setzen des Avatars.",
|
||||
"Failed to unban": "Entbannen fehlgeschlagen",
|
||||
"Failed to upload file": "Dateiupload fehlgeschlagen",
|
||||
"Favourite": "Favorit",
|
||||
"favourite": "Favoriten",
|
||||
"Forget room": "Raum vergessen",
|
||||
"Forgot your password?": "Passwort vergessen?",
|
||||
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Aus Sicherheitsgründen werden beim Ausloggen alle Ende-zu-Ende-Verschlüsselungsschlüssel von diesem Browser gelöscht. Wenn du in späteren Riot-Sitzungen die Konversationshistorie entschlüsseln möchtest, exportiere bitte deine Schlüssel zur sicheren Verwahrung.",
|
||||
"For security, this session has been signed out. Please sign in again.": "Zur Sicherheit wurde diese Sitzung abgemeldet. Bitte melde dich erneut an.",
|
||||
"Found a bug?": "Fehler gefunden?",
|
||||
"Guests cannot join this room even if explicitly invited.": "Gäste können diesem Raum nicht beitreten auch wenn sie explizit eingeladen werden.",
|
||||
"Guests can't set avatars. Please register.": "Gäste können keine Avatare setzen. Bitte registriere dich.",
|
||||
"Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte registrieren um hochzuladen.",
|
||||
"had": "hatte",
|
||||
"Hangup": "Auflegen",
|
||||
"Homeserver is": "Der Homeserver ist",
|
||||
"Identity Server is": "Der Identitätsserver ist",
|
||||
"I have verified my email address": "Ich habe meine E-Mail-Adresse verifiziert",
|
||||
"Import E2E room keys": "E2E-Raum-Schlüssel importieren",
|
||||
"Invalid Email Address": "Ungültige E-Mail-Adresse",
|
||||
"invited": "eingeladen",
|
||||
"Invite new room members": "Lade neue Raum-Mitglieder ein",
|
||||
"is a": "ist ein",
|
||||
"is trusted": "wird vertraut",
|
||||
"I want to sign in with": "Ich möchte mich anmelden mit",
|
||||
"joined and left": "trat bei und ging",
|
||||
"joined": "trat bei",
|
||||
"joined the room": "trat dem Raum bei",
|
||||
"Leave room": "Verlasse Raum",
|
||||
"left and rejoined": "ging(en) und trat(en) erneut bei",
|
||||
"left": "ging",
|
||||
"left the room": "verließ den Raum",
|
||||
"Logged in as": "Angemeldet als",
|
||||
"Logout": "Abmelden",
|
||||
"made future room history visible to": "mache kommende Raum-Historie sichtbar für",
|
||||
"Manage Integrations": "Verwalte Integrationen",
|
||||
"Members only": "Nur Mitglieder",
|
||||
"Mobile phone number": "Mobile Telefonnummer",
|
||||
"Moderator": "Moderator",
|
||||
"my Matrix ID": "Meine Matrix-ID",
|
||||
"Never send encrypted messages to unverified devices from this device": "Niemals verschlüsselte Nachrichten an unverifizierte Geräte von diesem Gerät aus versenden",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Niemals verschlüsselte Nachrichten an unverifizierte Geräte in diesem Raum von diesem Gerät aus senden",
|
||||
"New password": "Neues Passwort",
|
||||
"Notifications": "Benachrichtigungen",
|
||||
" (not supported by this browser)": " (von diesem Browser nicht unterstützt)",
|
||||
"<not supported>": "<nicht unterstützt>",
|
||||
"No users have specific privileges in this room": "Keine Nutzer haben in diesem Raum besondere Berechtigungen",
|
||||
"olm version": "OLM-Version",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Sobald Verschlüsselung für einen Raum aktiviert wird, kann diese (aktuell noch) nicht wieder deaktiviert werden",
|
||||
"Only people who have been invited": "Nur Personen die eingeladen wurden",
|
||||
"or": "oder",
|
||||
"other": "weiteres",
|
||||
"others": "andere",
|
||||
"Password": "Passwort",
|
||||
"Permissions": "Berechtigungen",
|
||||
"Phone": "Telefon",
|
||||
"placed a": "plazierte einen",
|
||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Bitte prüfen sie ihre E-Mails und klicken sie auf den enthaltenden Link. Anschließend klicke auf \"Fortsetzen\".",
|
||||
"Please Register": "Bitte registrieren",
|
||||
"Privacy warning": "Datenschutzwarnung",
|
||||
"Privileged Users": "Privilegierte Nutzer",
|
||||
"Profile": "Profil",
|
||||
"Refer a friend to Riot:": "Lade eine(n) Freund(in) zu Riot ein",
|
||||
"rejected": "abgeleht",
|
||||
"Once you've followed the link it contains, click below": "Nachdem du dem Link gefolgt bist, klicke unten",
|
||||
"rejected the invitation.": "lehnte die Einladung ab.",
|
||||
"Reject invitation": "Einladung ablehnen",
|
||||
"Remove Contact Information?": "Lösche Kontakt-Informationen?",
|
||||
"removed their display name": "löschte den eigenen Anzeigenamen",
|
||||
"Remove": "Entferne",
|
||||
"requested a VoIP conference": "hat eine VoIP-Konferenz angefordert",
|
||||
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Eine Passwortänderung sorgt aktuell dafür, dass alle Ende-zu-Ende-Schlüssel von allen Geräten zurückgesetzt werden. Dadurch wird die verschlüsselte Chat-Historie unlesbar, es sei denn Sie exportieren vorher Ihre Raum-Schlüssel und importieren sie nachher wieder. In Zukunft wird dies verbessert.",
|
||||
"restore": "Zum zurücksetzen",
|
||||
"Return to login screen": "Zur Anmeldung zurückkehren",
|
||||
"Room Colour": "Raumfarbe",
|
||||
"Room name (optional)": "Raumname (optional)",
|
||||
"Scroll to unread messages": "Scrolle zu ungelesenen Nachrichten",
|
||||
"Send Invites": "Sende Einladungen",
|
||||
"Send Reset Email": "Sende Rücksetz-E-mail",
|
||||
"sent an image": "sandte ein Bild",
|
||||
"sent an invitation to": "sandte eine Einladung an",
|
||||
"sent a video": "sandte ein Video",
|
||||
"Server may be unavailable or overloaded": "Server könnte nicht verfügbar oder überlastet sein",
|
||||
"set a profile picture": "setzte ein Profilbild",
|
||||
"set their display name to": "setzte den Anzeigenamen auf",
|
||||
"Settings": "Einstellungen",
|
||||
"Signed Out": "Abgemeldet",
|
||||
"Sign out": "Abmelden",
|
||||
"since the point in time of selecting this option": "seitdem diese Option gewählt wird",
|
||||
"since they joined": "seitdem sie beitraten",
|
||||
"since they were invited": "seitdem sie eingeladen wurden",
|
||||
"Someone": "Jemand",
|
||||
"Start a chat": "Starte einen Chat",
|
||||
"Start Chat": "Starte Chat",
|
||||
"Success": "Erfolg",
|
||||
"tag direct chat": "Zum kennzeichnen als direkten Chat",
|
||||
"The default role for new room members is": "Die Standard-Rolle für neue Raum-Mitglieder ist",
|
||||
"their invitations": "ihre Einladungen",
|
||||
"their invitation": "ihre Einladung",
|
||||
"These are experimental features that may break in unexpected ways. Use with caution": "Dies sind experimentelle Funktionen die in unerwarteter Weise Fehler verursachen können. Mit Vorsicht benutzen",
|
||||
"The visibility of existing history will be unchanged": "Die Sichtbarkeit der existenten Historie bleibt unverändert",
|
||||
"This doesn't appear to be a valid email address": "Die scheint keine valide E-Mail-Adresse zu sein",
|
||||
"this invitation?": "diese Einladung?",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Dies ist eine Vorschau dieses Raumes. Raum-Interaktionen wurden deaktiviert",
|
||||
"This room is not accessible by remote Matrix servers": "Dieser Raum ist über entfernte Matrix-Server nicht zugreifbar",
|
||||
"This room's internal ID is": "Die interne ID dieses Raumes ist",
|
||||
"To ban users": "Zum Nutzer bannen",
|
||||
"To configure the room": "Zum Raum konfigurieren",
|
||||
"To invite users into the room": "Zum Nutzer in den Raum einladen",
|
||||
"to join the discussion": "Zum mitdiskutieren",
|
||||
"To kick users": "Zum Nutzer kicken",
|
||||
"Admin": "Administrator",
|
||||
"Server may be unavailable, overloaded, or you hit a bug": "Server könnte nicht verfügbar oder überlastet sein oder du bist auf einen Fehler gestoßen",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar": "Verbindung zum Homeserver ist über HTTP nicht möglich, wenn HTTPS in deiner Browserleiste steht",
|
||||
"Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen",
|
||||
"Disable inline URL previews by default": "Deaktiviere URL-Vorschau im Chat standardmäßig",
|
||||
"Guests can't use labs features. Please register.": "Gäste können keine Labor-Funktionen nutzen. Bitte registrieren.",
|
||||
"Labs": "Labor",
|
||||
"Show panel": "Zeige Panel",
|
||||
"To redact messages": "Zum Nachrichten verbergen",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your": "Die Verbindung mit dem Homeserver ist fehlgeschlagen. Bitte überprüfe deine Verbindung und stelle sicher, dass dein(e) ",
|
||||
"tag as": "kennzeichne als",
|
||||
"To reset your password, enter the email address linked to your account": "Um dein Passwort zurückzusetzen, gebe deine E-Mail-Adresse, die mit deinem Account verbunden ist, ein",
|
||||
"To send messages": "Zum Nachrichten senden",
|
||||
"turned on end-to-end encryption (algorithm": "aktivierte Ende-zu-Ende-Verschlüsselung (Algorithmus",
|
||||
"Unable to add email address": "Unfähig die E-Mail-Adresse hinzuzufügen",
|
||||
"Unable to remove contact information": "Unfähig die Kontakt-Informationen zu löschen",
|
||||
"Unable to verify email address.": "Unfähig die E-Mail-Adresse zu verifizieren.",
|
||||
"Unban": "Entbannen",
|
||||
"Unencrypted room": "Unverschlüsselter Raum",
|
||||
"unknown error code": "Unbekannter Fehlercode",
|
||||
"unknown": "unbekannt",
|
||||
"Upload avatar": "Avatar hochladen",
|
||||
"uploaded a file": "lud eine Datei hoch",
|
||||
"Upload Files": "Dateien hochladen",
|
||||
"Upload file": "Datei hochladen",
|
||||
"User Interface": "Nutzerschnittstelle",
|
||||
"User name": "Nutzername",
|
||||
"Users": "Nutzer",
|
||||
"User": "Nutzer",
|
||||
"Verification Pending": "Verifizierung ausstehend",
|
||||
"Video call": "Videoanruf",
|
||||
"Voice call": "Sprachanruf",
|
||||
"VoIP conference finished.": "VoIP-Konferenz beendet.",
|
||||
"VoIP conference started.": "VoIP-Konferenz gestartet.",
|
||||
"(warning: cannot be disabled again!)": "(Warnung: Kann nicht wieder deaktiviert werden!)",
|
||||
"was banned": "wurde gebannt",
|
||||
"was invited": "wurde eingeladen",
|
||||
"was kicked": "wurde gekickt",
|
||||
"was unbanned": "wurde entbannt",
|
||||
"was": "wurde",
|
||||
"Who can access this room?": "Wer hat Zugang zu diesem Raum?",
|
||||
"Who can read history?": "Wer kann die Historie lesen?",
|
||||
"Who would you like to add to this room?": "Wen möchtest du zu diesem Raum hinzufügen?",
|
||||
"Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?",
|
||||
"Would you like to": "Möchtest du",
|
||||
"You are trying to access": "Du möchtest Zugang zu %(sth)s bekommen",
|
||||
"You do not have permission to post to this room": "Du hast keine Berechtigung an diesen Raum etwas zu senden",
|
||||
"You have been invited to join this room by %(inviterName)s": "Du wurdest in diesen Raum eingeladen von %(inviterName)s",
|
||||
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Du wurdest von allen Geräten ausgeloggt und wirst keine Push-Benachrichtigungen mehr bekommen. Um Benachrichtigungen zu reaktivieren melde dich auf jedem Gerät neu an",
|
||||
"you must be a": "nötige Rolle",
|
||||
"Your password has been reset": "Dein Passwort wurde zurückgesetzt",
|
||||
"You should not yet trust it to secure data": "Du solltest nicht darauf vertrauen um deine Daten abzusichern",
|
||||
"removed their profile picture": "löschte das eigene Profilbild",
|
||||
"times": "mal",
|
||||
"Bulk Options": "Bulk-Optionen",
|
||||
"Call Timeout": "Anruf-Timeout",
|
||||
"Conference call failed": "Konferenzgespräch fehlgeschlagen",
|
||||
"Conference calling is in development and may not be reliable": "Konferenzgespräche sind in Entwicklung und evtl. nicht zuverlässig",
|
||||
"Conference calls are not supported in encrypted rooms": "Konferenzgespräche sind in verschlüsselten Räumen nicht unterstützt",
|
||||
"Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt",
|
||||
"Existing Call": "Existierender Anruf",
|
||||
"Failed to set up conference call": "Aufbau des Konferenzgesprächs fehlgeschlagen",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Stelle sicher, dass du den Link in der E-Mail anklicktest",
|
||||
"Failure to create room": "Raumerstellung fehlgeschlagen",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Gäste können keine neuen Räume erstellen. Bitte registrieren um einen Raum zu erstellen und einen Chat zu starten",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot hat keine Berechtigung Benachrichtigungen zu senden - bitte prüfe deine Browser-Einstellungen",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot hat das Recht nicht bekommen Benachrichtigungen zu senden. Bitte erneut probieren",
|
||||
"This email address is already in use": "Diese E-Mail-Adresse wird bereits verwendet",
|
||||
"This email address was not found": "Diese E-Mail-Adresse wurde nicht gefunden",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "Die Datei '%(fileName)s' überschreitet das Größen-Limit für Uploads auf diesem Homeserver",
|
||||
"The file '%(fileName)s' failed to upload": "Das Hochladen der Datei '%(fileName)s' schlug fehl",
|
||||
"The remote side failed to pick up": "Die Gegenstelle konnte nicht abheben",
|
||||
"This phone number is already in use": "Diese Telefonnummer wird bereits verwendet",
|
||||
"Unable to restore previous session": "Frühere Sitzung nicht wiederherstellbar",
|
||||
"Unable to capture screen": "Unfähig den Bildschirm aufzunehmen",
|
||||
"Unable to enable Notifications": "Unfähig Benachrichtigungen zu aktivieren",
|
||||
"Upload Failed": "Upload fehlgeschlagen",
|
||||
"VoIP is unsupported": "VoIP ist nicht unterstützt",
|
||||
"You are already in a call": "Du bist bereits bei einem Anruf",
|
||||
"You cannot place a call with yourself": "Du kannst keinen Anruf mit dir selbst starten",
|
||||
"You cannot place VoIP calls in this browser": "Du kannst kein VoIP-Gespräch in diesem Browser starten",
|
||||
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "Du musst dich erneut anmelden um Ende-zu-Ende-Verschlüsselungscodes für dieses Gerät zu generieren und den öffentl. Schlüssel an deinen Homeserver zu senden. Dies muss einmal gemacht werden. Entschuldige die Unannehmlichkeit.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Deine E-Mail-Adresse scheint nicht mit einer Matrix-ID auf diesem Homeserver verknüpft zu sein",
|
||||
"Sun": "So",
|
||||
"Mon": "Mo",
|
||||
"Tue": "Di",
|
||||
"Wed": "Mi",
|
||||
"Thu": "Do",
|
||||
"Fri": "Fr",
|
||||
"Sat": "Sa",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Feb",
|
||||
"Mar": "März",
|
||||
"Apr": "April",
|
||||
"May": "Mai",
|
||||
"Jun": "Juni",
|
||||
"Jul": "Juli",
|
||||
"Aug": "Aug",
|
||||
"Sep": "Sep",
|
||||
"Oct": "Okt",
|
||||
"Nov": "Nov",
|
||||
"Dec": "Dez",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(time)s",
|
||||
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
|
||||
"Set a display name:": "Setze einen Anzeigenamen:",
|
||||
"Upload an avatar:": "Lade einen Avatar hoch:",
|
||||
"This server does not support authentication with a phone number.": "Dieser Server unterstützt keine Authentifizierung mittels Telefonnummer.",
|
||||
"Missing password.": "Fehlendes Passwort.",
|
||||
"Passwords don't match.": "Passwörter passen nicht zusammen.",
|
||||
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Passwort zu kurz (min. %(MIN_PASSWORD_LENGTH)s).",
|
||||
"This doesn't look like a valid email address.": "Dies sieht nicht nach einer validen E-Mail-Adresse aus.",
|
||||
"This doesn't look like a valid phone number.": "Dies sieht nicht nach einer validen Telefonnummer aus.",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Benutzernamen sollen nur Buchstaben, Nummern, Binde- und Unterstriche enthalten.",
|
||||
"An unknown error occurred.": "Ein unbekannter Fehler trat auf.",
|
||||
"I already have an account": "Ich habe bereits einen Account",
|
||||
"An error occured: %(error_string)s": "Ein Fehler trat auf: %(error_string)s",
|
||||
"Topic": "Thema",
|
||||
"Make this room private": "Mache diesen Raum privat",
|
||||
"Share message history with new users": "Teile Nachrichtenhistorie mit neuen Nutzern",
|
||||
"Encrypt room": "Entschlüssele Raum",
|
||||
"To send events of type": "Zum Senden von Ereignissen mit Typ",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s und %(lastPerson)s schreiben",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s akzeptierte eine Einladung.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s akzeptierte die Einladung für %(displayName)s.",
|
||||
"%(names)s and one other are typing": "%(names)s und eine weitere Person tippen",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen tippen",
|
||||
"%(senderName)s answered the call.": "%(senderName)s beantwortete den Anruf.",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s bannte %(targetName)s.",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s änderte den Anzeigenamen von %(oldDisplayName)s zu %(displayName)s.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s änderte das Profilbild.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s änderte das Thema zu \"%(topic)s\".",
|
||||
"/ddg is not a command": "/ddg ist kein Kommando",
|
||||
"%(senderName)s ended the call.": "%(senderName)s beendete den Anruf.",
|
||||
"Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl",
|
||||
"Failed to send request.": "Anfrage zu senden schlug fehl.",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s lud %(targetName)s ein.",
|
||||
"%(displayName)s is typing": "%(displayName)s tippt",
|
||||
"%(targetName)s joined the room.": "%(targetName)s trat dem Raum bei.",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s kickte %(targetName)s.",
|
||||
"%(targetName)s left the room.": "%(targetName)s verließ den Raum.",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s machte die zukünftige Raumhistorie sichtbar für",
|
||||
"Missing room_id in request": "Fehlende room_id in Anfrage",
|
||||
"Missing user_id in request": "Fehlende user_id in Anfrage",
|
||||
"Must be viewing a room": "Muss einen Raum ansehen",
|
||||
"New Composer & Autocomplete": "Neuer Eingabeverarbeiter & Autovervollständigung",
|
||||
"(not supported by this browser)": "(nicht von diesem Browser unterstützt)",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s startete einen %(callType)s-Anruf.",
|
||||
"Power level must be positive integer.": "Berechtigungslevel muss eine positive Zahl sein.",
|
||||
"Reason": "Grund",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s lehnte die Einladung ab.",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s löschte den Anzeigenamen (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s löschte das Profilbild.",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s fragte nach einer VoIP-Konferenz.",
|
||||
"Room %(roomId)s not visible": "Raum %(roomId)s ist nicht sichtbar",
|
||||
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s hat ein Bild gesendet.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten.",
|
||||
"%(senderName)s set a profile picture.": "%(senderName)s setzte ein Profilbild.",
|
||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s setzte den Anzeigenamen zu %(displayName)s.",
|
||||
"This room is not recognised.": "Dieser Raum wurde nicht erkannt.",
|
||||
"These are experimental features that may break in unexpected ways": "Dies sind experimentelle Funktionen, die in unerwarteter Weise Fehler verursachen können",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste.",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s schaltete Ende-zu-Ende-Verschlüsselung ein (Algorithmus: %(algorithm)s).",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s zog Bann für %(targetName)s zurück.",
|
||||
"Usage": "Verwendung",
|
||||
"Use with caution": "Mit Vorsicht benutzen",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s zog die Einladung für %(targetName)s zurück.",
|
||||
"You need to be able to invite users to do that.": "Du musst in der Lage sein Nutzer einzuladen um dies zu tun.",
|
||||
"You need to be logged in.": "Du musst angemeldet sein.",
|
||||
"There are no visible files in this room": "Es gibt keine sichtbaren Dateien in diesem Raum",
|
||||
"Error changing language": "Fehler beim Ändern der Sprache",
|
||||
"Riot was unable to find the correct Data for the selected Language.": "Riot war nicht in der Lage die korrekten Daten für die ausgewählte Sprache zu finden.",
|
||||
"Connectivity to the server has been lost.": "Verbindung zum Server untergebrochen.",
|
||||
"Sent messages will be stored until your connection has returned.": "Gesendete Nachrichten werden gespeichert bis die Verbindung wiederhergestellt wurde.",
|
||||
"Auto-complete": "Autovervollständigung",
|
||||
"Resend all": "Alles erneut senden",
|
||||
"cancel all": "alles abbrechen",
|
||||
"now. You can also select individual messages to resend or cancel.": "jetzt. Du kannst auch einzelne Nachrichten zum erneuten Senden oder Abbrechen auswählen.",
|
||||
"Active call": "Aktiver Anruf",
|
||||
"withdrawn": "zurückgezogen",
|
||||
"To link to a room it must have": "Um einen Raum zu verlinken, benötigt er",
|
||||
"were": "wurden",
|
||||
"en": "Englisch",
|
||||
"pt-br": "Portugisisch (Brasilien)",
|
||||
"de": "Deutsch",
|
||||
"da": "Dänisch",
|
||||
"ru": "Russisch",
|
||||
"Drop here %(toAction)s": "Hierher ziehen: %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Hierher ziehen: %(section)s taggen",
|
||||
"Press": "Drücke",
|
||||
"tag as %(tagName)s": "als %(tagName)s taggen",
|
||||
"to browse the directory": "um das Verzeichnis anzusehen",
|
||||
"to demote": "um die Priorität herabzusetzen",
|
||||
"to favourite": "zum Favorisieren",
|
||||
"to make a room or": "um einen Raum zu erstellen, oder",
|
||||
"to restore": "zum wiederherstellen",
|
||||
"to start a chat with someone": "um einen Chat mit jemandem zu starten",
|
||||
"to tag direct chat": "als direkten Chat markieren",
|
||||
"You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke",
|
||||
"click to reveal": "Klicke zum anzeigen",
|
||||
"To redact other users' messages": "Um Nachrichten anderer zu verbergen",
|
||||
"You are trying to access %(roomName)s": "Du versuchst auf %(roomName)s zuzugreifen",
|
||||
"af": "Afrikaans",
|
||||
"ar-ae": "Arabisch (U.A.E.)",
|
||||
"ar-bh": "Arabisch (Bahrain)",
|
||||
"ar-dz": "Arabisch (Algeria)",
|
||||
"ar-eg": "Arabisch (Ägypten)",
|
||||
"ar-iq": "Arabisch (Irak)",
|
||||
"ar-jo": "Arabisch (Jordanien)",
|
||||
"ar-kw": "Arabisch (Kuwait)",
|
||||
"ar-lb": "Arabisch (Libanon)",
|
||||
"ar-ly": "Arabisch (Lybien)",
|
||||
"ar-ma": "Arabisch (Marokko)",
|
||||
"ar-om": "Arabisch (Oman)",
|
||||
"ar-qa": "Arabisch (Qatar)",
|
||||
"ar-sa": "Arabisch (Saudi Arabien)",
|
||||
"ar-sy": "Arabisch (Syrien)",
|
||||
"ar-tn": "Arabisch (Tunisien)",
|
||||
"ar-ye": "Arabisch (Yemen)",
|
||||
"be": "Weißrussisch",
|
||||
"bg": "Bulgarisch",
|
||||
"cs": "Tschechisch",
|
||||
"de-at": "Deutsch (Österreich)",
|
||||
"de-ch": "Deutsch (Schweiz)",
|
||||
"de-li": "Deutsch (Liechtenstein)",
|
||||
"de-lu": "Deutsch (Luxemburg)",
|
||||
"el": "Griechisch",
|
||||
"en-au": "Englisch (Australien)",
|
||||
"en-bz": "Englisch (Belize)",
|
||||
"en-ca": "Englisch (Kanada)",
|
||||
"en-gb": "Englisch (Vereintes Königreich)",
|
||||
"en-ie": "Englisch (Irland)",
|
||||
"en-jm": "Englisch (Jamaika)",
|
||||
"en-nz": "Englisch (Neuseeland)",
|
||||
"en-tt": "Englisch (Trinidad)",
|
||||
"en-us": "Englisch (Vereinigte Staaten)",
|
||||
"en-za": "Englisch (Süd-Afrika)",
|
||||
"es-ar": "Spanisch (Argentinien)",
|
||||
"es-bo": "Spanisch (Bolivien)",
|
||||
"es-cl": "Spanisch (Chile)",
|
||||
"es-co": "Spanisch (Kolombien)",
|
||||
"es-cr": "Spanisch (Costa Rica)",
|
||||
"es-do": "Spanisch (Dominikanische Republik)",
|
||||
"es-ec": "Spanisch (Ecuador)",
|
||||
"es-gt": "Spanisch (Guatemala)",
|
||||
"es-hn": "Spanisch (Honduras)",
|
||||
"es-mx": "Spanisch (Mexico)",
|
||||
"es-ni": "Spanisch (Nicaragua)",
|
||||
"es-pa": "Spanisch (Panama)",
|
||||
"es-pe": "Spanisch (Peru)",
|
||||
"es-pr": "Spanisch (Puerto Rico)",
|
||||
"es-py": "Spanisch (Paraguay)",
|
||||
"es": "Spanisch (Spanien)",
|
||||
"es-sv": "Spanisch (El Salvador)",
|
||||
"es-uy": "Spanisch (Uruguay)",
|
||||
"es-ve": "Spanisch (Venezuela)",
|
||||
"et": "Estländisch",
|
||||
"eu": "Baskisch (Baskenland)",
|
||||
"fa": "Persisch (Farsi)",
|
||||
"fr-be": "Französisch (Belgien)",
|
||||
"fr-ca": "Französisch (Kanada)",
|
||||
"fr-ch": "Französisch (Schweiz)",
|
||||
"fr": "Französisch",
|
||||
"fr-lu": "Französisch (Luxemburg)",
|
||||
"gd": "Gälisch (Schottland)",
|
||||
"he": "Hebräisch",
|
||||
"hr": "Kroatisch",
|
||||
"hu": "Ungarisch",
|
||||
"id": "Indonesisch",
|
||||
"is": "Isländisch",
|
||||
"it-ch": "Italienisch (Schweiz)",
|
||||
"it": "Italienisch",
|
||||
"ja": "Japanisch",
|
||||
"ji": "Jiddisch",
|
||||
"ko": "Koreanisch (Johab)",
|
||||
"lt": "Litauisch",
|
||||
"lv": "lettisch",
|
||||
"mk": "Mazedonisch (FYROM)",
|
||||
"ms": "Malaysisch",
|
||||
"mt": "Maltesisch",
|
||||
"nl-be": "Niederländisch (Belgien)",
|
||||
"nl": "Niederländisch",
|
||||
"no": "Norwegisch",
|
||||
"pl": "Polnisch",
|
||||
"pt": "Portugiesisch",
|
||||
"rm": "Rätoromanisch",
|
||||
"ro-mo": "Rumänisch (Republik von Moldavien)",
|
||||
"ro": "Romanian",
|
||||
"ru-mo": "Russisch",
|
||||
"sb": "Sorbian",
|
||||
"sk": "Slowakisch",
|
||||
"sl": "Slowenisch",
|
||||
"sq": "Albanisch",
|
||||
"sr": "Serbisch (lateinisch)",
|
||||
"sv-fi": "Schwedisch (Finnland)",
|
||||
"sv": "Schwedisch",
|
||||
"sx": "Sutu",
|
||||
"sz": "Samisch (Lappish)",
|
||||
"th": "Thailändisch",
|
||||
"tn": "Setswana",
|
||||
"tr": "Türkisch",
|
||||
"ts": "Tsonga",
|
||||
"uk": "Ukrainisch",
|
||||
"ur": "Urdu",
|
||||
"ve": "Tshivenda",
|
||||
"vi": "Vietnamesisch",
|
||||
"zh-cn": "Chinesisch (PRC)",
|
||||
"zh-hk": "Chinesisch (Hong Kong SAR)",
|
||||
"zh-sg": "Chinesisch (Singapur)",
|
||||
"zh-tw": "Chinesisch (Taiwan)",
|
||||
"zu": "Zulu",
|
||||
"ca": "katalanisch",
|
||||
"fi": "Finnish",
|
||||
"fo": "Färöisch",
|
||||
"ga": "Irisch",
|
||||
"hi": "Hindi",
|
||||
"xh": "Xhosa",
|
||||
"Monday": "Montag",
|
||||
"Tuesday": "Dienstag",
|
||||
"Wednesday": "Mittwoch",
|
||||
"Thursday": "Donnerstag",
|
||||
"Friday": "Freitag",
|
||||
"Saturday": "Samstag",
|
||||
"Sunday": "Sonntag",
|
||||
"Failed to forget room %(errCode)s": "Das Entfernen des Raums %(errCode)s aus deiner Liste ist fehlgeschlagen",
|
||||
"Failed to join the room": "Fehler beim Betreten des Raumes",
|
||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Eine Textnachricht wurde an +%(msisdn)s gesendet. Bitte gebe den Verifikationscode ein, den er beinhaltet",
|
||||
"and %(overflowCount)s others...": "und %(overflowCount)s weitere...",
|
||||
"and one other...": "und ein(e) weitere(r)...",
|
||||
"Are you sure?": "Bist du sicher?",
|
||||
"Attachment": "Anhang",
|
||||
"Ban": "Banne",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem %(urlStart)s SSL-Zertifikat deines Heimservers %(urlEnd)s vertraut wird",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder %(urlStart)s aktiviere unsichere Skripte %(urlEnd)s",
|
||||
"changing room on a RoomView is not supported": "Das Ändern eines Raumes in einer RaumAnsicht wird nicht unterstützt",
|
||||
"Click to mute audio": "Klicke um den Ton stumm zu stellen",
|
||||
"Click to mute video": "Klicke um das Video stumm zu stellen",
|
||||
"Command error": "Befehlsfehler",
|
||||
"Decrypt %(text)s": "Entschlüssele %(text)s",
|
||||
"Delete": "Lösche",
|
||||
"Devices": "Geräte",
|
||||
"Direct chats": "Direkte Chats",
|
||||
"Disinvite": "Ausladen",
|
||||
"Download %(text)s": "%(text)s herunterladen",
|
||||
"Enter Code": "Code eingeben",
|
||||
"Failed to ban user": "Bannen des Nutzers fehlgeschlagen",
|
||||
"Failed to change power level": "Ändern des Berechtigungslevels fehlgeschlagen",
|
||||
"Failed to delete device": "Löschen des Geräts fehlgeschlagen",
|
||||
"Failed to join room": "Raumbeitritt fehlgeschlagen",
|
||||
"Failed to kick": "Kicken fehlgeschlagen",
|
||||
"Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen",
|
||||
"Failed to reject invite": "Einladung abzulehnen fehlgeschlagen",
|
||||
"Failed to save settings": "Einstellungen speichern fehlgeschlagen",
|
||||
"Failed to set display name": "Anzeigenamen zu ändern fehlgeschlagen",
|
||||
"Fill screen": "Fülle Bildschirm",
|
||||
"Guest users can't upload files. Please register to upload": "Gäste können Dateien nicht hochlagen. Bitte registrieren um hochzuladen",
|
||||
"Hide Text Formatting Toolbar": "Verberge Text-Formatierungs-Toolbar",
|
||||
"Incorrect verification code": "Verifikationscode inkorrekt",
|
||||
"Invalid alias format": "Ungültiges Alias-Format",
|
||||
"Invalid address format": "Ungültiges Adressformat",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' ist kein valides Adressformat",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' hat kein valides Aliasformat",
|
||||
"Join Room": "Raum beitreten",
|
||||
"Kick": "Kicke",
|
||||
"Level": "Level",
|
||||
"Local addresses for this room:": "Lokale Adressen dieses Raumes:",
|
||||
"Markdown is disabled": "Markdown ist deaktiviert",
|
||||
"Markdown is enabled": "Markdown ist aktiviert",
|
||||
"Message not sent due to unknown devices being present": "Nachrichten wurden nicht gesendet, da unbekannte Geräte anwesend sind",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "Neue Adresse (z.B. #foo:%(localDomain)s)",
|
||||
"not set": "nicht gesetzt",
|
||||
"not specified": "nicht spezifiziert",
|
||||
"No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungsschlüsseln",
|
||||
"No more results": "Keine weiteren Ergebnisse",
|
||||
"No results": "Keine Ergebnisse",
|
||||
"OK": "OK",
|
||||
"Revoke Moderator": "Moderator zurückziehen",
|
||||
"Search": "Suche",
|
||||
"Search failed": "Suche fehlgeschlagen",
|
||||
"Server error": "Serverfehler",
|
||||
"Server may be unavailable, overloaded, or search timed out :(": "Server ist entweder nicht verfügbar, überlastet oder die Suchezeit ist abgelaufen :(",
|
||||
"Server may be unavailable, overloaded, or the file too big": "Server ist entweder nicht verfügbar, überlastet oder die Datei ist zu groß",
|
||||
"Server unavailable, overloaded, or something else went wrong": "Server ist entweder nicht verfügbar, überlastet oder etwas anderes schlug fehl",
|
||||
"Some of your messages have not been sent": "Einige deiner Nachrichten wurden noch nicht gesendet",
|
||||
"Submit": "Absenden",
|
||||
"The main address for this room is: %(canonical_alias_section)s": "Die Hauptadresse für diesen Raum ist: %(canonical_alias_section)s",
|
||||
"This action cannot be performed by a guest user. Please register to be able to do this": "Diese Aktion kann nicht von einem Gast ausgeführt werden. Bitte registriere dich um dies zu tun",
|
||||
"%(actionVerb)s this person?": "Diese Person %(actionVerb)s?",
|
||||
"This room has no local addresses": "Dieser Raum hat keine lokale Adresse",
|
||||
"This room is private or inaccessible to guests. You may be able to join if you register": "Dieser Raum ist privat oder für Gäste nicht zugreifbar. Du kannst evtl. zugreifen, wenn du dich registrierst",
|
||||
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Versuchte einen spezifischen Punkt in der Raum-Chronik zu laden, aber du hast keine Berechtigung die angeforderte Nachricht anzuzeigen",
|
||||
"Tried to load a specific point in this room's timeline, but was unable to find it": "Versuchte einen spezifischen Punkt in der Raum-Chronik zu laden, aber er konnte nicht gefunden werden",
|
||||
"Turn Markdown off": "Markdown abschalten",
|
||||
"Turn Markdown on": "Markdown einschalten",
|
||||
"Unable to load device list": "Laden der Geräteliste nicht möglich",
|
||||
"Unknown command": "Unbekannter Befehl",
|
||||
"Unknown room %(roomId)s": "Unbekannter Raum %(roomId)s",
|
||||
"Usage: /markdown on|off": "Verwendung: /markdown on|off",
|
||||
"You seem to be in a call, are you sure you want to quit?": "Du scheinst in einem Anruf zu sein. Bist du sicher schließen zu wollen?",
|
||||
"You seem to be uploading files, are you sure you want to quit?": "Du scheinst Dateien hochzuladen. Bist du sicher schließen zu wollen?",
|
||||
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Du wirst nicht in der Lage sein, diese Änderung zu ändern da den Nutzer auf dasselbe Berechtigungslevel wie du hebst",
|
||||
"Make Moderator": "Mache zum Moderator",
|
||||
"Room": "Raum",
|
||||
"(~%(searchCount)s results)": "(~%(searchCount)s Ergebnisse)",
|
||||
"Cancel": "Abbrechen",
|
||||
"bold": "fett",
|
||||
"italic": "kursiv",
|
||||
"strike": "durchstreichen",
|
||||
"underline": "unterstreichen",
|
||||
"code": "Code",
|
||||
"quote": "Zitat",
|
||||
"bullet": "Aufzählung",
|
||||
"Click to unmute video": "Klicke um Video zu reaktivieren",
|
||||
"Click to unmute audio": "Klicke um Ton zu reaktivieren",
|
||||
"Failed to load timeline position": "Laden der Position im Zeitstrahl fehlgeschlagen",
|
||||
"Failed to toggle moderator status": "Umschalten des Moderatorstatus fehlgeschlagen",
|
||||
"Enable encryption": "Verschlüsselung aktivieren",
|
||||
"The main address for this room is": "Die Hauptadresse für diesen Raum ist",
|
||||
"Autoplay GIFs and videos": "Automatisch GIF's und Videos abspielen",
|
||||
"Don't send typing notifications": "Nicht senden, wenn ich tippe",
|
||||
"Hide read receipts": "Verberge Lesebestätigungen",
|
||||
"Never send encrypted messages to unverified devices in this room": "In diesem Raum keine verschlüsselten Nachrichten an unverifizierte Geräte senden",
|
||||
"numbullet": "Nummerierung",
|
||||
"%(items)s and %(remaining)s others": "%(items)s und %(remaining)s weitere",
|
||||
"%(items)s and one other": "%(items)s und ein(e) weitere(r)",
|
||||
"%(items)s and %(lastItem)s": "%(items)s und %(lastItem)s",
|
||||
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)strat(en) %(repeats)s mal bei",
|
||||
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)strat %(repeats)s mal bei",
|
||||
"%(severalUsers)sjoined": "%(severalUsers)straten bei",
|
||||
"%(oneUser)sjoined": "%(oneUser)strat bei",
|
||||
"%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sgingen %(repeats)s mal",
|
||||
"%(oneUser)sleft %(repeats)s times": "%(oneUser)sging %(repeats)s mal",
|
||||
"%(severalUsers)sleft": "%(severalUsers)sgingen",
|
||||
"%(oneUser)sleft": "%(oneUser)sging",
|
||||
"%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)straten bei und gingen %(repeats)s mal",
|
||||
"%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)strat bei und ging %(repeats)s mal",
|
||||
"%(severalUsers)sjoined and left": "%(severalUsers)straten bei und gingen",
|
||||
"%(oneUser)sjoined and left": "%(oneUser)strat bei und ging",
|
||||
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)s gingen und traten erneut bei - %(repeats)s mal",
|
||||
"%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sging und trat %(repeats)s mal erneut bei",
|
||||
"%(severalUsers)sleft and rejoined": "%(severalUsers)s gingen und traten erneut bei",
|
||||
"%(oneUser)sleft left and rejoined": "%(oneUser)sging und trat erneut bei",
|
||||
"%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)s lehnten %(repeats)s mal ihre Einladung ab",
|
||||
"%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)s lehnte seine/ihre Einladung %(repeats)s mal ab",
|
||||
"%(severalUsers)srejected their invitations": "%(severalUsers)slehnten ihre Einladung ab",
|
||||
"%(oneUser)srejected their invitation": "%(oneUser)slehnte seine/ihre Einladung ab",
|
||||
"%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)szogen ihre Einladungen %(repeats)s mal zurück",
|
||||
"%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)szog seine/ihre Einladung %(repeats)s mal zurück",
|
||||
"%(severalUsers)shad their invitations withdrawn": "%(severalUsers)szogen ihre Einladungen zurück",
|
||||
"%(oneUser)shad their invitation withdrawn": "%(oneUser)szog seine/ihre Einladung zurück",
|
||||
"were invited %(repeats)s times": "wurden %(repeats)s mal eingeladen",
|
||||
"was invited %(repeats)s times": "wurde %(repeats)s mal eingeladen",
|
||||
"were invited": "wurden eingeladen",
|
||||
"were banned %(repeats)s times": "wurden %(repeats)s mal gebannt",
|
||||
"was banned %(repeats)s times": "wurde %(repeats)s mal gebannt",
|
||||
"were banned": "wurden gebannt",
|
||||
"were unbanned %(repeats)s times": "wurden %(repeats)s mal entbannt",
|
||||
"was unbanned %(repeats)s times": "wurde %(repeats)s mal entbannt",
|
||||
"were unbanned": "wurden entbannt",
|
||||
"were kicked %(repeats)s times": "wurden %(repeats)s mal gekickt",
|
||||
"was kicked %(repeats)s times": "wurde %(repeats)s mal gekickt",
|
||||
"were kicked": "wurden gekickt",
|
||||
"%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sänderten %(repeats)s mal ihre Namen",
|
||||
"%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sänderte %(repeats)s mal seinen/ihren Namen",
|
||||
"%(severalUsers)schanged their name": "%(severalUsers)sänderten ihre Namen",
|
||||
"%(oneUser)schanged their name": "%(oneUser)sänderte seinen/ihren Namen",
|
||||
"%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)sänderten %(repeats)s mal ihren Avatar",
|
||||
"%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)sänderte %(repeats)s mal seinen/ihren Avatar",
|
||||
"%(severalUsers)schanged their avatar": "%(severalUsers)sänderten ihre Avatare",
|
||||
"%(oneUser)schanged their avatar": "%(oneUser)sänderte seinen/ihren Avatar",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s %(time)s",
|
||||
"%(oneUser)sleft and rejoined": "%(oneUser)s ging und trat erneut bei",
|
||||
"A registered account is required for this action": "Für diese Aktion ist ein registrierter Account notwendig",
|
||||
"Access Token:": "Zugangs-Token:",
|
||||
"Always show message timestamps": "Immer Nachrichten-Zeitstempel anzeigen",
|
||||
"Authentication": "Authentifikation",
|
||||
"An error has occurred.": "Ein Fehler passierte.",
|
||||
"Confirm password": "Passwort bestätigen",
|
||||
"Current password": "Aktuelles Passwort",
|
||||
"Email": "E-Mail",
|
||||
"Interface Language": "Oberflächen-Sprache",
|
||||
"Logged in as:": "Angemeldet als:",
|
||||
"matrix-react-sdk version:": "Version von matrix-react-sdk:",
|
||||
"New passwords don't match": "Neue Passwörter nicht gleich",
|
||||
"olm version:": "Version von olm:",
|
||||
"Passwords can't be empty": "Passwörter dürfen nicht leer sein",
|
||||
"Registration required": "Registrierung benötigt",
|
||||
"Report it": "Melde es",
|
||||
"riot-web version:": "Version von riot-web:",
|
||||
"Scroll to bottom of page": "Zum Ende der Seite springen",
|
||||
"Show timestamps in 12 hour format (e.g. 2:30pm)": "Zeige Zeitstempel im 12-Stunden format",
|
||||
"to tag as %(tagName)s": "um als \"%(tagName)s\" zu markieren",
|
||||
"Email address": "E-Mail-Adresse",
|
||||
"Error decrypting attachment": "Fehler beim Entschlüsseln des Anhangs",
|
||||
"Mute": "Stummschalten",
|
||||
"Operation failed": "Aktion fehlgeschlagen",
|
||||
"You need to enter a user name.": "Du musst einen Benutzernamen eingeben.",
|
||||
"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.": "Eine Änderung des Passworts setzt derzeit alle Schlüssel für die E2E-Verschlüsselung auf allen verwendeten Geräten zurück. Bereits verschlüsselte Chat-Inhalte sind somit nur noch lesbar, wenn du zunächst alle Schlüssel exportierst und später wieder importierst. Wir arbeiten an einer Verbesserung dieser momentan noch notwendigen Vorgehensweise.",
|
||||
"Unmute": "Stummschalten aufheben"
|
||||
}
|
677
src/i18n/strings/en_EN.json
Normal file
677
src/i18n/strings/en_EN.json
Normal file
|
@ -0,0 +1,677 @@
|
|||
{
|
||||
"af":"Afrikaans",
|
||||
"ar-ae":"Arabic (U.A.E.)",
|
||||
"ar-bh":"Arabic (Bahrain)",
|
||||
"ar-dz":"Arabic (Algeria)",
|
||||
"ar-eg":"Arabic (Egypt)",
|
||||
"ar-iq":"Arabic (Iraq)",
|
||||
"ar-jo":"Arabic (Jordan)",
|
||||
"ar-kw":"Arabic (Kuwait)",
|
||||
"ar-lb":"Arabic (Lebanon)",
|
||||
"ar-ly":"Arabic (Libya)",
|
||||
"ar-ma":"Arabic (Morocco)",
|
||||
"ar-om":"Arabic (Oman)",
|
||||
"ar-qa":"Arabic (Qatar)",
|
||||
"ar-sa":"Arabic (Saudi Arabia)",
|
||||
"ar-sy":"Arabic (Syria)",
|
||||
"ar-tn":"Arabic (Tunisia)",
|
||||
"ar-ye":"Arabic (Yemen)",
|
||||
"be":"Belarusian",
|
||||
"bg":"Bulgarian",
|
||||
"ca":"Catalan",
|
||||
"cs":"Czech",
|
||||
"da":"Danish",
|
||||
"de-at":"German (Austria)",
|
||||
"de-ch":"German (Switzerland)",
|
||||
"de":"German",
|
||||
"de-li":"German (Liechtenstein)",
|
||||
"de-lu":"German (Luxembourg)",
|
||||
"el":"Greek",
|
||||
"en-au":"English (Australia)",
|
||||
"en-bz":"English (Belize)",
|
||||
"en-ca":"English (Canada)",
|
||||
"en":"English",
|
||||
"en-gb":"English (United Kingdom)",
|
||||
"en-ie":"English (Ireland)",
|
||||
"en-jm":"English (Jamaica)",
|
||||
"en-nz":"English (New Zealand)",
|
||||
"en-tt":"English (Trinidad)",
|
||||
"en-us":"English (United States)",
|
||||
"en-za":"English (South Africa)",
|
||||
"es-ar":"Spanish (Argentina)",
|
||||
"es-bo":"Spanish (Bolivia)",
|
||||
"es-cl":"Spanish (Chile)",
|
||||
"es-co":"Spanish (Colombia)",
|
||||
"es-cr":"Spanish (Costa Rica)",
|
||||
"es-do":"Spanish (Dominican Republic)",
|
||||
"es-ec":"Spanish (Ecuador)",
|
||||
"es-gt":"Spanish (Guatemala)",
|
||||
"es-hn":"Spanish (Honduras)",
|
||||
"es-mx":"Spanish (Mexico)",
|
||||
"es-ni":"Spanish (Nicaragua)",
|
||||
"es-pa":"Spanish (Panama)",
|
||||
"es-pe":"Spanish (Peru)",
|
||||
"es-pr":"Spanish (Puerto Rico)",
|
||||
"es-py":"Spanish (Paraguay)",
|
||||
"es":"Spanish (Spain)",
|
||||
"es-sv":"Spanish (El Salvador)",
|
||||
"es-uy":"Spanish (Uruguay)",
|
||||
"es-ve":"Spanish (Venezuela)",
|
||||
"et":"Estonian",
|
||||
"eu":"Basque (Basque)",
|
||||
"fa":"Farsi",
|
||||
"fi":"Finnish",
|
||||
"fo":"Faeroese",
|
||||
"fr-be":"French (Belgium)",
|
||||
"fr-ca":"French (Canada)",
|
||||
"fr-ch":"French (Switzerland)",
|
||||
"fr":"French",
|
||||
"fr-lu":"French (Luxembourg)",
|
||||
"ga":"Irish",
|
||||
"gd":"Gaelic (Scotland)",
|
||||
"he":"Hebrew",
|
||||
"hi":"Hindi",
|
||||
"hr":"Croatian",
|
||||
"hu":"Hungarian",
|
||||
"id":"Indonesian",
|
||||
"is":"Icelandic",
|
||||
"it-ch":"Italian (Switzerland)",
|
||||
"it":"Italian",
|
||||
"ja":"Japanese",
|
||||
"ji":"Yiddish",
|
||||
"ko":"Korean",
|
||||
"lt":"Lithuanian",
|
||||
"lv":"Latvian",
|
||||
"mk":"Macedonian (FYROM)",
|
||||
"ms":"Malaysian",
|
||||
"mt":"Maltese",
|
||||
"nl-be":"Dutch (Belgium)",
|
||||
"nl":"Dutch",
|
||||
"no":"Norwegian",
|
||||
"pl":"Polish",
|
||||
"pt-br":"Brazilian Portuguese",
|
||||
"pt":"Portuguese",
|
||||
"rm":"Rhaeto-Romanic",
|
||||
"ro-mo":"Romanian (Republic of Moldova)",
|
||||
"ro":"Romanian",
|
||||
"ru-mo":"Russian (Republic of Moldova)",
|
||||
"ru":"Russian",
|
||||
"sb":"Sorbian",
|
||||
"sk":"Slovak",
|
||||
"sl":"Slovenian",
|
||||
"sq":"Albanian",
|
||||
"sr":"Serbian",
|
||||
"sv-fi":"Swedish (Finland)",
|
||||
"sv":"Swedish",
|
||||
"sx":"Sutu",
|
||||
"sz":"Sami (Lappish)",
|
||||
"th":"Thai",
|
||||
"tn":"Tswana",
|
||||
"tr":"Turkish",
|
||||
"ts":"Tsonga",
|
||||
"uk":"Ukrainian",
|
||||
"ur":"Urdu",
|
||||
"ve":"Venda",
|
||||
"vi":"Vietnamese",
|
||||
"xh":"Xhosa",
|
||||
"zh-cn":"Chinese (PRC)",
|
||||
"zh-hk":"Chinese (Hong Kong SAR)",
|
||||
"zh-sg":"Chinese (Singapore)",
|
||||
"zh-tw":"Chinese (Taiwan)",
|
||||
"zu":"Zulu",
|
||||
"A registered account is required for this action": "A registered account is required for this action",
|
||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains",
|
||||
"accept": "accept",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
|
||||
"Account": "Account",
|
||||
"Access Token:": "Access Token:",
|
||||
"Add email address": "Add email address",
|
||||
"Add phone number": "Add phone number",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Advanced",
|
||||
"Algorithm": "Algorithm",
|
||||
"Always show message timestamps": "Always show message timestamps",
|
||||
"Authentication": "Authentication",
|
||||
"all room members": "all room members",
|
||||
"all room members, from the point they are invited": "all room members, from the point they are invited",
|
||||
"all room members, from the point they joined": "all room members, from the point they joined",
|
||||
"an address": "an address",
|
||||
"and": "and",
|
||||
"%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others",
|
||||
"%(items)s and one other": "%(items)s and one other",
|
||||
"%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s",
|
||||
"and %(overflowCount)s others...": "and %(overflowCount)s others...",
|
||||
"and one other...": "and one other...",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
|
||||
"%(names)s and one other are typing": "%(names)s and one other are typing",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing",
|
||||
"An email has been sent to": "An email has been sent to",
|
||||
"A new password must be entered.": "A new password must be entered.",
|
||||
"%(senderName)s answered the call.": "%(senderName)s answered the call.",
|
||||
"anyone": "anyone",
|
||||
"An error has occurred.": "An error has occurred.",
|
||||
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
|
||||
"Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
|
||||
"Are you sure?": "Are you sure?",
|
||||
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
||||
"Are you sure you want upload the following files?": "Are you sure you want upload the following files?",
|
||||
"Attachment": "Attachment",
|
||||
"Autoplay GIFs and videos": "Autoplay GIFs and videos",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.",
|
||||
"Ban": "Ban",
|
||||
"Banned users": "Banned users",
|
||||
"Bans user with given id": "Bans user with given id",
|
||||
"Blacklisted": "Blacklisted",
|
||||
"Bug Report": "Bug Report",
|
||||
"Bulk Options": "Bulk Options",
|
||||
"Call Timeout": "Call Timeout",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s",
|
||||
"Can't load user settings": "Can't load user settings",
|
||||
"Change Password": "Change Password",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s changed the room name to %(roomName)s.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room",
|
||||
"Changes your display nickname": "Changes your display nickname",
|
||||
"changing room on a RoomView is not supported": "changing room on a RoomView is not supported",
|
||||
"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.": "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.",
|
||||
"Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key",
|
||||
"Clear Cache and Reload": "Clear Cache and Reload",
|
||||
"Clear Cache": "Clear Cache",
|
||||
"Click here": "Click here",
|
||||
"Click here to fix": "Click here to fix",
|
||||
"Click to mute audio": "Click to mute audio",
|
||||
"Click to mute video": "Click to mute video",
|
||||
"click to reveal": "click to reveal",
|
||||
"Click to unmute video": "Click to unmute video",
|
||||
"Click to unmute audio": "Click to unmute audio",
|
||||
"Command error": "Command error",
|
||||
"Commands": "Commands",
|
||||
"Conference call failed": "Conference call failed",
|
||||
"Conference calling is in development and may not be reliable": "Conference calling is in development and may not be reliable",
|
||||
"Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms",
|
||||
"Conference calls are not supported in this client": "Conference calls are not supported in this client",
|
||||
"Confirm password": "Confirm password",
|
||||
"Confirm your new password": "Confirm your new password",
|
||||
"Continue": "Continue",
|
||||
"Could not connect to the integration server": "Could not connect to the integration server",
|
||||
"Create an account": "Create an account",
|
||||
"Create Room": "Create Room",
|
||||
"Cryptography": "Cryptography",
|
||||
"Current password": "Current password",
|
||||
"Curve25519 identity key": "Curve25519 identity key",
|
||||
"/ddg is not a command": "/ddg is not a command",
|
||||
"Deactivate Account": "Deactivate Account",
|
||||
"Deactivate my account": "Deactivate my account",
|
||||
"decline": "decline",
|
||||
"Decrypt %(text)s": "Decrypt %(text)s",
|
||||
"Decryption error": "Decryption error",
|
||||
"Delete": "Delete",
|
||||
"demote": "demote",
|
||||
"Deops user with given id": "Deops user with given id",
|
||||
"Default": "Default",
|
||||
"Device ID": "Device ID",
|
||||
"Devices": "Devices",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
|
||||
"Direct Chat": "Direct Chat",
|
||||
"Direct chats": "Direct chats",
|
||||
"Disable inline URL previews by default": "Disable inline URL previews by default",
|
||||
"Disinvite": "Disinvite",
|
||||
"Display name": "Display name",
|
||||
"Displays action": "Displays action",
|
||||
"Don't send typing notifications": "Don't send typing notifications",
|
||||
"Download %(text)s": "Download %(text)s",
|
||||
"Drop here %(toAction)s": "Drop here %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Drop here to tag %(section)s",
|
||||
"Ed25519 fingerprint": "Ed25519 fingerprint",
|
||||
"Email": "Email",
|
||||
"Email address": "Email address",
|
||||
"Email, name or matrix ID": "Email, name or matrix ID",
|
||||
"Emoji": "Emoji",
|
||||
"Enable encryption": "Enable encryption",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
|
||||
"Encrypted room": "Encrypted room",
|
||||
"%(senderName)s ended the call.": "%(senderName)s ended the call.",
|
||||
"End-to-end encryption information": "End-to-end encryption information",
|
||||
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
|
||||
"Enter Code": "Enter Code",
|
||||
"Error": "Error",
|
||||
"Error decrypting attachment": "Error decrypting attachment",
|
||||
"Event information": "Event information",
|
||||
"Existing Call": "Existing Call",
|
||||
"Export E2E room keys": "Export E2E room keys",
|
||||
"Failed to ban user": "Failed to ban user",
|
||||
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
|
||||
"Failed to change power level": "Failed to change power level",
|
||||
"Failed to delete device": "Failed to delete device",
|
||||
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
||||
"Failed to join room": "Failed to join room",
|
||||
"Failed to join the room": "Failed to join the room",
|
||||
"Failed to kick": "Failed to kick",
|
||||
"Failed to leave room": "Failed to leave room",
|
||||
"Failed to load timeline position": "Failed to load timeline position",
|
||||
"Failed to lookup current room": "Failed to lookup current room",
|
||||
"Failed to mute user": "Failed to mute user",
|
||||
"Failed to reject invite": "Failed to reject invite",
|
||||
"Failed to reject invitation": "Failed to reject invitation",
|
||||
"Failed to save settings": "Failed to save settings",
|
||||
"Failed to send email": "Failed to send email",
|
||||
"Failed to send request.": "Failed to send request.",
|
||||
"Failed to set avatar.": "Failed to set avatar.",
|
||||
"Failed to set display name": "Failed to set display name",
|
||||
"Failed to set up conference call": "Failed to set up conference call",
|
||||
"Failed to toggle moderator status": "Failed to toggle moderator status",
|
||||
"Failed to unban": "Failed to unban",
|
||||
"Failed to upload file": "Failed to upload file",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
|
||||
"Failure to create room": "Failure to create room",
|
||||
"Favourite": "Favourite",
|
||||
"favourite": "favourite",
|
||||
"Favourites": "Favourites",
|
||||
"Fill screen": "Fill screen",
|
||||
"Filter room members": "Filter room members",
|
||||
"Forget room": "Forget room",
|
||||
"Forgot your password?": "Forgot your password?",
|
||||
"For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.",
|
||||
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.",
|
||||
"Found a bug?": "Found a bug?",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s",
|
||||
"Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Guest users can't create new rooms. Please register to create room and start a chat",
|
||||
"Guest users can't upload files. Please register to upload": "Guest users can't upload files. Please register to upload",
|
||||
"Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.",
|
||||
"Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.",
|
||||
"had": "had",
|
||||
"Hangup": "Hangup",
|
||||
"Hide read receipts": "Hide read receipts",
|
||||
"Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar",
|
||||
"Historical": "Historical",
|
||||
"Homeserver is": "Homeserver is",
|
||||
"Identity Server is": "Identity Server is",
|
||||
"I have verified my email address": "I have verified my email address",
|
||||
"Import E2E room keys": "Import E2E room keys",
|
||||
"Incorrect verification code": "Incorrect verification code",
|
||||
"Interface Language": "Interface Language",
|
||||
"Invalid alias format": "Invalid alias format",
|
||||
"Invalid address format": "Invalid address format",
|
||||
"Invalid Email Address": "Invalid Email Address",
|
||||
"Invalid file%(extra)s": "Invalid file%(extra)s",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.",
|
||||
"Invite new room members": "Invite new room members",
|
||||
"Invites": "Invites",
|
||||
"Invites user with given id to current room": "Invites user with given id to current room",
|
||||
"is a": "is a",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
|
||||
"%(displayName)s is typing": "%(displayName)s is typing",
|
||||
"I want to sign in with": "I want to sign in with",
|
||||
"Join Room": "Join Room",
|
||||
"joined and left": "joined and left",
|
||||
"joined": "joined",
|
||||
"%(targetName)s joined the room.": "%(targetName)s joined the room.",
|
||||
"Joins room with given alias": "Joins room with given alias",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.",
|
||||
"Kick": "Kick",
|
||||
"Kicks user with given id": "Kicks user with given id",
|
||||
"Labs": "Labs",
|
||||
"Leave room": "Leave room",
|
||||
"left and rejoined": "left and rejoined",
|
||||
"left": "left",
|
||||
"%(targetName)s left the room.": "%(targetName)s left the room.",
|
||||
"Level": "Level",
|
||||
"Local addresses for this room:": "Local addresses for this room:",
|
||||
"Logged in as:": "Logged in as:",
|
||||
"Login as guest": "Login as guest",
|
||||
"Logout": "Logout",
|
||||
"Low priority": "Low priority",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to",
|
||||
"Manage Integrations": "Manage Integrations",
|
||||
"Markdown is disabled": "Markdown is disabled",
|
||||
"Markdown is enabled": "Markdown is enabled",
|
||||
"matrix-react-sdk version:": "matrix-react-sdk version:",
|
||||
"Members only": "Members only",
|
||||
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
||||
"Missing room_id in request": "Missing room_id in request",
|
||||
"Missing user_id in request": "Missing user_id in request",
|
||||
"Mobile phone number": "Mobile phone number",
|
||||
"Moderator": "Moderator",
|
||||
"Must be viewing a room": "Must be viewing a room",
|
||||
"Mute": "Mute",
|
||||
"my Matrix ID": "my Matrix ID",
|
||||
"Name": "Name",
|
||||
"Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device",
|
||||
"Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)",
|
||||
"New Composer & Autocomplete": "New Composer & Autocomplete",
|
||||
"New password": "New password",
|
||||
"New passwords don't match": "New passwords don't match",
|
||||
"New passwords must match each other.": "New passwords must match each other.",
|
||||
"none": "none",
|
||||
"not set": "not set",
|
||||
"not specified": "not specified",
|
||||
"Notifications": "Notifications",
|
||||
"(not supported by this browser)": "(not supported by this browser)",
|
||||
"<not supported>": "<not supported>",
|
||||
"NOT verified": "NOT verified",
|
||||
"No devices with registered encryption keys": "No devices with registered encryption keys",
|
||||
"No more results": "No more results",
|
||||
"No results": "No results",
|
||||
"No users have specific privileges in this room": "No users have specific privileges in this room",
|
||||
"OK": "OK",
|
||||
"olm version:": "olm version:",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
|
||||
"Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below",
|
||||
"Only people who have been invited": "Only people who have been invited",
|
||||
"Operation failed": "Operation failed",
|
||||
"Password": "Password",
|
||||
"Passwords can't be empty": "Passwords can't be empty",
|
||||
"People": "People",
|
||||
"Permissions": "Permissions",
|
||||
"Phone": "Phone",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.",
|
||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.",
|
||||
"Please Register": "Please Register",
|
||||
"Power level must be positive integer.": "Power level must be positive integer.",
|
||||
"Press": "Press",
|
||||
"Privacy warning": "Privacy warning",
|
||||
"Privileged Users": "Privileged Users",
|
||||
"Profile": "Profile",
|
||||
"Reason": "Reason",
|
||||
"Revoke Moderator": "Revoke Moderator",
|
||||
"Refer a friend to Riot:": "Refer a friend to Riot:",
|
||||
"Registration required": "Registration required",
|
||||
"rejected": "rejected",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.",
|
||||
"Reject invitation": "Reject invitation",
|
||||
"Remove Contact Information?": "Remove Contact Information?",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.",
|
||||
"Remove": "Remove",
|
||||
"Remove %(threePid)s?": "Remove %(threePid)s?",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s requested a VoIP conference.",
|
||||
"Report it": "Report it",
|
||||
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.",
|
||||
"restore": "restore",
|
||||
"Return to app": "Return to app",
|
||||
"Return to login screen": "Return to login screen",
|
||||
"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",
|
||||
"riot-web version:": "riot-web version:",
|
||||
"Room %(roomId)s not visible": "Room %(roomId)s not visible",
|
||||
"Room Colour": "Room Colour",
|
||||
"Room name (optional)": "Room name (optional)",
|
||||
"Rooms": "Rooms",
|
||||
"Scroll to bottom of page": "Scroll to bottom of page",
|
||||
"Scroll to unread messages": "Scroll to unread messages",
|
||||
"Search": "Search",
|
||||
"Search failed": "Search failed",
|
||||
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
||||
"Send a message (unencrypted)": "Send a message (unencrypted)",
|
||||
"Send an encrypted message": "Send an encrypted message",
|
||||
"Sender device information": "Sender device information",
|
||||
"Send Invites": "Send Invites",
|
||||
"Send Reset Email": "Send Reset Email",
|
||||
"sent an image": "sent an image",
|
||||
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.",
|
||||
"sent a video": "sent a video",
|
||||
"Server error": "Server error",
|
||||
"Server may be unavailable or overloaded": "Server may be unavailable or overloaded",
|
||||
"Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(",
|
||||
"Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big",
|
||||
"Server may be unavailable, overloaded, or you hit a bug": "Server may be unavailable, overloaded, or you hit a bug",
|
||||
"Server unavailable, overloaded, or something else went wrong": "Server unavailable, overloaded, or something else went wrong",
|
||||
"Session ID": "Session ID",
|
||||
"%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.",
|
||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.",
|
||||
"Settings": "Settings",
|
||||
"Show panel": "Show panel",
|
||||
"Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)",
|
||||
"Signed Out": "Signed Out",
|
||||
"Sign in": "Sign in",
|
||||
"Sign out": "Sign out",
|
||||
"since the point in time of selecting this option": "since the point in time of selecting this option",
|
||||
"since they joined": "since they joined",
|
||||
"since they were invited": "since they were invited",
|
||||
"Some of your messages have not been sent": "Some of your messages have not been sent",
|
||||
"Someone": "Someone",
|
||||
"Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ",
|
||||
"Start a chat": "Start a chat",
|
||||
"Start Chat": "Start Chat",
|
||||
"Submit": "Submit",
|
||||
"Success": "Success",
|
||||
"tag as %(tagName)s": "tag as %(tagName)s",
|
||||
"tag direct chat": "tag direct chat",
|
||||
"The default role for new room members is": "The default role for new room members is",
|
||||
"The main address for this room is": "The main address for this room is",
|
||||
"This action cannot be performed by a guest user. Please register to be able to do this": "This action cannot be performed by a guest user. Please register to be able to do this",
|
||||
"This email address is already in use": "This email address is already in use",
|
||||
"This email address was not found": "This email address was not found",
|
||||
"%(actionVerb)s this person?": "%(actionVerb)s this person?",
|
||||
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
|
||||
"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",
|
||||
"The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload",
|
||||
"The remote side failed to pick up": "The remote side failed to pick up",
|
||||
"This room has no local addresses": "This room has no local addresses",
|
||||
"This room is not recognised.": "This room is not recognised.",
|
||||
"This room is private or inaccessible to guests. You may be able to join if you register": "This room is private or inaccessible to guests. You may be able to join if you register",
|
||||
"These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways",
|
||||
"The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
|
||||
"This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address",
|
||||
"this invitation?": "this invitation?",
|
||||
"This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled",
|
||||
"This phone number is already in use": "This phone number is already in use",
|
||||
"This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
|
||||
"This room's internal ID is": "This room's internal ID is",
|
||||
"times": "times",
|
||||
"To ban users": "To ban users",
|
||||
"to browse the directory": "to browse the directory",
|
||||
"To configure the room": "To configure the room",
|
||||
"to demote": "to demote",
|
||||
"to favourite": "to favourite",
|
||||
"To invite users into the room": "To invite users into the room",
|
||||
"to join the discussion": "to join the discussion",
|
||||
"To kick users": "To kick users",
|
||||
"To link to a room it must have": "To link to a room it must have",
|
||||
"to make a room or": "to make a room or",
|
||||
"To redact other users' messages": "To redact other users' messages",
|
||||
"To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account",
|
||||
"to restore": "to restore",
|
||||
"To send events of type": "To send events of type",
|
||||
"To send messages": "To send messages",
|
||||
"to start a chat with someone": "to start a chat with someone",
|
||||
"to tag as %(tagName)s": "to tag as %(tagName)s",
|
||||
"to tag direct chat": "to tag direct chat",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
|
||||
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question",
|
||||
"Tried to load a specific point in this room's timeline, but was unable to find it": "Tried to load a specific point in this room's timeline, but was unable to find it",
|
||||
"Turn Markdown off": "Turn Markdown off",
|
||||
"Turn Markdown on": "Turn Markdown on",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).",
|
||||
"Unable to add email address": "Unable to add email address",
|
||||
"Unable to remove contact information": "Unable to remove contact information",
|
||||
"Unable to restore previous session": "Unable to restore previous session",
|
||||
"Unable to verify email address.": "Unable to verify email address.",
|
||||
"Unban": "Unban",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.",
|
||||
"Unable to capture screen": "Unable to capture screen",
|
||||
"Unable to enable Notifications": "Unable to enable Notifications",
|
||||
"Unable to load device list": "Unable to load device list",
|
||||
"Unencrypted room": "Unencrypted room",
|
||||
"unencrypted": "unencrypted",
|
||||
"Unknown command": "Unknown command",
|
||||
"unknown device": "unknown device",
|
||||
"unknown error code": "unknown error code",
|
||||
"Unknown room %(roomId)s": "Unknown room %(roomId)s",
|
||||
"unknown": "unknown",
|
||||
"Unmute": "Unmute",
|
||||
"uploaded a file": "uploaded a file",
|
||||
"Upload avatar": "Upload avatar",
|
||||
"Upload Failed": "Upload Failed",
|
||||
"Upload Files": "Upload Files",
|
||||
"Upload file": "Upload file",
|
||||
"Usage": "Usage",
|
||||
"Use with caution": "Use with caution",
|
||||
"User ID": "User ID",
|
||||
"User Interface": "User Interface",
|
||||
"User name": "User name",
|
||||
"Users": "Users",
|
||||
"User": "User",
|
||||
"Verification Pending": "Verification Pending",
|
||||
"Verification": "Verification",
|
||||
"verified": "verified",
|
||||
"Video call": "Video call",
|
||||
"Voice call": "Voice call",
|
||||
"VoIP conference finished.": "VoIP conference finished.",
|
||||
"VoIP conference started.": "VoIP conference started.",
|
||||
"VoIP is unsupported": "VoIP is unsupported",
|
||||
"(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
|
||||
"Warning!": "Warning!",
|
||||
"Who can access this room?": "Who can access this room?",
|
||||
"Who can read history?": "Who can read history?",
|
||||
"Who would you like to add to this room?": "Who would you like to add to this room?",
|
||||
"Who would you like to communicate with?": "Who would you like to communicate with?",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.",
|
||||
"Would you like to": "Would you like to",
|
||||
"You are already in a call": "You are already in a call",
|
||||
"You're not in any rooms yet! Press": "You're not in any rooms yet! Press",
|
||||
"You are trying to access %(roomName)s": "You are trying to access %(roomName)s",
|
||||
"You cannot place a call with yourself": "You cannot place a call with yourself",
|
||||
"You cannot place VoIP calls in this browser": "You cannot place VoIP calls in this browser",
|
||||
"You do not have permission to post to this room": "You do not have permission to post to this room",
|
||||
"You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s",
|
||||
"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": "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",
|
||||
"You have no visible notifications": "You have no visible notifications",
|
||||
"you must be a": "you must be a",
|
||||
"You need to be able to invite users to do that.": "You need to be able to invite users to do that.",
|
||||
"You need to be logged in.": "You need to be logged in.",
|
||||
"You need to enter a user name.": "You need to enter a user name.",
|
||||
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Your email address does not appear to be associated with a Matrix ID on this Homeserver",
|
||||
"Your password has been reset": "Your password has been reset",
|
||||
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them",
|
||||
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
||||
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
||||
"You should not yet trust it to secure data": "You should not yet trust it to secure data",
|
||||
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself",
|
||||
"Sun": "Sun",
|
||||
"Mon": "Mon",
|
||||
"Tue": "Tue",
|
||||
"Wed": "Wed",
|
||||
"Thu": "Thu",
|
||||
"Fri": "Fri",
|
||||
"Sat": "Sat",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Feb",
|
||||
"Mar": "Mar",
|
||||
"Apr": "Apr",
|
||||
"May": "May",
|
||||
"Jun": "Jun",
|
||||
"Jul": "Jul",
|
||||
"Aug": "Aug",
|
||||
"Sep": "Sep",
|
||||
"Oct": "Oct",
|
||||
"Nov": "Nov",
|
||||
"Dec": "Dec",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
|
||||
"Set a display name:": "Set a display name:",
|
||||
"Upload an avatar:": "Upload an avatar:",
|
||||
"This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.",
|
||||
"Missing password.": "Missing password.",
|
||||
"Passwords don't match.": "Passwords don't match.",
|
||||
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).",
|
||||
"This doesn't look like a valid email address.": "This doesn't look like a valid email address.",
|
||||
"This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.",
|
||||
"An unknown error occurred.": "An unknown error occurred.",
|
||||
"I already have an account": "I already have an account",
|
||||
"An error occured: %(error_string)s": "An error occured: %(error_string)s",
|
||||
"Topic": "Topic",
|
||||
"Make Moderator": "Make Moderator",
|
||||
"Make this room private": "Make this room private",
|
||||
"Share message history with new users": "Share message history with new users",
|
||||
"Encrypt room": "Encrypt room",
|
||||
"There are no visible files in this room": "There are no visible files in this room",
|
||||
"Room": "Room",
|
||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||
"Auto-complete": "Auto-complete",
|
||||
"Resend all": "Resend all",
|
||||
"(~%(searchCount)s results)": "(~%(searchCount)s results)",
|
||||
"Cancel": "Cancel",
|
||||
"cancel all": "cancel all",
|
||||
"or": "or",
|
||||
"now. You can also select individual messages to resend or cancel.": "now. You can also select individual messages to resend or cancel.",
|
||||
"Active call": "Active call",
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
"Wednesday": "Wednesday",
|
||||
"Thursday": "Thursday",
|
||||
"Friday": "Friday",
|
||||
"Saturday": "Saturday",
|
||||
"Sunday": "Sunday",
|
||||
"bold": "bold",
|
||||
"italic": "italic",
|
||||
"strike": "strike",
|
||||
"underline": "underline",
|
||||
"code":"code",
|
||||
"quote":"quote",
|
||||
"bullet":"bullet",
|
||||
"numbullet":"numbullet",
|
||||
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times",
|
||||
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times",
|
||||
"%(severalUsers)sjoined": "%(severalUsers)sjoined",
|
||||
"%(oneUser)sjoined": "%(oneUser)sjoined",
|
||||
"%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times",
|
||||
"%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times",
|
||||
"%(severalUsers)sleft": "%(severalUsers)sleft",
|
||||
"%(oneUser)sleft": "%(oneUser)sleft",
|
||||
"%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times",
|
||||
"%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times",
|
||||
"%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left",
|
||||
"%(oneUser)sjoined and left": "%(oneUser)sjoined and left",
|
||||
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times",
|
||||
"%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times",
|
||||
"%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined",
|
||||
"%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined",
|
||||
"%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times",
|
||||
"%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times",
|
||||
"%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations",
|
||||
"%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation",
|
||||
"%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times",
|
||||
"%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times",
|
||||
"%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn",
|
||||
"%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn",
|
||||
"were invited %(repeats)s times": "were invited %(repeats)s times",
|
||||
"was invited %(repeats)s times": "was invited %(repeats)s times",
|
||||
"were invited": "were invited",
|
||||
"was invited": "was invited",
|
||||
"were banned %(repeats)s times": "were banned %(repeats)s times",
|
||||
"was banned %(repeats)s times": "was banned %(repeats)s times",
|
||||
"were banned": "were banned",
|
||||
"was banned": "was banned",
|
||||
"were unbanned %(repeats)s times": "were unbanned %(repeats)s times",
|
||||
"was unbanned %(repeats)s times": "was unbanned %(repeats)s times",
|
||||
"were unbanned": "were unbanned",
|
||||
"was unbanned": "was unbanned",
|
||||
"were kicked %(repeats)s times": "were kicked %(repeats)s times",
|
||||
"was kicked %(repeats)s times": "was kicked %(repeats)s times",
|
||||
"were kicked": "were kicked",
|
||||
"was kicked": "was kicked",
|
||||
"%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times",
|
||||
"%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times",
|
||||
"%(severalUsers)schanged their name": "%(severalUsers)schanged their name",
|
||||
"%(oneUser)schanged their name": "%(oneUser)schanged their name",
|
||||
"%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times",
|
||||
"%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times",
|
||||
"%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar",
|
||||
"%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar",
|
||||
"Please select the destination room for this message": "Please select the destination room for this message"
|
||||
}
|
382
src/i18n/strings/fr.json
Normal file
382
src/i18n/strings/fr.json
Normal file
|
@ -0,0 +1,382 @@
|
|||
{
|
||||
"af": "Afrikaans",
|
||||
"ar-ae": "Arabic (U.A.E.)",
|
||||
"ar-bh": "Arabic (Bahrain)",
|
||||
"ar-dz": "Arabic (Algeria)",
|
||||
"ar-eg": "Arabic (Egypt)",
|
||||
"ar-iq": "Arabic (Iraq)",
|
||||
"ar-jo": "Arabic (Jordan)",
|
||||
"ar-kw": "Arabic (Kuwait)",
|
||||
"ar-lb": "Arabic (Lebanon)",
|
||||
"ar-ly": "Arabic (Libya)",
|
||||
"ar-ma": "Arabic (Morocco)",
|
||||
"ar-om": "Arabic (Oman)",
|
||||
"ar-qa": "Arabic (Qatar)",
|
||||
"ar-sa": "Arabic (Saudi Arabia)",
|
||||
"ar-sy": "Arabic (Syria)",
|
||||
"ar-tn": "Arabic (Tunisia)",
|
||||
"ar-ye": "Arabic (Yemen)",
|
||||
"be": "Belarusian",
|
||||
"bg": "Bulgarian",
|
||||
"ca": "Catalan",
|
||||
"cs": "Czech",
|
||||
"da": "Danish",
|
||||
"de-at": "German (Austria)",
|
||||
"de-ch": "German (Switzerland)",
|
||||
"de": "German",
|
||||
"de-li": "German (Liechtenstein)",
|
||||
"de-lu": "German (Luxembourg)",
|
||||
"el": "Greek",
|
||||
"en-au": "English (Australia)",
|
||||
"en-bz": "English (Belize)",
|
||||
"en-ca": "English (Canada)",
|
||||
"en": "English",
|
||||
"en-gb": "English (United Kingdom)",
|
||||
"en-ie": "English (Ireland)",
|
||||
"en-jm": "English (Jamaica)",
|
||||
"en-nz": "English (New Zealand)",
|
||||
"en-tt": "English (Trinidad)",
|
||||
"en-us": "English (United States)",
|
||||
"en-za": "English (South Africa)",
|
||||
"es-ar": "Spanish (Argentina)",
|
||||
"es-bo": "Spanish (Bolivia)",
|
||||
"es-cl": "Spanish (Chile)",
|
||||
"es-co": "Spanish (Colombia)",
|
||||
"es-cr": "Spanish (Costa Rica)",
|
||||
"es-do": "Spanish (Dominican Republic)",
|
||||
"es-ec": "Spanish (Ecuador)",
|
||||
"es-gt": "Spanish (Guatemala)",
|
||||
"es-hn": "Spanish (Honduras)",
|
||||
"es-mx": "Spanish (Mexico)",
|
||||
"es-ni": "Spanish (Nicaragua)",
|
||||
"es-pa": "Spanish (Panama)",
|
||||
"es-pe": "Spanish (Peru)",
|
||||
"es-pr": "Spanish (Puerto Rico)",
|
||||
"es-py": "Spanish (Paraguay)",
|
||||
"es": "Spanish (Spain)",
|
||||
"es-sv": "Spanish (El Salvador)",
|
||||
"es-uy": "Spanish (Uruguay)",
|
||||
"es-ve": "Spanish (Venezuela)",
|
||||
"et": "Estonian",
|
||||
"eu": "Basque (Basque)",
|
||||
"fa": "Farsi",
|
||||
"fi": "Finnish",
|
||||
"fo": "Faeroese",
|
||||
"fr-be": "French (Belgium)",
|
||||
"fr-ca": "French (Canada)",
|
||||
"fr-ch": "French (Switzerland)",
|
||||
"fr": "French",
|
||||
"fr-lu": "French (Luxembourg)",
|
||||
"ga": "Irish",
|
||||
"gd": "Gaelic (Scotland)",
|
||||
"he": "Hebrew",
|
||||
"hi": "Hindi",
|
||||
"hr": "Croatian",
|
||||
"hu": "Hungarian",
|
||||
"id": "Indonesian",
|
||||
"is": "Icelandic",
|
||||
"it-ch": "Italian (Switzerland)",
|
||||
"it": "Italian",
|
||||
"ja": "Japanese",
|
||||
"ji": "Yiddish",
|
||||
"ko": "Korean (Johab)",
|
||||
"lt": "Lithuanian",
|
||||
"lv": "Latvian",
|
||||
"mk": "Macedonian (FYROM)",
|
||||
"ms": "Malaysian",
|
||||
"mt": "Maltese",
|
||||
"nl-be": "Dutch (Belgium)",
|
||||
"nl": "Dutch",
|
||||
"no": "Norwegian",
|
||||
"pl": "Polish",
|
||||
"pt-br": "Brazilian Portuguese",
|
||||
"pt": "Portuguese",
|
||||
"rm": "Rhaeto-Romanic",
|
||||
"ro-mo": "Romanian (Republic of Moldova)",
|
||||
"ro": "Romanian",
|
||||
"ru-mo": "Russian (Republic of Moldova)",
|
||||
"ru": "Russian",
|
||||
"sb": "Sorbian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sq": "Albanian",
|
||||
"sr": "Serbian (Latin)",
|
||||
"sv-fi": "Swedish (Finland)",
|
||||
"sv": "Swedish",
|
||||
"sx": "Sutu",
|
||||
"sz": "Sami (Lappish)",
|
||||
"th": "Thai",
|
||||
"tn": "Tswana",
|
||||
"tr": "Turkish",
|
||||
"ts": "Tsonga",
|
||||
"uk": "Ukrainian",
|
||||
"ur": "Urdu",
|
||||
"ve": "Venda",
|
||||
"vi": "Vietnamese",
|
||||
"xh": "Xhosa",
|
||||
"zh-cn": "Chinese (PRC)",
|
||||
"zh-hk": "Chinese (Hong Kong SAR)",
|
||||
"zh-sg": "Chinese (Singapore)",
|
||||
"zh-tw": "Chinese (Taiwan)",
|
||||
"zu": "Zulu",
|
||||
"anyone": "anyone",
|
||||
"Direct Chat": "Conversation Directe",
|
||||
"Direct chats": "Conversations directes",
|
||||
"Disable inline URL previews by default": "Désactiver l’aperçu des URLs",
|
||||
"Disinvite": "Désinviter",
|
||||
"Display name": "Nom d'affichage",
|
||||
"Displays action": "Affiche l'action",
|
||||
"Don't send typing notifications": "Ne pas envoyer les notifications de saisie",
|
||||
"Download %(text)s": "Télécharger %(text)s",
|
||||
"Drop here %(toAction)s": "Déposer ici %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s",
|
||||
"Ed25519 fingerprint": "Empreinte Ed25519",
|
||||
"Email Address": "Adresse e-mail",
|
||||
"Email, name or matrix ID": "E-mail, nom or identifiant Matrix",
|
||||
"Emoji": "Emoticône",
|
||||
"Enable encryption": "Activer l'encryption",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Les messages encryptés ne seront pas visibles dans les clients qui n’implémentent pas encore l’encryption",
|
||||
"Encrypted room": "Salon encrypté",
|
||||
"%(senderName)s ended the call.": "%(senderName)s a terminé l’appel.",
|
||||
"End-to-end encryption information": "Information sur l'encryption bout-en-bout",
|
||||
"End-to-end encryption is in beta and may not be reliable": "L’encryption bout-en-bout est en béta et risque de ne pas être fiable",
|
||||
"Enter Code": "Entrer le code",
|
||||
"Error": "Erreur",
|
||||
"Event information": "Event information",
|
||||
"Existing Call": "Existing Call",
|
||||
"Export E2E room keys": "Export E2E room keys",
|
||||
"Failed to ban user": "Failed to ban user",
|
||||
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
|
||||
"Failed to change power level": "Failed to change power level",
|
||||
"Failed to delete device": "Failed to delete device",
|
||||
"Failed to forget room %(errCode)s": "Echec lors de l'oublie du salon %(errCode)s",
|
||||
"Please Register": "Veuillez vous enregistrer",
|
||||
"Remove": "Supprimer",
|
||||
"was banned": "banned",
|
||||
"was invited": "invited",
|
||||
"was kicked": "kicked",
|
||||
"was unbanned": "unbanned",
|
||||
"Monday": "Lundi",
|
||||
"Tuesday": "Mardi",
|
||||
"Wednesday": "Mercredi",
|
||||
"Thursday": "Jeudi",
|
||||
"Friday": "Vendredi",
|
||||
"Saturday": "Samedi",
|
||||
"Sunday": "Dimanche",
|
||||
"bold": "gras",
|
||||
"italic": "italique",
|
||||
"strike": "barré",
|
||||
"underline": "souligné",
|
||||
"Favourite": "Favoris",
|
||||
"Notifications": "Notifications",
|
||||
"Settings": "Paramètres",
|
||||
"Failed to join the room": "Échec de l'adhésion au salon",
|
||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Un message texte a été envoyé à +%(msisdn)s. Merci d'entrer le code de vérification qu'il contient",
|
||||
"accept": "Accepter",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s a accepté une invitation.",
|
||||
"Account": "Compte",
|
||||
"Add email address": "Ajouter une adresse e-mail",
|
||||
"Add phone number": "Ajouter un numéro de téléphone",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avancé",
|
||||
"Algorithm": "Algorithme",
|
||||
"all room members": "tous les membres du salon",
|
||||
"all room members, from the point they are invited": "tous les membres du salon, depuis le moment où ils ont été invités",
|
||||
"all room members, from the point they joined": "tous les membres du salon, depuis le moment où ils ont joint",
|
||||
"an address": "une adresse",
|
||||
"and": "et",
|
||||
"%(items)s and %(remaining)s others": "%(items)s et %(remaining)s autres",
|
||||
"%(items)s and one other": "%(items)s et un autre",
|
||||
"%(items)s and %(lastItem)s": "%(items)s et %(lastItem)s",
|
||||
"and %(overflowCount)s others...": "et %(overflowCount)s autres...",
|
||||
"and one other...": "et un autre...",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s et %(lastPerson)s sont en train de taper",
|
||||
"%(names)s and one other are typing": "%(names)s et un autre sont en train de taper",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s et %(count)s d'autres sont en train de taper",
|
||||
"An email has been sent to": "Un e-mail a été envoyé à",
|
||||
"A new password must be entered.": "Un nouveau mot de passe doit être entré.",
|
||||
"Anyone who knows the room's link, apart from guests": "Tout ceux qui connaissent le lien du salon, à part les invités",
|
||||
"Anyone who knows the room's link, including guests": "Tout ceux qui connaissent le lien du salon, y compris les invités",
|
||||
"Are you sure?": "Êtes-vous sûr ?",
|
||||
"Are you sure you want to reject the invitation?": "Êtes-vous sûr de vouloir rejeter l'invitation ?",
|
||||
"Are you sure you want upload the following files?": "Êtes-vous sûr de vouloir télécharger les fichiers suivants ?",
|
||||
"Attachment": "Pièce jointe",
|
||||
"Autoplay GIFs and videos": "Jouer automatiquement les GIFs et vidéos",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s a banni %(targetName)s.",
|
||||
"Ban": "Bannir",
|
||||
"Banned users": "Utilisateurs bannis",
|
||||
"Bans user with given id": "Utilisateurs bannis avec un identifiant donné",
|
||||
"Blacklisted": "Sur liste noire",
|
||||
"Bug Report": "Rapport d'erreur",
|
||||
"Call Timeout": "Délai d’appel expiré",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Connexion au Home Server impossible - merci de vérifier votre connectivité et que le %(urlStart)s certificat SSL de votre Home Server %(urlEnd)s est de confiance",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Impossible de se connecter au homeserver en HTTP si l'URL dans la barre de votre explorateur est en HTTPS. Utilisez HTTPS ou %(urlStart)s activez le support des scripts non-vérifiés %(urlEnd)s",
|
||||
"Can't load user settings": "Impossible de charger les paramètres utilisateur",
|
||||
"Change Password": "Changer le mot de passe",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s a changé son nom d’affichage de %(oldDisplayName)s en %(displayName)s.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s a changé sa photo de profil.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s a changé le niveau de pouvoir de %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s a changé le nom du salon en %(roomName)s.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s a changé le sujet du salon en \"%(topic)s\".",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Les changements de visibilité de l’historique de ce salon ne s’appliquent qu’aux messages futurs",
|
||||
"Changes your display nickname": "Change votre nom d'affichage",
|
||||
"Claimed Ed25519 fingerprint key": "Clé empreinte Ed25519 revendiquée",
|
||||
"Clear Cache and Reload": "Vider le cache et recharger",
|
||||
"Clear Cache": "Vider le cache",
|
||||
"Click here": "Cliquer ici",
|
||||
"Click here to fix": "Cliquer ici pour réparer",
|
||||
"Click to mute audio": "Cliquer pour couper le son",
|
||||
"Click to mute video": "Cliquer ici pour couper la vidéo",
|
||||
"click to reveal": "cliquer pour dévoiler",
|
||||
"Click to unmute video": "Cliquer pour rétablir la vidéo",
|
||||
"Click to unmute audio": "Cliquer pour rétablir le son",
|
||||
"Command error": "Erreur de commande",
|
||||
"Commands": "Commandes",
|
||||
"Conference call failed": "Échec de la conférence",
|
||||
"Conference calling is in development and may not be reliable": "Les appels en conférence sont encore en développement et sont potentiellement peu fiables",
|
||||
"Conference calls are not supported in encrypted rooms": "Les appels en conférence ne sont pas supportés dans les salons encryptés",
|
||||
"Conference calls are not supported in this client": "Les appels en conférence ne sont pas supportés avec ce client",
|
||||
"Confirm password": "Confirmer le mot de passe",
|
||||
"Confirm your new password": "Confirmer votre nouveau mot de passe",
|
||||
"Continue": "Continuer",
|
||||
"Could not connect to the integration server": "Impossible de se connecter au serveur d'intégration",
|
||||
"Create an account": "Créer un compte",
|
||||
"Create Room": "Créer un salon",
|
||||
"Cryptography": "Encryption",
|
||||
"Current password": "Mot de passe actuel",
|
||||
"Curve25519 identity key": "Clé d’identité Curve25519",
|
||||
"/ddg is not a command": "/ddg n'est pas une commande",
|
||||
"Deactivate Account": "Désactiver le compte",
|
||||
"Deactivate my account": "Désactiver mon compte",
|
||||
"decline": "décliner",
|
||||
"Decrypt %(text)s": "Décrypter %(text)s",
|
||||
"Decryption error": "Erreur de décryptage",
|
||||
"Delete": "Supprimer",
|
||||
"demote": "rétrograder",
|
||||
"Deops user with given id": "Retire les privilèges d’opérateur d’un utilisateur avec un ID donné",
|
||||
"Device ID": "ID de l'appareil",
|
||||
"Devices": "Appareils",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne seront pas capables de décrypter l’historique précédant leur adhésion au salon",
|
||||
"ml": "Malayalam",
|
||||
"Failed to join room": "Échec lors de l’adhésion au salon",
|
||||
"Failed to kick": "Échec lors de l'expulsion",
|
||||
"Failed to leave room": "Échec du départ",
|
||||
"Failed to load timeline position": "Erreur lors du chargement de la position dans la chronologie",
|
||||
"Failed to lookup current room": "Échec lors de la recherche du salon actuel",
|
||||
"Failed to mute user": "Échec lors de l'interruption de l'utilisateur",
|
||||
"Failed to reject invite": "Échec lors du rejet de l'invitation",
|
||||
"Failed to reject invitation": "Échec lors du rejet de l'invitation",
|
||||
"Failed to save settings": "Échec lors de la sauvegarde des paramètres",
|
||||
"Failed to send email": "Échec lors de l’envoi de l’e-mail",
|
||||
"Failed to send request.": "Échec lors de l’envoi de la requête.",
|
||||
"Failed to set display name": "Échec lors de l'enregistrement du nom d'affichage",
|
||||
"Failed to set up conference call": "Échec lors de l’établissement de l’appel",
|
||||
"Failed to toggle moderator status": "Échec lors de l’établissement du statut de modérateur",
|
||||
"A registered account is required for this action": "Il est nécessaire d’avoir un compte enregistré pour effectuer cette action",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s a accepté l’invitation de %(displayName)s.",
|
||||
"Access Token:": "Jeton d’accès :",
|
||||
"Always show message timestamps": "Toujours afficher l'heure des messages",
|
||||
"Authentication": "Authentification",
|
||||
"%(senderName)s answered the call.": "%(senderName)s a répondu à l’appel.",
|
||||
"An error has occurred.": "Une erreur est survenue.",
|
||||
"Email": "E-mail",
|
||||
"Failed to send request.": "Erreur lors de l'envoi de la requête.",
|
||||
"Failed to unban": "Échec de l'amnistie",
|
||||
"Failed to upload file": "Échec du téléchargement",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Échec de la vérification de l’adresse e-mail: vérifiez que vous avez bien cliqué sur le lien dans l’e-mail",
|
||||
"Failure to create room": "Échec de la création du salon",
|
||||
"favourite": "favoris",
|
||||
"Favourites": "Favoris",
|
||||
"Fill screen": "Plein écran",
|
||||
"Filter room members": "Filtrer les membres par nom",
|
||||
"Forget room": "Oublier le salon",
|
||||
"Forgot your password?": "Mot de passe perdu ?",
|
||||
"For security, this session has been signed out. Please sign in again.": "Par sécurité, la session a expiré. Merci de vous authentifer à nouveau.",
|
||||
"Found a bug?": "Trouvé un problème ?",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s à %(toPowerLevel)s",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Les utilisateurs invités ne peuvent créer de nouveaux salons. Merci de vous enregistrer pour commencer une discussion",
|
||||
"Guest users can't upload files. Please register to upload": "Les utilisateurs invités ne peuvent telécharger de fichiers. Merci de vous enregistrer pour télécharger",
|
||||
"had": "avait",
|
||||
"Hangup": "Raccrocher",
|
||||
"Hide read receipts": "Cacher les accusés de réception",
|
||||
"Hide Text Formatting Toolbar": "Cacher la barre de formattage de texte",
|
||||
"Historical": "Historique",
|
||||
"Homeserver is": "Le homeserver est",
|
||||
"Identity Server is": "Le serveur d'identité est",
|
||||
"I have verified my email address": "J’ai vérifié mon adresse e-mail",
|
||||
"Import E2E room keys": "Importer les clés d’encryption bout-en-bout",
|
||||
"Incorrect verification code": "Code de vérification incorrect",
|
||||
"Interface Language": "Langue de l'interface",
|
||||
"Invalid alias format": "Format de l'alias invalide",
|
||||
"Invalid address format": "Format d'adresse invalide",
|
||||
"Invalid Email Address": "Adresse e-mail invalide",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s a invité %(targetName)s.",
|
||||
"Invite new room members": "Inviter de nouveaux membres",
|
||||
"Invites": "Invitations",
|
||||
"Invites user with given id to current room": "Inviter l’utilisateur avec un ID donné dans le salon actuel",
|
||||
"is a": "est un",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' n'est pas un format valide pour une adresse",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' n'est pas un format valide pour un alias",
|
||||
"%(displayName)s is typing": "%(displayName)s est en train de taper",
|
||||
"I want to sign in with": "Je veux m'identifier avec",
|
||||
"Join Room": "Rejoindre le salon",
|
||||
"joined and left": "a joint et quitté",
|
||||
"joined": "a joint",
|
||||
"%(targetName)s joined the room.": "%(targetName)s a joint le salon.",
|
||||
"Joins room with given alias": "Joint le salon avec l'alias défini",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a expulsé %(targetName)s.",
|
||||
"Kick": "Expluser",
|
||||
"Kicks user with given id": "Expulse l'utilisateur and l'ID donné",
|
||||
"Labs": "Laboratoire",
|
||||
"Leave room": "Quitter le salon",
|
||||
"left and rejoined": "a quitté et rejoint",
|
||||
"left": "a quitté",
|
||||
"%(targetName)s left the room.": "%(targetName)s a quitté le salon.",
|
||||
"Level": "Niveau",
|
||||
"Local addresses for this room:": "Adresse locale pour ce salon :",
|
||||
"Logged in as:": "Identifié en tant que :",
|
||||
"Login as guest": "Identifié en tant que qu'invité",
|
||||
"Logout": "Se déconnecter",
|
||||
"Low priority": "Priorité basse",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s a rendu l'historique visible de",
|
||||
"Manage Integrations": "Gestion des intégrations",
|
||||
"Markdown is disabled": "Le formatage \"Markdown\" est désactivé",
|
||||
"Markdown is enabled": "Le formatage “Markdown” est activé",
|
||||
"matrix-react-sdk version:": "Version du matrix-react-sdk :",
|
||||
"Members only": "Membres uniquement",
|
||||
"Message not sent due to unknown devices being present": "Message non-envoyé à cause de la présence d’appareils non-vérifiés",
|
||||
"Missing room_id in request": "Absence du room_id dans la requête",
|
||||
"Missing user_id in request": "Absence du user_id dans la requête",
|
||||
"Mobile phone number": "Numéro de téléphone mobile",
|
||||
"Moderator": "Modérateur",
|
||||
"Must be viewing a room": "Doit être en train de visualiser un salon",
|
||||
"my Matrix ID": "mon Matrix ID",
|
||||
"Name": "Nom",
|
||||
"Never send encrypted messages to unverified devices from this device": "Ne jamais envoyer de message encryptés aux appareils non-vérifiés depuis cet appareil",
|
||||
"Never send encrypted messages to unverified devices in this room": "Ne jamais envoyer de message encryptés aux appareils non-vérifiés dans ce salon",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Ne jamais envoyer de message encryptés aux appareils non-vérifiés dans ce salon depuis cet appareil",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "Nouvelle adresse (par ex. #foo:%(localDomain)s)",
|
||||
"New Composer & Autocomplete": "Nouveau compositeur & Autocomplétion",
|
||||
"New password": "Nouveau mot de passe",
|
||||
"New passwords don't match": "Les mots de passe ne correspondent pas",
|
||||
"New passwords must match each other.": "Les nouveaux mots de passe doivent être identiques.",
|
||||
"none": "aucun",
|
||||
"not set": "non défini",
|
||||
"not specified": "non spécifié",
|
||||
"(not supported by this browser)": "(non supporté par cet explorateur)",
|
||||
"<not supported>": "<non supporté>",
|
||||
"NOT verified": "NON vérifié",
|
||||
"No devices with registered encryption keys": "Pas d’appareil avec des clés d’encryption enregistrées",
|
||||
"No more results": "Fin des résultats",
|
||||
"No results": "Pas de résultats",
|
||||
"unknown error code": "Code erreur inconnu",
|
||||
"OK": "OK",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Une fois le chiffrement activé dans un salon il ne peut pas être désactivé (pour le moment)",
|
||||
"Only people who have been invited": "Seul les personnes ayant été invitées",
|
||||
"or": "ou",
|
||||
"Password": "Mot de passe",
|
||||
"Passwords can't be empty": "Le mot de passe ne peut pas être vide",
|
||||
"People": "Personne",
|
||||
"Permissions": "Permissions",
|
||||
"Phone": "Téléphone"
|
||||
}
|
2
src/i18n/strings/ml.json
Normal file
2
src/i18n/strings/ml.json
Normal file
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
4
src/i18n/strings/nl.json
Normal file
4
src/i18n/strings/nl.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"af": "Afrikaans",
|
||||
"ar-ae": "Arabisch (U.A.E.)"
|
||||
}
|
501
src/i18n/strings/pt.json
Normal file
501
src/i18n/strings/pt.json
Normal file
|
@ -0,0 +1,501 @@
|
|||
{
|
||||
"accept": "aceitar",
|
||||
"accepted an invitation": "aceitou um convite",
|
||||
"accepted the invitation for": "aceitou o convite para",
|
||||
"Account": "Conta",
|
||||
"Add email address": "Adicionar endereço de email",
|
||||
"Add phone number": "Adicionar número de telefone",
|
||||
"Admin": "Administrador/a",
|
||||
"Advanced": "Avançado",
|
||||
"Algorithm": "Algoritmo",
|
||||
"all room members, from the point they are invited.": "todos os membros da sala, a partir de quando foram convidados",
|
||||
"all room members, from the point they joined.": "todos os membros da sala, a partir de quando entraram",
|
||||
"all room members": "todos os membros da sala",
|
||||
"an address": "um endereço",
|
||||
"and": "e",
|
||||
"An email has been sent to": "Um email foi enviado para",
|
||||
"A new password must be entered.": "Uma nova senha precisa ser informada.",
|
||||
"answered the call.": "respondeu à chamada.",
|
||||
"Anyone who knows the room's link, apart from guests": "Qualquer pessoa que tenha o link da sala, exceto visitantes",
|
||||
"Anyone who knows the room's link, including guests": "Qualquer pessoa que tenha o link da sala, incluindo visitantes",
|
||||
"Are you sure you want to leave the room?": "Você tem certeza que deseja sair da sala?",
|
||||
"Are you sure you want to reject the invitation?": "Você tem certeza que deseja rejeitar este convite?",
|
||||
"Are you sure you want upload the following files?": "Você tem certeza que deseja enviar os seguintes arquivos?",
|
||||
"banned": "baniu",
|
||||
"Banned users": "Usuárias/os banidas/os",
|
||||
"Bans user with given id": "Banir usuários com o identificador informado",
|
||||
"Blacklisted": "Bloqueado",
|
||||
"Bug Report": "Repotar problemas de funcionamento",
|
||||
"Bulk Options": "Opcões de Batelada",
|
||||
"Can't load user settings": "Não é possível carregar configurações de usuário",
|
||||
"changed avatar": "mudou sua imagem de perfil (avatar)",
|
||||
"changed name": "mudou seu nome",
|
||||
"changed their display name from": "mudou seu nome para",
|
||||
"changed their profile picture": "alterou sua foto de perfil",
|
||||
"changed the power level of": "mudou o nível de permissões de",
|
||||
"changed the room name to": "mudou o nome da sala para",
|
||||
"changed the topic to": "mudou o tópico para",
|
||||
"Changes to who can read history will only apply to future messages in this room": "As mudanças sobre quem pode ler o histórico da sala só serão aplicadas às mensagens futuras nesta sala",
|
||||
"Changes your display nickname": "Troca o seu apelido",
|
||||
"Claimed Ed25519 fingerprint key": "Chave reivindicada da Impressão Digital Ed25519",
|
||||
"Clear Cache and Reload": "Limpar Memória Cache e Recarregar",
|
||||
"Clear Cache": "Limpar Memória Cache",
|
||||
"Click here": "Clique aqui",
|
||||
"Click here to fix": "Clique aqui para resolver isso",
|
||||
"Commands": "Comandos",
|
||||
"Confirm your new password": "Confirme a nova senha",
|
||||
"Continue": "Continuar",
|
||||
"Could not connect to the integration server": "Não foi possível conectar ao servidor de integrações",
|
||||
"Create an account": "Criar uma conta",
|
||||
"Create a new account": "Criar uma conta",
|
||||
"Create Room": "Criar Sala",
|
||||
"Cryptography": "Criptografia",
|
||||
"Curve25519 identity key": "Chave de Indetificação Curve25519",
|
||||
"Deactivate Account": "Desativar conta",
|
||||
"Deactivate my account": "Desativar minha conta",
|
||||
"decline": "rejeitar",
|
||||
"Decryption error": "Erro de descriptografia",
|
||||
"Default": "Padrão",
|
||||
"demote": "reduzir prioridade",
|
||||
"Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
|
||||
"Device ID": "Identificador do dispositivo",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
|
||||
"Direct Chat": "Conversa pessoal",
|
||||
"Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
|
||||
"Display name": "Nome",
|
||||
"Displays action": "Visualizar atividades",
|
||||
"Ed25519 fingerprint": "Impressão Digital Ed25519",
|
||||
"Email Address": "endereço de email",
|
||||
"Email, name or matrix ID": "Email, nome ou ID matrix",
|
||||
"Emoji": "Emoji",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Mensagens criptografadas não serão visíveis em clientes que ainda não implementaram criptografia",
|
||||
"Encrypted room": "Sala criptografada",
|
||||
"Encryption is enabled in this room": "Criptografia está habilitada nesta sala",
|
||||
"Encryption is not enabled in this room": "Criptografia não está habilitada nesta sala",
|
||||
"ended the call.": "chamada encerrada.",
|
||||
"End-to-end encryption information": "Informação criptografada ponta-a-ponta",
|
||||
"End-to-end encryption is in beta and may not be reliable": "A criptografia ponta a ponta está em estágio beta e não deve ser totalmente confiável",
|
||||
"Error": "Erro",
|
||||
"Event information": "Informação do evento",
|
||||
"Export E2E room keys": "Exportar chaves ponta-a-ponta da sala",
|
||||
"Failed to change password. Is your password correct?": "Não foi possível modificar a senha. A senha informada está correta?",
|
||||
"Failed to forget room": "Não foi possível esquecer a sala",
|
||||
"Failed to leave room": "Falha ao tentar deixar a sala",
|
||||
"Failed to reject invitation": "Falha ao tentar rejeitar convite",
|
||||
"Failed to send email: ": "Falha ao tentar enviar email",
|
||||
"Failed to set avatar.": "Falha ao tentar definir foto do perfil.",
|
||||
"Failed to unban": "Não foi possível desfazer o banimento",
|
||||
"Failed to upload file": "Falha ao enviar o arquivo",
|
||||
"favourite": "favoritar",
|
||||
"Favourite": "Favorito",
|
||||
"Favourites": "Favoritos",
|
||||
"Filter room members": "Filtrar membros de sala",
|
||||
"Forget room": "Esquecer sala",
|
||||
"Forgot your password?": "Esqueceu sua senha?",
|
||||
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Por segurança, deslogar irá remover qualquer chave de criptografia ponta-a-ponta deste navegador. Caso deseje descriptografar o histórico das suas conversas E2E em sessões Riot futuras, por favor exporte as chaves da sala para sua garantia.",
|
||||
"For security, this session has been signed out. Please sign in again.": "Por questões de segurança, esta sessão foi encerrada. Por gentileza conecte-se novamente.",
|
||||
"Found a bug?": "Encontrou um problema de funcionamento do sistema?",
|
||||
"Guests cannot join this room even if explicitly invited.": "Visitantes não podem entrar nesta sala, mesmo se forem explicitamente convidadas/os.",
|
||||
"Guests can't set avatars. Please register.": "Convidados não podem definir uma foto do perfil. Por favor, registre-se.",
|
||||
"Guests can't use labs features. Please register.": "Convidados não podem usar as funcionalidades de laboratório (lab), por gentileza se registre.",
|
||||
"Guest users can't upload files. Please register to upload": "Usuários não podem fazer envio de arquivos. Por favor se cadastre para enviar arquivos",
|
||||
"had": "teve",
|
||||
"Hangup": "Desligar",
|
||||
"Historical": "Histórico",
|
||||
"Homeserver is": "Servidor padrão é",
|
||||
"Identity Server is": "O servidor de identificação é",
|
||||
"I have verified my email address": "Eu verifiquei o meu endereço de email",
|
||||
"Import E2E room keys": "Importar chave de criptografia ponta-a-ponta (E2E) da sala",
|
||||
"Invalid Email Address": "Endereço de email inválido",
|
||||
"invited": "convidou",
|
||||
"Invite new room members": "Convidar novo membros para sala",
|
||||
"Invites": "Convidar",
|
||||
"Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala",
|
||||
"is a": "é um(a)",
|
||||
"I want to sign in with": "Quero entrar",
|
||||
"joined and left": "entrou e saiu",
|
||||
"joined": "entrou",
|
||||
"joined the room": "entrou na sala",
|
||||
"Joins room with given alias": "Entra na sala com o nome informado",
|
||||
"Kicks user with given id": "Remove usuário com o identificador informado",
|
||||
"Labs": "Laboratório",
|
||||
"Leave room": "Sair da sala",
|
||||
"left and rejoined": "saiu e entrou novamente",
|
||||
"left": "saiu",
|
||||
"left the room": "saiu da sala",
|
||||
"Logged in as": "Logado como",
|
||||
"Login as guest": "Entrar como visitante",
|
||||
"Logout": "Sair",
|
||||
"Low priority": "Baixa prioridade",
|
||||
"made future room history visible to": "deixou o histórico futuro da sala visível para",
|
||||
"Manage Integrations": "Gerenciar integrações",
|
||||
"Members only": "Apenas integrantes da sala",
|
||||
"Mobile phone number": "Telefone celular",
|
||||
"Moderator": "Moderador/a",
|
||||
"my Matrix ID": "com meu ID do Matrix",
|
||||
"Name": "Nome",
|
||||
"Never send encrypted messages to unverified devices from this device": "Nunca envie mensagens criptografada para um dispositivo não verificado a partir deste dispositivo",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala a partir deste dispositivo",
|
||||
"New password": "Nova senha",
|
||||
"New passwords must match each other.": "As novas senhas informadas precisam ser idênticas.",
|
||||
"none": "nenhum",
|
||||
"Notifications": "Notificações",
|
||||
" (not supported by this browser)": "não suportado por este navegador",
|
||||
"<not supported>": "<não suportado>",
|
||||
"NOT verified": "NÃO verificado",
|
||||
"No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
|
||||
"olm version: ": "Versão do olm: ",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
|
||||
"Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
|
||||
"Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
|
||||
"or": "ou",
|
||||
"other": "outro",
|
||||
"others": "outros",
|
||||
"Password": "Senha",
|
||||
"People": "Pessoas",
|
||||
"Permissions": "Permissões",
|
||||
"Phone": "Telefone",
|
||||
"placed a": "iniciou uma",
|
||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Por favor verifique seu email e clique no link enviado. Quando finalizar este processo, clique para continuar.",
|
||||
"Please Register": "Por favor, cadastre-se",
|
||||
"Privacy warning": "Alerta sobre privacidade",
|
||||
"Privileged Users": "Usuárias/os privilegiadas/os",
|
||||
"Profile": "Perfil",
|
||||
"Refer a friend to Riot:": "Indicar um amigo para participar",
|
||||
"rejected": "recusou",
|
||||
"rejected the invitation.": "rejeitou o convite.",
|
||||
"Reject invitation": "Rejeitar convite",
|
||||
"Remove Contact Information?": "Remover informação de contato?",
|
||||
"removed their display name": "removeu seu nome",
|
||||
"removed their profile picture": "removeu sua foto de perfil",
|
||||
"Remove": "Remover",
|
||||
"requested a VoIP conference": "requisitou uma conferência VoIP",
|
||||
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado.",
|
||||
"restore": "restaurar",
|
||||
"Return to app": "Retornar ao aplicativo",
|
||||
"Return to login screen": "Retornar à tela de login",
|
||||
"Room Colour": "Cores da sala",
|
||||
"Room name (optional)": "Título da Sala (opcional)",
|
||||
"Rooms": "Salas",
|
||||
"Scroll to unread messages": "Rolar para baixo para ver as mensagens não lidas",
|
||||
"Searches DuckDuckGo for results": "Buscar por resultados no buscador DuckDuckGo",
|
||||
"Send a message (unencrypted)": "Enviar uma mensagem",
|
||||
"Send an encrypted message": "Enviar uma mensagem criptografada",
|
||||
"Sender device information": "Informação do dispositivo emissor",
|
||||
"Send Invites": "Enviar convites",
|
||||
"Send Reset Email": "Enviar email para redefinição de senha",
|
||||
"sent an image": "enviou uma imagem",
|
||||
"sent an invitation to": "enviou um convite para",
|
||||
"sent a video": "enviou um vídeo",
|
||||
"Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado",
|
||||
"Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução",
|
||||
"Session ID": "Identificador de sessão",
|
||||
"set a profile picture": "colocou uma foto de perfil",
|
||||
"set their display name to": "configurou seu nome para",
|
||||
"Settings": "Configurações",
|
||||
"Show panel": "Mostrar painel",
|
||||
"Signed Out": "Deslogar",
|
||||
"Sign in": "Entrar",
|
||||
"Sign out": "Sair",
|
||||
"since the point in time of selecting this option": "a partir do momento em que você selecionar esta opção",
|
||||
"since they joined": "desde que entraram na sala",
|
||||
"since they were invited": "desde que foram convidadas/os",
|
||||
"Someone": "Alguém",
|
||||
"Sorry, this homeserver is using a login which is not recognised ": "Desculpe, o servidor padrão está usando um login de acesso que não é válido ",
|
||||
"Start a chat": "Começar uma conversa",
|
||||
"Start Chat": "Começar conversa",
|
||||
"Success": "Sucesso",
|
||||
"tag as": "etiquetar como",
|
||||
"tag direct chat": "definir como conversa pessoal",
|
||||
"The default role for new room members is": "O papel padrão para novas/os integrantes da sala é",
|
||||
"The email address linked to your account must be entered.": "O endereço de email relacionado a sua conta precisa ser informado.",
|
||||
"their invitations": "seus convites",
|
||||
"their invitation": "seu convite",
|
||||
"These are experimental features that may break in unexpected ways. Use with caution": "Estes são recursos experimentais que podem não funcionar corretamente. Use com cuidado.",
|
||||
"The visibility of existing history will be unchanged": "A visibilidade do histórico atual não será alterada",
|
||||
"This doesn't appear to be a valid email address": "Este não aparenta ser um endereço de email válido",
|
||||
"this invitation?": "este convite?",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Esta é uma pré visualização desta sala. As interações com a sala estão desabilitadas",
|
||||
"This room is not accessible by remote Matrix servers": "Esta sala não é acessível para servidores Matrix remotos",
|
||||
"This room's internal ID is": "O ID interno desta sala é",
|
||||
"times": "vezes",
|
||||
"To ban users": "Para banir usuárias/os",
|
||||
"To configure the room": "Para poder configurar a sala",
|
||||
"To invite users into the room": "Para convidar usuárias/os para esta sala",
|
||||
"to join the discussion": "para se juntar à conversa",
|
||||
"To kick users": "Para poder remover pessoas da sala",
|
||||
"To link to a room it must have": "Para fazer um link para uma sala, ela deve ter",
|
||||
"To redact messages": "Para poder apagar mensagens",
|
||||
"To reset your password, enter the email address linked to your account": "Para redefinir sua senha, entre com o email da sua conta",
|
||||
"To send events of type": "Para enviar eventos do tipo",
|
||||
"To send messages": "Para enviar mensagens",
|
||||
"turned on end-to-end encryption (algorithm": "acionou a encriptação ponta-a-ponta (algoritmo",
|
||||
"Unable to add email address": "Não foi possível adicionar endereço de email",
|
||||
"Unable to remove contact information": "Não foi possível remover informação de contato",
|
||||
"Unable to verify email address.": "Não foi possível verificar o endereço de email.",
|
||||
"Unban": "Desfazer banimento",
|
||||
"Unencrypted room": "Sala não criptografada",
|
||||
"unencrypted": "não criptografado",
|
||||
"unknown device": "dispositivo desconhecido",
|
||||
"unknown error code": "código de erro desconhecido",
|
||||
"unknown": "desconhecido",
|
||||
"Upload avatar": "Enviar icone de perfil de usuário",
|
||||
"uploaded a file": "enviou um arquivo",
|
||||
"Upload Files": "Enviar arquivos",
|
||||
"Upload file": "Enviar arquivo",
|
||||
"User ID": "Identificador de Usuário",
|
||||
"User Interface": "Interface de usuário",
|
||||
"User name": "Nome de usuária/o",
|
||||
"Users": "Usuários",
|
||||
"User": "Usuária/o",
|
||||
"Verification Pending": "Verificação pendente",
|
||||
"Verification": "Verificação",
|
||||
"verified": "verificado",
|
||||
"Video call": "Chamada de vídeo",
|
||||
"Voice call": "Chamada de voz",
|
||||
"VoIP conference finished.": "Conferência VoIP encerrada.",
|
||||
"VoIP conference started.": "Conferência VoIP iniciada.",
|
||||
"(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
|
||||
"Warning!": "Atenção!",
|
||||
"was banned": "banida/o",
|
||||
"was invited": "convidada/o",
|
||||
"was kicked": "retirada/o da sala",
|
||||
"was unbanned": "des-banida/o",
|
||||
"was": "foi",
|
||||
"were": "foram",
|
||||
"Who can access this room?": "Quem pode acessar esta sala?",
|
||||
"Who can read history?": "Quem pode ler o histórico da sala?",
|
||||
"Who would you like to add to this room?": "Quem Você gostaria que fosse adicionado a esta sala?",
|
||||
"Who would you like to communicate with?": "Com quem você gostaria de se comunicar?",
|
||||
"withdrawn": "retirado",
|
||||
"Would you like to": "Você gostaria de",
|
||||
"You are trying to access": "Você está tentando acessar a sala",
|
||||
"You do not have permission to post to this room": "Você não tem permissão de postar nesta sala",
|
||||
"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": "Você foi desconectada/o de todos os dispositivos e portanto não receberá mais notificações no seu celular ou no computador. Para reativar as notificações, entre novamente em cada um dos dispositivos que costuma usar",
|
||||
"You have no visible notifications": "Voce não possui notificações visíveis",
|
||||
"you must be a": "você precisa ser",
|
||||
"Your password has been reset": "Sua senha foi redefinida",
|
||||
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
|
||||
"You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
|
||||
"Sun": "Dom",
|
||||
"Mon": "Seg",
|
||||
"Tue": "Ter",
|
||||
"Wed": "Qua",
|
||||
"Thu": "Qui",
|
||||
"Fri": "Sex",
|
||||
"Sat": "Sáb",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Fev",
|
||||
"Mar": "Mar",
|
||||
"Apr": "Abr",
|
||||
"May": "Mai",
|
||||
"Jun": "Jun",
|
||||
"Jul": "Jul",
|
||||
"Aug": "Ago",
|
||||
"Sep": "Set",
|
||||
"Oct": "Out",
|
||||
"Nov": "Nov",
|
||||
"Dec": "Dez",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s às %(time)s",
|
||||
"%(weekDayName)s %(time)s": "%(weekDayName)s às %(time)s",
|
||||
"en": "Inglês",
|
||||
"pt-br": "Português do Brasil",
|
||||
"de": "Alemão",
|
||||
"da": "Dinamarquês",
|
||||
"ru": "Russo",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s aceitou um convite.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s aceitou o convite para %(displayName)s.",
|
||||
"all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
|
||||
"all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
|
||||
"%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
|
||||
"%(senderName)s answered the call.": "%(senderName)s atendeu à chamada.",
|
||||
"anyone": "qualquer pessoa",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
|
||||
"Call Timeout": "Tempo esgotado. Chamada encerrada",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s alterou sua imagem de perfil.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s.",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s alterou o tópico para \"%(topic)s\".",
|
||||
"click to reveal": "clique para ver",
|
||||
"Conference call failed": "Chamada de conferência falhou",
|
||||
"Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
|
||||
"Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas",
|
||||
"Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador",
|
||||
"/ddg is not a command": "/ddg não é um comando",
|
||||
"Drop here %(toAction)s": "Arraste aqui %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
|
||||
"%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
|
||||
"Existing Call": "Chamada em andamento",
|
||||
"Failed to lookup current room": "Não foi possível buscar na sala atual",
|
||||
"Failed to send email": "Não foi possível enviar email",
|
||||
"Failed to send request.": "Não foi possível mandar requisição.",
|
||||
"Failed to set up conference call": "Não foi possível montar a chamada de conferência",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
|
||||
"Failure to create room": "Não foi possível criar a sala",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.",
|
||||
"%(displayName)s is typing": "%(displayName)s está escrevendo",
|
||||
"%(targetName)s joined the room.": "%(targetName)s entrou na sala.",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
|
||||
"%(targetName)s left the room.": "%(targetName)s saiu da sala.",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
|
||||
"Missing room_id in request": "Faltou o id da sala na requisição",
|
||||
"Missing user_id in request": "Faltou o id de usuário na requisição",
|
||||
"Must be viewing a room": "Tem que estar visualizando uma sala",
|
||||
"New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
|
||||
"(not supported by this browser)": "(não é compatível com este navegador)",
|
||||
"olm version": "versão olm",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.",
|
||||
"Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",
|
||||
"Press": "Aperte",
|
||||
"Reason": "Razão",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s recusou o convite.",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s removeu sua imagem de perfil.",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s está solicitando uma conferência de voz.",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
|
||||
"Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
|
||||
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s enviou uma imagem.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala.",
|
||||
"%(senderName)s set a profile picture.": "%(senderName)s definiu uma imagem de perfil.",
|
||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s definiu seu nome público para %(displayName)s.",
|
||||
"tag as %(tagName)s": "marcar como %(tagName)s",
|
||||
"This email address is already in use": "Este endereço de email já está sendo usado",
|
||||
"This email address was not found": "Este endereço de email não foi encontrado",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
|
||||
"The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
|
||||
"The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
|
||||
"This room is not recognised.": "Esta sala não é reconhecida.",
|
||||
"These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
|
||||
"This phone number is already in use": "Este número de telefone já está sendo usado",
|
||||
"to browse the directory": "para navegar na lista pública de salas",
|
||||
"to demote": "para reduzir prioridade",
|
||||
"to favourite": "para favoritar",
|
||||
"to make a room or": "para criar uma sala ou",
|
||||
"To redact other users' messages": "Para apagar mensagens de outras pessoas",
|
||||
"to restore": "para restaurar",
|
||||
"to start a chat with someone": "para iniciar uma conversa com alguém",
|
||||
"to tag direct chat": "para marcar a conversa como pessoal",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções.",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s).",
|
||||
"Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s desfez o banimento de %(targetName)s.",
|
||||
"Unable to capture screen": "Não foi possível capturar a imagem da tela",
|
||||
"Unable to enable Notifications": "Não foi possível ativar as notificações",
|
||||
"Upload Failed": "O envio falhou",
|
||||
"Usage": "Uso",
|
||||
"Use with caution": "Use com cautela",
|
||||
"VoIP is unsupported": "Chamada de voz não permitida",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s desfez o convite a %(targetName)s's.",
|
||||
"You are already in a call": "Você já está em uma chamada",
|
||||
"You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
|
||||
"You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
|
||||
"You cannot place a call with yourself": "Você não pode iniciar uma chamada",
|
||||
"You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
|
||||
"You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.",
|
||||
"You need to be logged in.": "Você tem que estar logado.",
|
||||
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
|
||||
"Set a display name:": "Defina um nome público para você:",
|
||||
"Upload an avatar:": "Envie uma imagem de perfil para identificar você:",
|
||||
"This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.",
|
||||
"Missing password.": "Faltou a senha.",
|
||||
"Passwords don't match.": "As senhas não conferem.",
|
||||
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "A senha é muito curta (o mínimo é de %(MIN_PASSWORD_LENGTH)s caracteres).",
|
||||
"This doesn't look like a valid email address.": "Este endereço de email não parece ser válido.",
|
||||
"This doesn't look like a valid phone number.": "Este número de telefone não parece ser válido.",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Nomes de usuária/o podem conter apenas letras, números, pontos, hífens e linha inferior (_).",
|
||||
"An unknown error occurred.": "Um erro desconhecido ocorreu.",
|
||||
"I already have an account": "Eu já tenho uma conta",
|
||||
"An error occured: %(error_string)s": "Um erro ocorreu: %(error_string)s",
|
||||
"Topic": "Tópico",
|
||||
"Make this room private": "Tornar esta sala privada",
|
||||
"Share message history with new users": "Compartilhar histórico de mensagens com novas/os usuárias/os",
|
||||
"Encrypt room": "Criptografar esta sala",
|
||||
"There are no visible files in this room": "Não há arquivos públicos nesta sala",
|
||||
"Error changing language": "Erro ao mudar de idioma",
|
||||
"Riot was unable to find the correct Data for the selected Language.": "Não foi possível encontrar os dados para o idioma selecionado.",
|
||||
"Connectivity to the server has been lost.": "A conexão com o servidor foi perdida. Verifique sua conexão de internet.",
|
||||
"Sent messages will be stored until your connection has returned.": "Imagens enviadas ficarão armazenadas até que sua conexão seja reestabelecida.",
|
||||
"Auto-complete": "Autocompletar",
|
||||
"Resend all": "Reenviar todas as mensagens",
|
||||
"cancel all": "cancelar todas",
|
||||
"now. You can also select individual messages to resend or cancel.": "agora. Você também pode escolher mensagens individuais e definir se vai reenviar ou cancelar o envio.",
|
||||
"Active call": "Chamada ativa",
|
||||
"af": "Afrikaans",
|
||||
"ar-ae": "Árabe (U.A.E.)",
|
||||
"ar-bh": "Árabe (Bahrain)",
|
||||
"ar-dz": "Árabe (Algéria)",
|
||||
"ar-eg": "Árabe (Egipto)",
|
||||
"ar-tn": "Árabe (Tunisia)",
|
||||
"be": "Bielorusso",
|
||||
"bg": "Búlgaro",
|
||||
"ca": "Catalão",
|
||||
"cs": "Checo",
|
||||
"de-at": "Alemao (Austria)",
|
||||
"el": "Grego",
|
||||
"en-au": "Inglês (Austrália)",
|
||||
"en-ca": "Inglês (Canadá)",
|
||||
"en-ie": "Inglês (Irlanda)",
|
||||
"en-nz": "Inglês (Nova Zelândia)",
|
||||
"en-us": "Inglês (Estados Unidos)",
|
||||
"es-ar": "Espanhol (Argentina)",
|
||||
"es-mx": "Espanhol (Mexico)",
|
||||
"es-py": "Espanhol (Paraguai)",
|
||||
"es": "Espanhol (Espanha)",
|
||||
"et": "Estoniano",
|
||||
"fa": "Farsi",
|
||||
"fi": "Finlandês",
|
||||
"fr-be": "Francês (Bélgica)",
|
||||
"fr-ca": "Francês (Canadá)",
|
||||
"fr-ch": "Francês (Suíça)",
|
||||
"fr": "Francês",
|
||||
"ga": "Irlandês",
|
||||
"he": "Hebreu",
|
||||
"hi": "Hindi",
|
||||
"hr": "Croata",
|
||||
"hu": "Húngaro",
|
||||
"id": "Indonésio",
|
||||
"is": "Islandês",
|
||||
"it": "Italiano",
|
||||
"ja": "Japonês",
|
||||
"ji": "iídiche",
|
||||
"lt": "Lituano",
|
||||
"lv": "Letão",
|
||||
"mt": "Maltês",
|
||||
"nl-be": "Holandês (Bélgica)",
|
||||
"nl": "Holandês",
|
||||
"no": "Norueguês",
|
||||
"pl": "Polaco",
|
||||
"pt": "Português",
|
||||
"ro": "Romeno",
|
||||
"sk": "Eslovaco",
|
||||
"sl": "Esloveno",
|
||||
"sq": "Albanês",
|
||||
"sr": "Sérvio (Latim)",
|
||||
"sv": "Sueco",
|
||||
"th": "Tailandês",
|
||||
"tn": "tswana",
|
||||
"tr": "Turco",
|
||||
"ts": "tsonga",
|
||||
"uk": "Ucraniano",
|
||||
"ur": "urdu",
|
||||
"ve": "venda",
|
||||
"vi": "Vietnamita",
|
||||
"xh": "xosa",
|
||||
"zu": "zulu",
|
||||
"Failed to forget room %(errCode)s": "Falha ao esquecer a sala %(errCode)s",
|
||||
"Failed to join the room": "Falha ao entrar na sala",
|
||||
"Sunday": "Domingo",
|
||||
"Monday": "Segunda",
|
||||
"Tuesday": "Terça",
|
||||
"Wednesday": "Quarta",
|
||||
"Thursday": "Quinta",
|
||||
"Friday": "Sexta",
|
||||
"Saturday": "Sábado"
|
||||
}
|
703
src/i18n/strings/pt_BR.json
Normal file
703
src/i18n/strings/pt_BR.json
Normal file
|
@ -0,0 +1,703 @@
|
|||
{
|
||||
"accept": "aceitar",
|
||||
"accepted an invitation": "aceitou um convite",
|
||||
"accepted the invitation for": "aceitou o convite para",
|
||||
"Account": "Conta",
|
||||
"Add email address": "Adicionar endereço de email",
|
||||
"Add phone number": "Adicionar número de telefone",
|
||||
"Admin": "Administrador/a",
|
||||
"Advanced": "Avançado",
|
||||
"Algorithm": "Algoritmo",
|
||||
"all room members, from the point they are invited.": "todos os membros da sala, a partir de quando foram convidados",
|
||||
"all room members, from the point they joined.": "todos os membros da sala, a partir de quando entraram",
|
||||
"all room members": "todas as pessoas da sala",
|
||||
"an address": "um endereço",
|
||||
"and": "e",
|
||||
"An email has been sent to": "Um email foi enviado para",
|
||||
"New passwords don't match": "As novas senhas não conferem",
|
||||
"A new password must be entered.": "Uma nova senha precisa ser informada.",
|
||||
"answered the call.": "respondeu à chamada.",
|
||||
"Anyone who knows the room's link, apart from guests": "Qualquer pessoa que tenha o link da sala, exceto visitantes",
|
||||
"Anyone who knows the room's link, including guests": "Qualquer pessoa que tenha o link da sala, incluindo visitantes",
|
||||
"Are you sure you want to leave the room?": "Você tem certeza que deseja sair da sala?",
|
||||
"Are you sure you want to reject the invitation?": "Você tem certeza que deseja rejeitar este convite?",
|
||||
"Are you sure you want upload the following files?": "Você tem certeza que deseja enviar os seguintes arquivos?",
|
||||
"banned": "baniu",
|
||||
"Banned users": "Usuárias/os banidas/os",
|
||||
"Bans user with given id": "Banir usuários com o identificador informado",
|
||||
"Blacklisted": "Bloqueado",
|
||||
"Bug Report": "Repotar problemas de funcionamento",
|
||||
"Bulk Options": "Opcões de Batelada",
|
||||
"Can't load user settings": "Não é possível carregar configurações de usuário",
|
||||
"changed avatar": "mudou sua imagem de perfil (avatar)",
|
||||
"changed name": "mudou seu nome",
|
||||
"changed their display name from": "mudou seu nome para",
|
||||
"changed their profile picture": "alterou sua foto de perfil",
|
||||
"changed the power level of": "mudou o nível de permissões de",
|
||||
"changed the room name to": "mudou o nome da sala para",
|
||||
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s mudou o tópico para \"%(topic)s\".",
|
||||
"Changes to who can read history will only apply to future messages in this room": "As mudanças sobre quem pode ler o histórico da sala só serão aplicadas às mensagens futuras nesta sala",
|
||||
"Changes your display nickname": "Troca o seu apelido",
|
||||
"Claimed Ed25519 fingerprint key": "Chave reivindicada da Impressão Digital Ed25519",
|
||||
"Clear Cache and Reload": "Limpar Memória Cache e Recarregar",
|
||||
"Clear Cache": "Limpar Memória Cache",
|
||||
"Click here": "Clique aqui",
|
||||
"Click here to fix": "Clique aqui para resolver isso",
|
||||
"Commands": "Comandos",
|
||||
"Confirm password": "Confirme a nova senha",
|
||||
"Confirm your new password": "Confirme a nova senha",
|
||||
"Continue": "Continuar",
|
||||
"Could not connect to the integration server": "Não foi possível conectar ao servidor de integrações",
|
||||
"Create an account": "Criar uma conta",
|
||||
"Create a new account": "Criar uma conta",
|
||||
"Create Room": "Criar Sala",
|
||||
"Cryptography": "Criptografia",
|
||||
"Current password": "Senha atual",
|
||||
"Curve25519 identity key": "Chave de Indetificação Curve25519",
|
||||
"Deactivate Account": "Desativar conta",
|
||||
"Deactivate my account": "Desativar minha conta",
|
||||
"decline": "rejeitar",
|
||||
"Decryption error": "Erro de descriptografia",
|
||||
"Default": "Padrão",
|
||||
"demote": "reduzir prioridade",
|
||||
"Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
|
||||
"Device ID": "Identificador do dispositivo",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
|
||||
"Direct Chat": "Conversa pessoal",
|
||||
"Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
|
||||
"Display name": "Nome",
|
||||
"Displays action": "Visualizar atividades",
|
||||
"Ed25519 fingerprint": "Impressão Digital Ed25519",
|
||||
"Email Address": "endereço de email",
|
||||
"Email, name or matrix ID": "Email, nome ou ID matrix",
|
||||
"Emoji": "Emoji",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Mensagens criptografadas não serão visíveis em clientes que ainda não implementaram criptografia",
|
||||
"Encrypted room": "Sala criptografada",
|
||||
"Encryption is enabled in this room": "Criptografia está habilitada nesta sala",
|
||||
"Encryption is not enabled in this room": "Criptografia não está habilitada nesta sala",
|
||||
"ended the call.": "chamada encerrada.",
|
||||
"End-to-end encryption information": "Informação criptografada ponta-a-ponta",
|
||||
"End-to-end encryption is in beta and may not be reliable": "A criptografia ponta a ponta está em estágio beta e não deve ser totalmente confiável",
|
||||
"Error": "Erro",
|
||||
"Event information": "Informação do evento",
|
||||
"Export E2E room keys": "Exportar chaves ponta-a-ponta da sala",
|
||||
"Failed to change password. Is your password correct?": "Não foi possível modificar a senha. A senha informada está correta?",
|
||||
"Failed to forget room": "Não foi possível esquecer a sala",
|
||||
"Failed to leave room": "Falha ao tentar deixar a sala",
|
||||
"Failed to reject invitation": "Falha ao tentar rejeitar convite",
|
||||
"Failed to send email: ": "Falha ao tentar enviar email",
|
||||
"Failed to set avatar.": "Falha ao tentar definir foto do perfil.",
|
||||
"Failed to unban": "Não foi possível desfazer o banimento",
|
||||
"Failed to upload file": "Falha ao enviar o arquivo",
|
||||
"favourite": "favoritar",
|
||||
"Favourite": "Favorito",
|
||||
"Favourites": "Favoritos",
|
||||
"Filter room members": "Filtrar integrantes da sala",
|
||||
"Forget room": "Esquecer sala",
|
||||
"Forgot your password?": "Esqueceu sua senha?",
|
||||
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Por segurança, deslogar irá remover qualquer chave de criptografia ponta-a-ponta deste navegador. Caso deseje descriptografar o histórico das suas conversas E2E em sessões Riot futuras, por favor exporte as chaves da sala para sua garantia.",
|
||||
"For security, this session has been signed out. Please sign in again.": "Por questões de segurança, esta sessão foi encerrada. Por gentileza conecte-se novamente.",
|
||||
"Found a bug?": "Encontrou um problema de funcionamento do sistema?",
|
||||
"Guests cannot join this room even if explicitly invited.": "Visitantes não podem entrar nesta sala, mesmo se forem explicitamente convidadas/os.",
|
||||
"Guests can't set avatars. Please register.": "Convidados não podem definir uma foto do perfil. Por favor, registre-se.",
|
||||
"Guests can't use labs features. Please register.": "Convidados não podem usar as funcionalidades de laboratório (lab), por gentileza se registre.",
|
||||
"Guest users can't upload files. Please register to upload": "Usuários não podem fazer envio de arquivos. Por favor se cadastre para enviar arquivos",
|
||||
"had": "teve",
|
||||
"Hangup": "Desligar",
|
||||
"Historical": "Histórico",
|
||||
"Homeserver is": "Servidor padrão é",
|
||||
"Identity Server is": "O servidor de identificação é",
|
||||
"I have verified my email address": "Eu verifiquei o meu endereço de email",
|
||||
"Import E2E room keys": "Importar chave de criptografia ponta-a-ponta (E2E) da sala",
|
||||
"Invalid Email Address": "Endereço de email inválido",
|
||||
"invited": "convidou",
|
||||
"Invite new room members": "Convidar novas pessoas para ingressar na sala",
|
||||
"Invites": "Convidar",
|
||||
"Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala",
|
||||
"is a": "é um(a)",
|
||||
"I want to sign in with": "Quero entrar",
|
||||
"joined and left": "entrou e saiu",
|
||||
"joined": "entrou",
|
||||
"joined the room": "entrou na sala",
|
||||
"Joins room with given alias": "Entra na sala com o nome informado",
|
||||
"Kicks user with given id": "Remove usuário com o identificador informado",
|
||||
"Labs": "Laboratório",
|
||||
"Leave room": "Sair da sala",
|
||||
"left and rejoined": "saiu e entrou novamente",
|
||||
"left": "saiu",
|
||||
"left the room": "saiu da sala",
|
||||
"Logged in as": "Logado como",
|
||||
"Login as guest": "Entrar como visitante",
|
||||
"Logout": "Sair",
|
||||
"Low priority": "Baixa prioridade",
|
||||
"made future room history visible to": "deixou o histórico futuro da sala visível para",
|
||||
"Manage Integrations": "Gerenciar integrações",
|
||||
"Members only": "Apenas integrantes da sala",
|
||||
"Mobile phone number": "Telefone celular",
|
||||
"Moderator": "Moderador/a",
|
||||
"my Matrix ID": "com meu ID do Matrix",
|
||||
"Name": "Nome",
|
||||
"Never send encrypted messages to unverified devices from this device": "Nunca envie mensagens criptografada para um dispositivo não verificado a partir deste dispositivo",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala a partir deste dispositivo",
|
||||
"New password": "Nova senha",
|
||||
"New passwords must match each other.": "As novas senhas informadas precisam ser idênticas.",
|
||||
"none": "nenhum",
|
||||
"Notifications": "Notificações",
|
||||
" (not supported by this browser)": "não suportado por este navegador",
|
||||
"<not supported>": "<não suportado>",
|
||||
"NOT verified": "NÃO verificado",
|
||||
"No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
|
||||
"olm version: ": "Versão do olm: ",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
|
||||
"Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
|
||||
"Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
|
||||
"or": "ou",
|
||||
"other": "outro",
|
||||
"others": "outros",
|
||||
"Password": "Senha",
|
||||
"Passwords can't be empty": "As senhas não podem estar em branco",
|
||||
"People": "Pessoas",
|
||||
"Permissions": "Permissões",
|
||||
"Phone": "Telefone",
|
||||
"placed a": "iniciou uma",
|
||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Por favor verifique seu email e clique no link enviado. Quando finalizar este processo, clique para continuar.",
|
||||
"Please Register": "Por favor, cadastre-se",
|
||||
"Privacy warning": "Alerta sobre privacidade",
|
||||
"Privileged Users": "Usuárias/os privilegiadas/os",
|
||||
"Profile": "Perfil",
|
||||
"Refer a friend to Riot:": "Indicar um amigo para participar",
|
||||
"rejected": "recusou",
|
||||
"rejected the invitation.": "rejeitou o convite.",
|
||||
"Reject invitation": "Rejeitar convite",
|
||||
"Remove Contact Information?": "Remover informação de contato?",
|
||||
"removed their display name": "removeu seu nome",
|
||||
"removed their profile picture": "removeu sua foto de perfil",
|
||||
"Remove": "Remover",
|
||||
"requested a VoIP conference": "requisitou uma conferência VoIP",
|
||||
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado.",
|
||||
"restore": "restaurar",
|
||||
"Return to app": "Retornar ao aplicativo",
|
||||
"Return to login screen": "Retornar à tela de login",
|
||||
"Room Colour": "Cores da sala",
|
||||
"Room name (optional)": "Título da Sala (opcional)",
|
||||
"Rooms": "Salas",
|
||||
"Scroll to bottom of page": "Ir para o fim da página",
|
||||
"Scroll to unread messages": "Rolar para baixo para ver as mensagens não lidas",
|
||||
"Searches DuckDuckGo for results": "Buscar por resultados no buscador DuckDuckGo",
|
||||
"Send a message (unencrypted)": "Enviar uma mensagem",
|
||||
"Send an encrypted message": "Enviar uma mensagem criptografada",
|
||||
"Sender device information": "Informação do dispositivo emissor",
|
||||
"Send Invites": "Enviar convites",
|
||||
"Send Reset Email": "Enviar email para redefinição de senha",
|
||||
"sent an image": "enviou uma imagem",
|
||||
"sent an invitation to": "enviou um convite para",
|
||||
"sent a video": "enviou um vídeo",
|
||||
"Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado",
|
||||
"Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução",
|
||||
"Session ID": "Identificador de sessão",
|
||||
"set a profile picture": "colocou uma foto de perfil",
|
||||
"set their display name to": "configurou seu nome para",
|
||||
"Settings": "Configurações",
|
||||
"Show panel": "Mostrar painel",
|
||||
"Signed Out": "Deslogar",
|
||||
"Sign in": "Entrar",
|
||||
"Sign out": "Sair",
|
||||
"since the point in time of selecting this option": "a partir do momento em que você selecionar esta opção",
|
||||
"since they joined": "desde que entraram na sala",
|
||||
"since they were invited": "desde que foram convidadas/os",
|
||||
"Someone": "Alguém",
|
||||
"Sorry, this homeserver is using a login which is not recognised ": "Desculpe, o servidor padrão está usando um login de acesso que não é válido ",
|
||||
"Start a chat": "Começar uma conversa",
|
||||
"Start Chat": "Começar conversa",
|
||||
"Success": "Sucesso",
|
||||
"tag as": "etiquetar como",
|
||||
"tag direct chat": "definir como conversa pessoal",
|
||||
"The default role for new room members is": "O papel padrão para novas/os integrantes da sala é",
|
||||
"The email address linked to your account must be entered.": "O endereço de email relacionado a sua conta precisa ser informado.",
|
||||
"their invitations": "seus convites",
|
||||
"their invitation": "seu convite",
|
||||
"These are experimental features that may break in unexpected ways. Use with caution": "Estes são recursos experimentais que podem não funcionar corretamente. Use com cuidado.",
|
||||
"The visibility of existing history will be unchanged": "A visibilidade do histórico atual não será alterada",
|
||||
"This doesn't appear to be a valid email address": "Este não aparenta ser um endereço de email válido",
|
||||
"this invitation?": "este convite?",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Esta é uma pré visualização desta sala. As interações com a sala estão desabilitadas",
|
||||
"This room is not accessible by remote Matrix servers": "Esta sala não é acessível para servidores Matrix remotos",
|
||||
"This room's internal ID is": "O ID interno desta sala é",
|
||||
"times": "vezes",
|
||||
"To ban users": "Para banir usuárias/os",
|
||||
"To configure the room": "Para poder configurar a sala",
|
||||
"To invite users into the room": "Para convidar usuárias/os para esta sala",
|
||||
"to join the discussion": "para se juntar à conversa",
|
||||
"To kick users": "Para poder remover pessoas da sala",
|
||||
"To link to a room it must have": "Para fazer um link para uma sala, ela deve ter",
|
||||
"To redact messages": "Para poder apagar mensagens",
|
||||
"To reset your password, enter the email address linked to your account": "Para redefinir sua senha, entre com o email da sua conta",
|
||||
"To send events of type": "Para enviar eventos do tipo",
|
||||
"To send messages": "Para enviar mensagens",
|
||||
"turned on end-to-end encryption (algorithm": "acionou a encriptação ponta-a-ponta (algoritmo",
|
||||
"Unable to add email address": "Não foi possível adicionar endereço de email",
|
||||
"Unable to remove contact information": "Não foi possível remover informação de contato",
|
||||
"Unable to verify email address.": "Não foi possível verificar o endereço de email.",
|
||||
"Unban": "Desfazer banimento",
|
||||
"Unencrypted room": "Sala não criptografada",
|
||||
"unencrypted": "não criptografado",
|
||||
"unknown device": "dispositivo desconhecido",
|
||||
"unknown error code": "código de erro desconhecido",
|
||||
"unknown": "desconhecido",
|
||||
"Upload avatar": "Enviar icone de perfil de usuário",
|
||||
"uploaded a file": "enviou um arquivo",
|
||||
"Upload Files": "Enviar arquivos",
|
||||
"Upload file": "Enviar arquivo",
|
||||
"User ID": "Identificador de Usuário",
|
||||
"User Interface": "Interface de usuário",
|
||||
"User name": "Nome de usuária/o",
|
||||
"Users": "Usuários",
|
||||
"User": "Usuária/o",
|
||||
"Verification Pending": "Verificação pendente",
|
||||
"Verification": "Verificação",
|
||||
"verified": "verificado",
|
||||
"Video call": "Chamada de vídeo",
|
||||
"Voice call": "Chamada de voz",
|
||||
"VoIP conference finished.": "Conferência VoIP encerrada.",
|
||||
"VoIP conference started.": "Conferência VoIP iniciada.",
|
||||
"(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
|
||||
"Warning": "Atenção!",
|
||||
"was banned": "banida/o",
|
||||
"was invited": "convidada/o",
|
||||
"was kicked": "retirada/o da sala",
|
||||
"was unbanned": "des-banida/o",
|
||||
"was": "foi",
|
||||
"were": "foram",
|
||||
"Who can access this room?": "Quem pode acessar esta sala?",
|
||||
"Who can read history?": "Quem pode ler o histórico da sala?",
|
||||
"Who would you like to add to this room?": "Quais pessoas você gostaria de adicionar a esta sala?",
|
||||
"Who would you like to communicate with?": "Com quem você gostaria de se comunicar?",
|
||||
"withdrawn": "retirado",
|
||||
"Would you like to": "Você gostaria de",
|
||||
"You are trying to access": "Você está tentando acessar a sala",
|
||||
"You do not have permission to post to this room": "Você não tem permissão de postar nesta sala",
|
||||
"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": "Você foi desconectada/o de todos os dispositivos e portanto não receberá mais notificações no seu celular ou no computador. Para reativar as notificações, entre novamente em cada um dos dispositivos que costuma usar",
|
||||
"You have no visible notifications": "Voce não possui notificações visíveis",
|
||||
"you must be a": "você precisa ser",
|
||||
"Your password has been reset": "Sua senha foi redefinida",
|
||||
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
|
||||
"You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
|
||||
"Sun": "Dom",
|
||||
"Mon": "Seg",
|
||||
"Tue": "Ter",
|
||||
"Wed": "Qua",
|
||||
"Thu": "Qui",
|
||||
"Fri": "Sex",
|
||||
"Sat": "Sáb",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Fev",
|
||||
"Mar": "Mar",
|
||||
"Apr": "Abr",
|
||||
"May": "Mai",
|
||||
"Jun": "Jun",
|
||||
"Jul": "Jul",
|
||||
"Aug": "Ago",
|
||||
"Sep": "Set",
|
||||
"Oct": "Out",
|
||||
"Nov": "Nov",
|
||||
"Dec": "Dez",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s às %(time)s",
|
||||
"%(weekDayName)s %(time)s": "%(weekDayName)s às %(time)s",
|
||||
"en": "Inglês",
|
||||
"pt-br": "Português do Brasil",
|
||||
"de": "Alemão",
|
||||
"da": "Dinamarquês",
|
||||
"ru": "Russo",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s aceitou um convite.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s aceitou o convite para %(displayName)s.",
|
||||
"all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
|
||||
"all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
|
||||
"%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
|
||||
"%(senderName)s answered the call.": "%(senderName)s atendeu à chamada.",
|
||||
"anyone": "qualquer pessoa",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
|
||||
"Call Timeout": "Tempo esgotado. Chamada encerrada",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s.",
|
||||
"%(senderName)s changed their profile picture.": "%(senderName)s alterou sua imagem de perfil.",
|
||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s.",
|
||||
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s.",
|
||||
"click to reveal": "clique para ver",
|
||||
"Conference call failed": "Chamada de conferência falhou",
|
||||
"Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
|
||||
"Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas",
|
||||
"Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador",
|
||||
"/ddg is not a command": "/ddg não é um comando",
|
||||
"Drop here %(toAction)s": "Arraste aqui %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
|
||||
"%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
|
||||
"Existing Call": "Chamada em andamento",
|
||||
"Failed to lookup current room": "Não foi possível buscar na sala atual",
|
||||
"Failed to send email": "Não foi possível enviar email",
|
||||
"Failed to send request.": "Não foi possível mandar requisição.",
|
||||
"Failed to set up conference call": "Não foi possível montar a chamada de conferência",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
|
||||
"Failure to create room": "Não foi possível criar a sala",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.",
|
||||
"%(displayName)s is typing": "%(displayName)s está escrevendo",
|
||||
"%(targetName)s joined the room.": "%(targetName)s entrou na sala.",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
|
||||
"%(targetName)s left the room.": "%(targetName)s saiu da sala.",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
|
||||
"Missing room_id in request": "Faltou o id da sala na requisição",
|
||||
"Missing user_id in request": "Faltou o id de usuário na requisição",
|
||||
"Must be viewing a room": "Tem que estar visualizando uma sala",
|
||||
"New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
|
||||
"(not supported by this browser)": "(não é compatível com este navegador)",
|
||||
"olm version": "versão olm",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.",
|
||||
"Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",
|
||||
"Press": "Aperte",
|
||||
"Reason": "Razão",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s recusou o convite.",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s removeu sua imagem de perfil.",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s está solicitando uma conferência de voz.",
|
||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
|
||||
"Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
|
||||
"Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
|
||||
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s enviou uma imagem.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala.",
|
||||
"%(senderName)s set a profile picture.": "%(senderName)s definiu uma imagem de perfil.",
|
||||
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s definiu seu nome público para %(displayName)s.",
|
||||
"tag as %(tagName)s": "marcar como %(tagName)s",
|
||||
"This email address is already in use": "Este endereço de email já está sendo usado",
|
||||
"This email address was not found": "Este endereço de email não foi encontrado",
|
||||
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
|
||||
"The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
|
||||
"The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
|
||||
"This room is not recognised.": "Esta sala não é reconhecida.",
|
||||
"These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
|
||||
"This phone number is already in use": "Este número de telefone já está sendo usado",
|
||||
"to browse the directory": "para navegar na lista pública de salas",
|
||||
"to demote": "para reduzir prioridade",
|
||||
"to favourite": "para favoritar",
|
||||
"to make a room or": "para criar uma sala ou",
|
||||
"To redact other users' messages": "Para apagar mensagens de outras pessoas",
|
||||
"to restore": "para restaurar",
|
||||
"to start a chat with someone": "para iniciar uma conversa com alguém",
|
||||
"to tag as %(tagName)s": "para marcar como %(tagName)s",
|
||||
"to tag direct chat": "para marcar a conversa como pessoal",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções.",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s).",
|
||||
"Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s desfez o banimento de %(targetName)s.",
|
||||
"Unable to capture screen": "Não foi possível capturar a imagem da tela",
|
||||
"Unable to enable Notifications": "Não foi possível ativar as notificações",
|
||||
"Upload Failed": "O envio falhou",
|
||||
"Usage": "Uso",
|
||||
"Use with caution": "Use com cautela",
|
||||
"VoIP is unsupported": "Chamada de voz não permitida",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s desfez o convite a %(targetName)s.",
|
||||
"You are already in a call": "Você já está em uma chamada",
|
||||
"You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
|
||||
"You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
|
||||
"You cannot place a call with yourself": "Você não pode iniciar uma chamada",
|
||||
"You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
|
||||
"You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.",
|
||||
"You need to be logged in.": "Você tem que estar logado.",
|
||||
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
|
||||
"Set a display name:": "Defina um nome público para você:",
|
||||
"Upload an avatar:": "Envie uma imagem de perfil para identificar você:",
|
||||
"This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.",
|
||||
"Missing password.": "Faltou a senha.",
|
||||
"Passwords don't match.": "As senhas não conferem.",
|
||||
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "A senha é muito curta (o mínimo é de %(MIN_PASSWORD_LENGTH)s caracteres).",
|
||||
"This doesn't look like a valid email address.": "Este endereço de email não parece ser válido.",
|
||||
"This doesn't look like a valid phone number.": "Este número de telefone não parece ser válido.",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Nomes de usuária/o podem conter apenas letras, números, pontos, hífens e linha inferior (_).",
|
||||
"An unknown error occurred.": "Um erro desconhecido ocorreu.",
|
||||
"I already have an account": "Eu já tenho uma conta",
|
||||
"An error occured: %(error_string)s": "Um erro ocorreu: %(error_string)s",
|
||||
"Topic": "Tópico",
|
||||
"Make this room private": "Tornar esta sala privada",
|
||||
"Share message history with new users": "Compartilhar histórico de mensagens com novas/os usuárias/os",
|
||||
"Encrypt room": "Criptografar esta sala",
|
||||
"There are no visible files in this room": "Não há arquivos públicos nesta sala",
|
||||
"Error changing language": "Erro ao mudar de idioma",
|
||||
"Riot was unable to find the correct Data for the selected Language.": "Não foi possível encontrar os dados para o idioma selecionado.",
|
||||
"Connectivity to the server has been lost.": "A conexão com o servidor foi perdida. Verifique sua conexão de internet.",
|
||||
"Sent messages will be stored until your connection has returned.": "Imagens enviadas ficarão armazenadas até que sua conexão seja reestabelecida.",
|
||||
"Auto-complete": "Autocompletar",
|
||||
"Resend all": "Reenviar todas as mensagens",
|
||||
"cancel all": "cancelar todas",
|
||||
"now. You can also select individual messages to resend or cancel.": "agora. Você também pode escolher mensagens individuais e definir se vai reenviar ou cancelar o envio.",
|
||||
"Active call": "Chamada ativa",
|
||||
"af": "Afrikaans",
|
||||
"ar-ae": "Árabe (U.A.E.)",
|
||||
"ar-bh": "Árabe (Bahrain)",
|
||||
"ar-dz": "Árabe (Algéria)",
|
||||
"Sunday": "Domingo",
|
||||
"Monday": "Segunda",
|
||||
"ar-eg": "Árabe (Egito)",
|
||||
"ar-tn": "Árabe (Tunisia)",
|
||||
"be": "Bielorusso",
|
||||
"bg": "Búlgaro",
|
||||
"ca": "Catalão",
|
||||
"cs": "Tcheco",
|
||||
"el": "Grego",
|
||||
"en-au": "Inglês (Austrália)",
|
||||
"en-ca": "Inglês (Canadá)",
|
||||
"en-gb": "Inglês (Reino Unido)",
|
||||
"en-ie": "Inglês (Irlanda)",
|
||||
"en-nz": "Inglês (Nova Zelândia)",
|
||||
"en-us": "Inglês (Estados Unidos)",
|
||||
"es-ar": "Espanhol (Argentina)",
|
||||
"es-py": "Espanhol (Paraguai)",
|
||||
"es": "Espanhol (Espanha)",
|
||||
"et": "Estônia",
|
||||
"fa": "Farsi",
|
||||
"fi": "Finlandês",
|
||||
"fr-be": "Francês (Bélgica)",
|
||||
"fr-ca": "Francês (Canadá)",
|
||||
"fr-ch": "Francês (Suíça)",
|
||||
"fr": "Francês",
|
||||
"ga": "Irlandês",
|
||||
"he": "Hebreu",
|
||||
"hi": "Hindu",
|
||||
"hr": "Croácia",
|
||||
"hu": "Hungria",
|
||||
"id": "Indonésio",
|
||||
"is": "Islandês",
|
||||
"it": "Italiano",
|
||||
"ja": "Japonês",
|
||||
"ji": "Ídiche",
|
||||
"lt": "Lituânia",
|
||||
"lv": "Letão",
|
||||
"ms": "Malaio",
|
||||
"mt": "Maltês",
|
||||
"nl-be": "Holandês (Bélgica)",
|
||||
"nl": "Holandês",
|
||||
"no": "Norueguês",
|
||||
"pl": "Polonês",
|
||||
"pt": "Português (Portugal)",
|
||||
"rm": "Romanche",
|
||||
"ro": "Romeno",
|
||||
"sk": "Eslovaco",
|
||||
"sl": "Esloveno",
|
||||
"sq": "Albanês",
|
||||
"sr": "Sérvio (latino)",
|
||||
"sv": "Suécia",
|
||||
"th": "Tailandês",
|
||||
"tn": "Tsuana",
|
||||
"tr": "Turquia",
|
||||
"ts": "Tsonga",
|
||||
"uk": "Ucraniano",
|
||||
"ur": "Urdu",
|
||||
"vi": "Vietnamita",
|
||||
"xh": "Xhosa",
|
||||
"zu": "Zulu",
|
||||
"Failed to forget room %(errCode)s": "Falhou ao esquecer a sala %(errCode)s",
|
||||
"Failed to join the room": "Falhou ao entrar na sala",
|
||||
"Tuesday": "Terça",
|
||||
"Wednesday": "Quarta",
|
||||
"Thursday": "Quinta",
|
||||
"Friday": "Sexta",
|
||||
"Saturday": "Sábado",
|
||||
"ar-iq": "Árabe (Iraque)",
|
||||
"ar-jo": "Árabe (Jordânia)",
|
||||
"ar-kw": "Árabe (Kuwait)",
|
||||
"ar-lb": "Árabe (Líbano)",
|
||||
"ar-ly": "Árabe (Líbia)",
|
||||
"ar-ma": "Árabe (Marrocos)",
|
||||
"ar-om": "Árabe (Omã)",
|
||||
"ar-qa": "Árabe (Catar)",
|
||||
"ar-sa": "Árabe (Arábia Saudita)",
|
||||
"ar-sy": "Árabe (Síria)",
|
||||
"ar-ye": "Árabe (Iémen)",
|
||||
"de-at": "Alemão (Austria)",
|
||||
"de-ch": "Alemão (Suíça)",
|
||||
"de-li": "Alemão (Liechtenstein)",
|
||||
"de-lu": "Alemão (Luxemburgo)",
|
||||
"en-bz": "Inglês (Belize)",
|
||||
"en-jm": "Inglês (Jamaica)",
|
||||
"en-tt": "English (Trindade)",
|
||||
"en-za": "English (África do Sul)",
|
||||
"es-bo": "Espanhol (Bolívia)",
|
||||
"es-cl": "Espanhol (Chile)",
|
||||
"es-co": "Espanhol (Colômbia)",
|
||||
"es-cr": "Espanhol (Costa Rica)",
|
||||
"es-do": "Espanhol (República Dominicana)",
|
||||
"es-ec": "Espanhol (Equador)",
|
||||
"es-gt": "Espanhol (Guatemala)",
|
||||
"es-hn": "Espanhol (Honduras)",
|
||||
"es-mx": "Espanhol (México)",
|
||||
"es-ni": "Espanhol (Nicarágua)",
|
||||
"es-pa": "Espanhol (Panamá)",
|
||||
"%(oneUser)schanged their avatar": "%(oneUser)salterou sua imagem pública",
|
||||
"es-pe": "Espanhol (Peru)",
|
||||
"es-pr": "Espanhol (Porto Rico)",
|
||||
"es-sv": "Espanhol (El Salvador)",
|
||||
"es-uy": "Espanhol (Uruguai)",
|
||||
"es-ve": "Espanhol (Venezuela)",
|
||||
"eu": "Basco (Basco)",
|
||||
"fr-lu": "Francês (Luxemburgo)",
|
||||
"gd": "Galês (Escócia)",
|
||||
"it-ch": "Italiano (Suíça)",
|
||||
"ko": "Coreano (Johab)",
|
||||
"mk": "Macedônio (República da Macedônia)",
|
||||
"ro-mo": "Romano (Moldávia)",
|
||||
"ru-mo": "Russo (Moldávia)",
|
||||
"sb": "Sorábio",
|
||||
"sv-fi": "Sueco (Finlândia)",
|
||||
"zh-cn": "Chinês (República Popular da China)",
|
||||
"zh-hk": "Chinês (Hong Kong SAR)",
|
||||
"zh-sg": "Chinês (Singapura)",
|
||||
"zh-tw": "Chinês (Taiwan)",
|
||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Uma mensagem de texto foi enviada para +%(msisdn)s. Gentileza entrar com o código de verificação que contém",
|
||||
"%(items)s and %(remaining)s others": "%(items)s e %(remaining)s outros",
|
||||
"%(items)s and one other": "%(items)s e um outro",
|
||||
"%(items)s and %(lastItem)s": "%(items)s e %(lastItem)s",
|
||||
"and %(overflowCount)s others...": "e %(overflowCount)s outros...",
|
||||
"and one other...": "e um outro...",
|
||||
"Are you sure?": "Você tem certeza?",
|
||||
"Attachment": "Anexo",
|
||||
"Autoplay GIFs and videos": "Reproduzir automaticamente GIFs e videos",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s de %(fullYear)s às %(time)s",
|
||||
"fo": "Feroês",
|
||||
"sx": "Sutu",
|
||||
"sz": "Sami (Lappish)",
|
||||
"ve": "Venda",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Não consigo conectar ao servidor padrão - favor checar sua conexão à internet e verificar se o certificado SSL do seu %(urlStart)s servidor padrão %(urlEnd)s é confiável",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Não consigo conectar ao servidor padrão através de HTTP quando uma URL HTTPS está na barra de endereços do seu navegador. Use HTTPS ou então %(urlStart)s habilite scripts não seguros no seu navegador %(urlEnd)s",
|
||||
"Change Password": "Alterar senha",
|
||||
"changing room on a RoomView is not supported": "mudar a sala em uma 'RoomView' não é permitido",
|
||||
"Click to mute audio": "Clique para colocar o áudio no mudo",
|
||||
"Click to mute video": "Clique para desabilitar imagens de vídeo",
|
||||
"Click to unmute video": "Clique para voltar a mostrar imagens de vídeo",
|
||||
"Click to unmute audio": "Clique para retirar áudio do mudo",
|
||||
"Command error": "Erro de comando",
|
||||
"Decrypt %(text)s": "Descriptografar %(text)s",
|
||||
"Delete": "Apagar",
|
||||
"Devices": "Dispositivos",
|
||||
"Direct chats": "Conversas pessoais",
|
||||
"Disinvite": "Desconvidar",
|
||||
"Don't send typing notifications": "Não enviar notificação de estar digitando",
|
||||
"Download %(text)s": "Baixar %(text)s",
|
||||
"Enable encryption": "Habilitar criptografia",
|
||||
"Enter Code": "Entre com o código",
|
||||
"Failed to ban user": "Não foi possível banir o/a usuário/a",
|
||||
"Failed to change power level": "Não foi possível mudar o nível de permissões",
|
||||
"Failed to delete device": "Não foi possível remover o dispositivo",
|
||||
"Failed to join room": "Não foi possível ingressar na sala",
|
||||
"Failed to kick": "Não foi possível remover usuária/o",
|
||||
"Failed to load timeline position": "Não foi possível carregar a posição na linha do tempo",
|
||||
"Failed to mute user": "Não foi possível remover notificações da/do usuária/o",
|
||||
"Failed to reject invite": "Não foi possível rejeitar o convite",
|
||||
"Failed to save settings": "Não foi possível salvar as configurações",
|
||||
"Failed to set display name": "Houve falha ao definir o nome público",
|
||||
"Failed to toggle moderator status": "Houve falha ao alterar o status de moderador/a",
|
||||
"Fill screen": "Tela cheia",
|
||||
"Hide read receipts": "Ocultar recebimentos de leitura",
|
||||
"Hide Text Formatting Toolbar": "Ocultar a barra de formatação de texto",
|
||||
"Incorrect verification code": "Código de verificação incorreto",
|
||||
"Invalid alias format": "Formato de alias é inválido",
|
||||
"Invalid address format": "Formato de endereço é inválido",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' não é um formato válido para um endereço",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' não é um formato válido para um alias",
|
||||
"Join Room": "Ingressar na sala",
|
||||
"Kick": "Remover",
|
||||
"Level": "Nível",
|
||||
"Local addresses for this room:": "Endereço local desta sala:",
|
||||
"Markdown is disabled": "A formatação 'Markdown' está desabilitada",
|
||||
"Markdown is enabled": "A formatação 'Markdown' está habilitada",
|
||||
"Message not sent due to unknown devices being present": "A mensagem não foi enviada por causa da presença de dispositivos desconhecidos",
|
||||
"Never send encrypted messages to unverified devices in this room": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "Novo endereço (p.ex: #algo:%(localDomain)s)",
|
||||
"not set": "não definido",
|
||||
"not specified": "não especificado",
|
||||
"No devices with registered encryption keys": "Não há dispositivos com chaves de criptografia registradas",
|
||||
"No more results": "Não há mais resultados",
|
||||
"No results": "Sem resultados",
|
||||
"OK": "OK",
|
||||
"Revoke Moderator": "Retirar status de moderador",
|
||||
"Search": "Localizar",
|
||||
"Search failed": "Busca falhou",
|
||||
"Server error": "Erro no servidor",
|
||||
"Server may be unavailable, overloaded, or search timed out :(": "O servidor pode estar indisponível, sobrecarregado, ou a busca ultrapassou o tempo limite :(",
|
||||
"Server may be unavailable, overloaded, or the file too big": "O servidor pode estar indisponível, sobrecarregado, ou o arquivo é muito grande",
|
||||
"Server unavailable, overloaded, or something else went wrong": "O servidor pode estar indisponível, sobrecarregado, ou alguma outra coisa não funcionou",
|
||||
"Some of your messages have not been sent": "Algumas das suas mensagens não foram enviadas",
|
||||
"Submit": "Enviar",
|
||||
"The main address for this room is": "O endereço principal desta sala é",
|
||||
"This action cannot be performed by a guest user. Please register to be able to do this": "Esta ação não pode ser realizada por um/a usuário/a visitante. Por favor, registre-se para poder fazer isso",
|
||||
"%(actionVerb)s this person?": "%(actionVerb)s esta pessoa?",
|
||||
"This room has no local addresses": "Esta sala não tem endereços locais",
|
||||
"This room is private or inaccessible to guests. You may be able to join if you register": "Esta sala é privada ou inacessível para visitantes. Você poderá ingressar nela se registrar-se",
|
||||
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Tentei carregar um ponto específico na linha do tempo desta sala, mas parece que você não tem permissões para ver a mensagem em questão",
|
||||
"Tried to load a specific point in this room's timeline, but was unable to find it": "Tentei carregar um ponto específico na linha do tempo desta sala, mas não o encontrei",
|
||||
"Turn Markdown off": "Desabilitar a formatação 'Markdown'",
|
||||
"Turn Markdown on": "Habilitar a marcação 'Markdown'",
|
||||
"Unable to load device list": "Não foi possível carregar a lista de dispositivos",
|
||||
"Unknown command": "Comando desconhecido",
|
||||
"Unknown room %(roomId)s": "A sala %(roomId)s é desconhecida",
|
||||
"You have been invited to join this room by %(inviterName)s": "Você foi convidada/o por %(inviterName)s a ingressar nesta sala",
|
||||
"You seem to be in a call, are you sure you want to quit?": "Parece que você está em uma chamada. Tem certeza que quer sair?",
|
||||
"You seem to be uploading files, are you sure you want to quit?": "Parece que você está enviando arquivos. Tem certeza que quer sair?",
|
||||
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Você não poderá desfazer esta mudança, pois estará dando a este(a) usuário(a) o mesmo nível de permissões que você",
|
||||
"Make Moderator": "Tornar moderador(a)",
|
||||
"Room": "Sala",
|
||||
"(~%(searchCount)s results)": "(±%(searchCount)s resultados)",
|
||||
"Cancel": "Cancelar",
|
||||
"bold": "negrito",
|
||||
"italic": "itálico",
|
||||
"strike": "tachado",
|
||||
"underline": "sublinhado",
|
||||
"code": "código de programação",
|
||||
"quote": "citação",
|
||||
"bullet": "marcador de lista",
|
||||
"numbullet": "marcador de numeração",
|
||||
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)singressaram %(repeats)s vezes",
|
||||
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)singressou %(repeats)s vezes",
|
||||
"%(severalUsers)sjoined": "%(severalUsers)singressaram",
|
||||
"%(oneUser)sjoined": "%(oneUser)singressou",
|
||||
"%(severalUsers)sleft %(repeats)s times": "%(severalUsers)ssaíram %(repeats)s vezes",
|
||||
"%(oneUser)sleft %(repeats)s times": "%(oneUser)ssaiu %(repeats)s vezes",
|
||||
"%(severalUsers)sleft": "%(severalUsers)ssaíram",
|
||||
"%(oneUser)sleft": "%(oneUser)ssaiu",
|
||||
"%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)singressaram e saíram %(repeats)s vezes",
|
||||
"%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)singressou e saiu %(repeats)s vezes",
|
||||
"%(severalUsers)sjoined and left": "%(severalUsers)singressaram e saíram",
|
||||
"%(oneUser)sjoined and left": "%(oneUser)singressou e saiu",
|
||||
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)ssaíram e entraram novamente %(repeats)s vezes",
|
||||
"%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)ssaiu e entrou novamente %(repeats)s vezes",
|
||||
"%(severalUsers)sleft and rejoined": "%(severalUsers)ssaíram e entraram novamente",
|
||||
"%(oneUser)sleft and rejoined": "%(oneUser)ssaiu e entrou novamente",
|
||||
"%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejeitaram seus convites %(repeats)s vezes",
|
||||
"%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejeitou seu convite %(repeats)s vezes",
|
||||
"%(severalUsers)srejected their invitations": "%(severalUsers)srejeitaram seus convites",
|
||||
"%(oneUser)srejected their invitation": "%(oneUser)srejeitou seu convite",
|
||||
"%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)stiveram seus convites desfeitos %(repeats)s vezes",
|
||||
"%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)steve seu convite desfeito %(repeats)s vezes",
|
||||
"%(severalUsers)shad their invitations withdrawn": "%(severalUsers)stiveram seus convites desfeitos",
|
||||
"%(oneUser)shad their invitation withdrawn": "%(oneUser)steve seu convite desfeito",
|
||||
"were invited %(repeats)s times": "foram convidadas(os) %(repeats)s vezes",
|
||||
"was invited %(repeats)s times": "foi convidada(o) %(repeats)s vezes",
|
||||
"were invited": "foram convidadas(os)",
|
||||
"were banned %(repeats)s times": "foram banidas(os) %(repeats)s vezes",
|
||||
"was banned %(repeats)s times": "foi banida(o) %(repeats)s vezes",
|
||||
"were banned": "foram banidas(os)",
|
||||
"were unbanned %(repeats)s times": "tiveram banimento desfeito %(repeats)s vezes",
|
||||
"was unbanned %(repeats)s times": "teve banimento desfeito %(repeats)s vezes",
|
||||
"were unbanned": "tiveram banimento desfeito",
|
||||
"were kicked %(repeats)s times": "foram expulsas(os) %(repeats)s vezes",
|
||||
"was kicked %(repeats)s times": "foi expulsa(o) %(repeats)s vezes",
|
||||
"were kicked": "foram expulsas(os)",
|
||||
"%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)salteraram seu nome %(repeats)s vezes",
|
||||
"%(oneUser)schanged their name %(repeats)s times": "%(oneUser)salterou seu nome %(repeats)s vezes",
|
||||
"%(severalUsers)schanged their name": "%(severalUsers)salteraram seus nomes",
|
||||
"%(oneUser)schanged their name": "%(oneUser)salterou seu nome",
|
||||
"%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)salteraram sua imagem pública %(repeats)s vezes",
|
||||
"%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)salterou sua imagem pública %(repeats)s vezes",
|
||||
"%(severalUsers)schanged their avatar": "%(severalUsers)salteraram sua imagem pública",
|
||||
"Ban": "Banir"
|
||||
}
|
655
src/i18n/strings/ru.json
Normal file
655
src/i18n/strings/ru.json
Normal file
|
@ -0,0 +1,655 @@
|
|||
{
|
||||
"accept": "принимать",
|
||||
"accepted an invitation": "принял приглашение",
|
||||
"accepted the invitation for": "принял приглашение на",
|
||||
"Account": "Аккаунт",
|
||||
"Add email address": "Добавить email адрес",
|
||||
"Add phone number": "Добавить телефонный номер",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Дополнительно",
|
||||
"Algorithm": "Алгоритм",
|
||||
"all room members": "все участники комнаты",
|
||||
"all room members, from the point they are invited": "все участники комнаты, с момента приглашения",
|
||||
"all room members, from the point they joined": "все участники комнаты, с момента вступления",
|
||||
"an address": "адрес",
|
||||
"and": "и",
|
||||
"An email has been sent to": "Email был отправлен",
|
||||
"A new password must be entered.": "Введите новый пароль.",
|
||||
"answered the call.": "принятый звонок.",
|
||||
"anyone": "кто угодно",
|
||||
"Anyone who knows the room's link, apart from guests": "Любой, кто знает ссылку на комнату, кроме гостей",
|
||||
"Anyone who knows the room's link, including guests": "Любой, кто знает ссылку комнаты, включая гостей",
|
||||
"Are you sure you want to reject the invitation?": "Вы уверены что вы хотите отклонить приглашение?",
|
||||
"Are you sure you want upload the following files?": "Вы уверены что вы хотите закачать следующий файл?",
|
||||
"banned": "banned",
|
||||
"Banned users": "Запрещенный пользователь",
|
||||
"Bans user with given id": "Запретить пользователя с определенным id",
|
||||
"Blacklisted": "В черный список",
|
||||
"Bug Report": "Отчет ошибок",
|
||||
"Bulk Options": "Объемные параметры",
|
||||
"Can't load user settings": "Не может загрузить настройки пользователя",
|
||||
"changed avatar": "изменен аватар",
|
||||
"changed name": "измененное имя",
|
||||
"changed their display name from": "changed their display name from",
|
||||
"changed their profile picture": "changed their profile picture",
|
||||
"changed the power level of": "changed the power level of",
|
||||
"changed the room name to": "changed the room name to",
|
||||
"changed the topic to": "changed the topic to",
|
||||
"Changes to who can read history will only apply to future messages in this room": "Изменения того, кто может прочитать историю, будут только относиться к будущим сообщениям в этой комнате",
|
||||
"Changes your display nickname": "Изменяет Ваш псевдоним",
|
||||
"Claimed Ed25519 fingerprint key": "Требуемый Ed25519 ключ цифрового отпечатка",
|
||||
"Clear Cache and Reload": "Очистить кэш и перезагрузить",
|
||||
"Clear Cache": "Очистить кэш",
|
||||
"Click here": "Нажать здесь",
|
||||
"Click here to fix": "Нажать здесь для фиксации",
|
||||
"Commands": "Команды",
|
||||
"Confirm your new password": "Подтвердите ваш новый пароль",
|
||||
"Continue": "Продолжить",
|
||||
"Could not connect to the integration server": "Не может подключится к серверу интеграции",
|
||||
"Create an account": "Создайте учётную запись",
|
||||
"Create Room": "Создайте Комнату",
|
||||
"Cryptography": "Шифрование",
|
||||
"Curve25519 identity key": "Curve25519 идентификационный ключ",
|
||||
"Deactivate Account": "Деактивировать Учётную запись",
|
||||
"Deactivate my account": "Деактивировать мою учётную запись",
|
||||
"decline": "отказаться",
|
||||
"Decryption error": "Ошибка дешифрования",
|
||||
"Default": "Default",
|
||||
"demote": "понижать",
|
||||
"Deops user with given id": "Deops пользователь с данным id",
|
||||
"Device ID": "Устройство ID",
|
||||
"Devices will not yet be able to decrypt history from before they joined the room": "Устройство еще не будет в состоянии дешифровать историю, до присоединения к комнате",
|
||||
"Direct Chat": "Персональное сообщение",
|
||||
"Disable inline URL previews by default": "Отключить встроенные предварительные просмотры URL по умолчанию",
|
||||
"Display name": "Отображаемое имя",
|
||||
"Displays action": "Отображение действий",
|
||||
"Ed25519 fingerprint": "Ed25519 fingerprint",
|
||||
"Email Address": "Email адрес",
|
||||
"Email, name or matrix ID": "Email, имя или matrix ID",
|
||||
"Emoji": "Смайлы",
|
||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Зашифрованные сообщения не будут видимы в клиентах, которые еще не подключили шифрование",
|
||||
"Encrypted room": "Зашифрованная комната",
|
||||
"ended the call.": "ended the call.",
|
||||
"End-to-end encryption information": "Информация сквозного шифрования (e2e)",
|
||||
"End-to-end encryption is in beta and may not be reliable": "Сквозное шифрование (e2e) в бета-версии и не может быть надежным",
|
||||
"Error": "Ошибка",
|
||||
"Event information": "Event information",
|
||||
"Export E2E room keys": "Экспорт E2E ключей комнаты",
|
||||
"Failed to change password. Is your password correct?": "Не удалось изменить пароль. Ваш пароль правильный?",
|
||||
"Failed to forget room": "Не удалось забыть комнату",
|
||||
"Failed to leave room": "Не удалось выйти из комнаты",
|
||||
"Failed to reject invitation": "Не удалось отклонить приглашение",
|
||||
"Failed to send email": "Не удалось отослать email",
|
||||
"Failed to unban": "Не удалось отменить запрет",
|
||||
"Failed to upload file": "Не удалось закачать файл",
|
||||
"Favourite": "Избранное",
|
||||
"favourite": "фаворит",
|
||||
"Favourites": "Фавориты",
|
||||
"Filter room members": "Фильтр участников комнаты",
|
||||
"Forget room": "Забыть комнату",
|
||||
"Forgot your password?": "Вы забыли пароль?",
|
||||
"For security, this session has been signed out. Please sign in again.": "Для обеспечения безопасности, эта сессия была подписана. Войдите в систему еще раз.",
|
||||
"Found a bug?": "Нашли ошибку?",
|
||||
"had": "имеет",
|
||||
"Hangup": "Отключение",
|
||||
"Historical": "Исторический",
|
||||
"Homeserver is": "Домашний сервер является",
|
||||
"Identity Server is": "Регистрационный Сервер является",
|
||||
"I have verified my email address": "Я проверил мой адрес электронной почты",
|
||||
"Import E2E room keys": "Импортировать E2E ключ комнаты",
|
||||
"Invalid Email Address": "Недействительный адрес электронной почты",
|
||||
"invited": "invited",
|
||||
"Invite new room members": "Прегласить новых учасников комнаты",
|
||||
"Invites": "Приглашать",
|
||||
"Invites user with given id to current room": "Пригласить пользователя с данным id в текущую комнату",
|
||||
"is a": "является",
|
||||
"I want to sign in with": "Я хочу регистрироваться с",
|
||||
"joined and left": "присоединенный и оставленный",
|
||||
"joined": "присоединенный",
|
||||
"joined the room": "joined the room",
|
||||
"Joins room with given alias": "Присоединяется к комнате с данным псевдонимом",
|
||||
"Kicks user with given id": "Кик пользователя с заданным id",
|
||||
"Labs": "Лаборатория",
|
||||
"Leave room": "Уйти из комнаты",
|
||||
"left and rejoined": "Покинуть и переподключится",
|
||||
"left": "покинуть",
|
||||
"left the room": "left the room",
|
||||
"Logged in as": "Зарегистрированный как",
|
||||
"Login as guest": "Вход в систему как гость",
|
||||
"Logout": "Выход из системы",
|
||||
"Low priority": "Низкий приоритет",
|
||||
"made future room history visible to": "made future room history visible to",
|
||||
"Manage Integrations": "Управление интеграций",
|
||||
"Members only": "Только участники",
|
||||
"Mobile phone number": "Номер мобильного телефона",
|
||||
"Moderator": "Ведущий",
|
||||
"my Matrix ID": "мой Matrix ID",
|
||||
"Name": "Имя",
|
||||
"Never send encrypted messages to unverified devices from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства с этого устройства",
|
||||
"Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства в этой комнате из этого устройства",
|
||||
"New password": "Новый пароль",
|
||||
"New passwords must match each other.": "Новые пароли должны соответствовать друг другу.",
|
||||
"none": "никто",
|
||||
"Notifications": "Уведомления",
|
||||
" (not supported by this browser)": " (not supported by this browser)",
|
||||
"<not supported>": "<не поддерживаемый>",
|
||||
"NOT verified": "НЕ проверенный",
|
||||
"No users have specific privileges in this room": "Ни у каких пользователей нет специальных полномочий в этой комнате",
|
||||
"olm version": "olm версия",
|
||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Как только шифрование включено для комнаты, оно не может быть выключено снова (на данный момент)",
|
||||
"or": "или",
|
||||
"other": "другой",
|
||||
"others": "другие",
|
||||
"Password": "Пароль",
|
||||
"People": "Люди",
|
||||
"Permissions": "Разрешение",
|
||||
"Phone": "Телефон",
|
||||
"placed a": "placed a",
|
||||
"Please Register": "Пожалуйста, зарегистрируйтесь",
|
||||
"rejected the invitation.": "rejected the invitation.",
|
||||
"removed their display name": "removed their display name",
|
||||
"removed their profile picture": "removed their profile picture",
|
||||
"Remove": "Удалить",
|
||||
"requested a VoIP conference": "requested a VoIP conference",
|
||||
"Return to login screen": "Return to login screen",
|
||||
"Send Reset Email": "Send Reset Email",
|
||||
"sent an image": "sent an image",
|
||||
"sent an invitation to": "sent an invitation to",
|
||||
"set a profile picture": "set a profile picture",
|
||||
"set their display name to": "set their display name to",
|
||||
"Settings": "Настройки",
|
||||
"Start a chat": "Start a chat",
|
||||
"Start Chat": "Start Chat",
|
||||
"tag as": "tag as",
|
||||
"These are experimental features that may break in unexpected ways. Use with caution": "These are experimental features that may break in unexpected ways. Use with caution",
|
||||
"To send events of type": "Для отправки типа событий",
|
||||
"To send messages": "Отправить сообщения",
|
||||
"turned on end-to-end encryption (algorithm": "turned on end-to-end encryption (algorithm",
|
||||
"Unable to add email address": "Невозможно добавить email адрес",
|
||||
"Unable to remove contact information": "Невозможно удалить контактную информацию",
|
||||
"Unable to verify email address.": "Невозможно проверить адрес электронной почты.",
|
||||
"Unban": "Отменить запрет",
|
||||
"Unencrypted room": "Незашифрованная комната",
|
||||
"unencrypted": "незашифрованно",
|
||||
"unknown device": "неизвесное устройство",
|
||||
"unknown error code": "неизвестная ошибка",
|
||||
"unknown": "неизвестно",
|
||||
"Upload avatar": "Загрузить аватар",
|
||||
"uploaded a file": "загруженный файл",
|
||||
"Upload Files": "Загрузка файлов",
|
||||
"Upload file": "Загрузка файла",
|
||||
"User ID": "ID пользователя",
|
||||
"User Interface": "Пользовательский интерфейс",
|
||||
"User name": "Имя пользователя",
|
||||
"Users": "Пользователи",
|
||||
"User": "Пользователь",
|
||||
"Verification Pending": "Ожидание проверки",
|
||||
"Verification": "Проверка",
|
||||
"verified": "проверенный",
|
||||
"Video call": "Видио вызов",
|
||||
"Voice call": "Голосовой вызов",
|
||||
"VoIP conference finished.": "VoIP конференция закончилась.",
|
||||
"VoIP conference started.": "VoIP Конференция стартовала.",
|
||||
"(warning: cannot be disabled again!)": "(предупреждение: не может быть снова отключен!)",
|
||||
"Warning!": "Предупреждение!",
|
||||
"was banned": "запрещен",
|
||||
"was invited": "приглашенный",
|
||||
"was kicked": "выброшен",
|
||||
"was unbanned": "незапрещенный",
|
||||
"was": "был",
|
||||
"were": "быть",
|
||||
"Who can access this room?": "Кто может получить доступ к этой комнате?",
|
||||
"Who can read history?": "Кто может читать историю?",
|
||||
"Who would you like to add to this room?": "Кого хотели бы Вы добавлять к этой комнате?",
|
||||
"Who would you like to communicate with?": "С кем хотели бы Вы связываться?",
|
||||
"withdrawn": "уходить",
|
||||
"Would you like to": "Хотели бы Вы",
|
||||
"You do not have permission to post to this room": "У Вас нет разрешения писать в эту комнату",
|
||||
"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": "Вы вышли из всех устройств и не будет больше получать push-уведомления. Чтобы повторно включить уведомления, войдите в систему еще раз для каждого устройства",
|
||||
"You have no visible notifications": "У Вас нет видимых уведомлений",
|
||||
"you must be a": "Вы должны быть",
|
||||
"Your password has been reset": "Ваш пароль был изменен",
|
||||
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Ваш пароль был успешно изменен. Вы не будете получать Push-уведомления о других устройствах, пока не войдете обратно на них",
|
||||
"You should not yet trust it to secure data": "Вы еще не должны доверять этому защиту данных",
|
||||
"en": "Английский",
|
||||
"pt-br": "Португальский Бразилия",
|
||||
"de": "Немецкий",
|
||||
"da": "Датский",
|
||||
"ru": "Русский",
|
||||
"%(targetName)s accepted an invitation.": "%(targetName)s принял приглашение.",
|
||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s принял приглашение от %(displayName)s.",
|
||||
"Resend all": "Переслать снова всем",
|
||||
"cancel all": "отменить всем",
|
||||
"Active call": "Активный звонок",
|
||||
"%(names)s and %(lastPerson)s are typing": "%(names)s и %(lastPerson)s печатает",
|
||||
"%(names)s and one other are typing": "%(names)s и другой печатают",
|
||||
"%(names)s and %(count)s others are typing": "%(names)s и %(count)s другие печатают",
|
||||
"%(senderName)s answered the call.": "%(senderName)s ответил на звонок.",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s запрещенный %(targetName)s.",
|
||||
"Call Timeout": "Время ожидания вызова",
|
||||
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s их имя измененное с %(oldDisplayName)s на %(displayName)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 topic to \"%(topic)s\".": "%(senderDisplayName)s измененная тема на %(topic)s.",
|
||||
"Conference call failed": "Конференц-вызов прервался",
|
||||
"Conference calling is in development and may not be reliable": "Конференц-вызов находится в процессе и может не быть надежным",
|
||||
"Conference calls are not supported in encrypted rooms": "Конференц-вызовы не поддерживаются в зашифрованных комнатах",
|
||||
"Conference calls are not supported in this client": "Конференц-вызовы не поддерживаются в этом клиенте",
|
||||
"/ddg is not a command": "/ddg не команда",
|
||||
"Drop here %(toAction)s": "Вставить здесь %(toAction)s",
|
||||
"Drop here to tag %(section)s": "Вставить здесь для тега %(section)s",
|
||||
"%(senderName)s ended the call.": "%(senderName)s прекратил звонок.",
|
||||
"Existing Call": "Существующий вызов",
|
||||
"Failed to lookup current room": "Не удалось выполнить поиск текущий комнаты",
|
||||
"Failed to send request.": "Не удалось выслать запрос.",
|
||||
"Failed to set up conference call": "Не удалось установить конференц-вызов",
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты",
|
||||
"Failure to create room": "Не удалось создать комнату",
|
||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s из %(fromPowerLevel)s до %(toPowerLevel)s",
|
||||
"Guest users can't create new rooms. Please register to create room and start a chat": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата",
|
||||
"click to reveal": "нажать для открытия",
|
||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.",
|
||||
"%(displayName)s is typing": "%(displayName)s вводит текст",
|
||||
"%(targetName)s joined the room.": "%(targetName)s вошёл в комнату.",
|
||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s выкинул %(targetName)s.",
|
||||
"%(targetName)s left the room.": "%(targetName)s покинул комнату.",
|
||||
"%(senderName)s made future room history visible to": "%(senderName)s история сделаной будущей комнаты, видимая для",
|
||||
"Missing room_id in request": "Отсутствует room_id в запросе",
|
||||
"Missing user_id in request": "Отсутствует user_id в запросе",
|
||||
"Must be viewing a room": "Комната должна быть посищена",
|
||||
"New Composer & Autocomplete": "Новые Компонист & Автозаполнение",
|
||||
"(not supported by this browser)": "(не поддерживаемый этим браузером)",
|
||||
"af": "Африкаанс",
|
||||
"ar-ae": "Арабский (О.А.Е)",
|
||||
"ar-bh": "Арабский (Бахрейн)",
|
||||
"ar-dz": "Арабский (Алжыр)",
|
||||
"ar-eg": "Арабский (Египет)",
|
||||
"ar-iq": "Арабский (Ирак)",
|
||||
"ar-jo": "Арабский (Иордания)",
|
||||
"ar-kw": "Арабский (Кувейт)",
|
||||
"ar-lb": "Арабский (Ливан)",
|
||||
"ar-ly": "Арабский (Ливия)",
|
||||
"ar-ma": "Арабский (Марокко)",
|
||||
"ar-om": "Арабский (Оман)",
|
||||
"ar-qa": "Арабский (Катар)",
|
||||
"ar-sa": "Арабский (Саудовская Аравия)",
|
||||
"ar-sy": "Арабский (Сирия)",
|
||||
"ar-tn": "Арабский (Тунис)",
|
||||
"ar-ye": "Арабский (Йемен)",
|
||||
"be": "Беларуский",
|
||||
"bg": "Болгарский",
|
||||
"ca": "Каталанский",
|
||||
"cs": "Чешский",
|
||||
"de-at": "Немецкий (Австрия)",
|
||||
"de-ch": "Немецкий (Швейцария)",
|
||||
"de-li": "Немецкий (Лихтенштейн)",
|
||||
"de-lu": "Немецкий (Люксембург)",
|
||||
"el": "Гречиский",
|
||||
"en-au": "Английский (Австралия)",
|
||||
"en-bz": "Английский (Белиз)",
|
||||
"en-ca": "Английский (Канада)",
|
||||
"en-gb": "Английский (UK)",
|
||||
"en-ie": "Английский (Ирландия)",
|
||||
"en-jm": "Английский (Ямайка)",
|
||||
"en-nz": "Английский (Новая Зеландия)",
|
||||
"en-tt": "Английский (Тринидад)",
|
||||
"en-us": "Английский (US)",
|
||||
"en-za": "Английский (Южная Африка)",
|
||||
"es-ar": "Испанский (Аргентина)",
|
||||
"es-bo": "Испанский (Боливия)",
|
||||
"es-cl": "Испанский (Чили)",
|
||||
"es-co": "Испанский (Колумбия)",
|
||||
"es-cr": "Испанский (Коста Рика)",
|
||||
"es-do": "Испанский (Дом. Республика)",
|
||||
"es-ec": "Испанский (Еквадор)",
|
||||
"es-gt": "Испанский (Гватемала)",
|
||||
"es-hn": "Испанский (Гондурас)",
|
||||
"es-mx": "Испанский (Мексика)",
|
||||
"es-ni": "Испанский (Никарагуа)",
|
||||
"es-pa": "Испанский (Панама)",
|
||||
"et": "Эстонский",
|
||||
"fi": "Финский",
|
||||
"fr": "Французкий",
|
||||
"hr": "Хорватский",
|
||||
"it": "Итальянский",
|
||||
"ja": "Японский",
|
||||
"pl": "Польский",
|
||||
"pt": "Португальский",
|
||||
"ru-mo": "Русский (Молдавская Республика)",
|
||||
"ro": "Румынский",
|
||||
"uk": "Украинский",
|
||||
"now. You can also select individual messages to resend or cancel.": "теперь. Вы можете также выбрать отдельные сообщения, чтобы снова послать или отменить.",
|
||||
"Auto-complete": "Автозаполнение",
|
||||
"Error changing language": "Ошибка изменения языка",
|
||||
"Riot was unable to find the correct Data for the selected Language.": "Riot был неспособен найти правельные данные для выбранного языка.",
|
||||
"Connectivity to the server has been lost.": "Связь с сервером была потеряна.",
|
||||
"Sent messages will be stored until your connection has returned.": "Отправленные сообщения будут храниться, пока Ваше соединение не возобновиться.",
|
||||
"There are no visible files in this room": "В этой комнате нет никаких видимых файлов",
|
||||
"This doesn't look like a valid phone number.": "Это не похоже на допустимый телефонный номер.",
|
||||
"Missing password.": "Пароль отсутствует.",
|
||||
"Set a display name:": "Настроить отображаемое имя:",
|
||||
"Passwords don't match.": "Пароли не совпадают.",
|
||||
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Пароль слишком короткий (min %(MIN_PASSWORD_LENGTH)s).",
|
||||
"This doesn't look like a valid email address.": "Это не похоже на допустимый email адрес.",
|
||||
"This server does not support authentication with a phone number.": "Этот сервер не поддерживает аутентификацию с телефонным номером.",
|
||||
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Имена пользователей могут только содержать буквы, числа, точки, дефисы и подчеркивания.",
|
||||
"An unknown error occurred.": "Произошла неизвестная ошибка.",
|
||||
"I already have an account": "У меня уже есть учетная запись",
|
||||
"An error occured: %(error_string)s": "Произошла ошибка: %(error_string)s",
|
||||
"Topic": "Тема",
|
||||
"Make this room private": "Сделать эту комнату частной",
|
||||
"Share message history with new users": "Поделись историей сообщений с новыми учасниками",
|
||||
"Encrypt room": "Зашифровать комнату",
|
||||
"es-pe": "Испанский (Перу)",
|
||||
"hu": "Венгерский",
|
||||
"nl": "Датский",
|
||||
"no": "Норвежский",
|
||||
"sv": "Шведский",
|
||||
"th": "Тайландский",
|
||||
"vi": "Ветнамский",
|
||||
"Monday": "Понедельник",
|
||||
"Tuesday": "Вторник",
|
||||
"Wednesday": "Среда",
|
||||
"Thursday": "Четверг",
|
||||
"Friday": "Пятница",
|
||||
"Saturday": "Суббота",
|
||||
"Sunday": "Воскресенье",
|
||||
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
|
||||
"Upload an avatar:": "Загрузить аватар",
|
||||
"You need to be logged in.": "Вы должны быть зарегистрированы",
|
||||
"You need to be able to invite users to do that.": "Вам необходимо пригласить пользователей чтобы сделать это.",
|
||||
"You cannot place VoIP calls in this browser": "Вы не можете сделать вызовы VoIP с этим браузером",
|
||||
"You are already in a call": "Вы уже находитесь в разговоре",
|
||||
"You're not in any rooms yet! Press": "Вы еще не находитесь ни в каких комнатах! Нажать",
|
||||
"You are trying to access %(roomName)s": "Вы пытаетесь получить доступ %(roomName)s",
|
||||
"You cannot place a call with yourself": "Вы не можете позвонить самим себе",
|
||||
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s анулировал %(targetName)s's преглашение.",
|
||||
"Sep": "Сен.",
|
||||
"Jan": "Янв.",
|
||||
"Feb": "Фев.",
|
||||
"Mar": "Мар.",
|
||||
"Apr": "Апр.",
|
||||
"May": "Май",
|
||||
"Jun": "Июн.",
|
||||
"Jul": "Июл.",
|
||||
"Aug": "Авг.",
|
||||
"Oct": "Окт.",
|
||||
"Nov": "Ноя.",
|
||||
"Dec": "Дек.",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
||||
"Mon": "Пн",
|
||||
"Sun": "Вс",
|
||||
"Tue": "Вт",
|
||||
"Wed": "Ср",
|
||||
"Thu": "Чт",
|
||||
"Fri": "Пя",
|
||||
"Sat": "Сб",
|
||||
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "Вам необходимо снова войти в генерировать сквозное шифрование (е2е) ключей для этого устройства и предоставить публичный ключ Вашему домашнему серверу. Это после выключения; приносим извинения за причиненные неудобства.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Ваш адрес электронной почты, кажется, не связан с Matrix ID на этом Homeserver",
|
||||
"to start a chat with someone": "Начать чат с кем-то",
|
||||
"to tag direct chat": "Пометить прямой чат",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них.",
|
||||
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s включил сквозное шифрование (algorithm %(algorithm)s).",
|
||||
"Unable to restore previous session": "Невозможно востановить предыдущий сеанс",
|
||||
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s запрет отменен %(targetName)s.",
|
||||
"Unable to capture screen": "Невозможно записать снимок экрана",
|
||||
"Unable to enable Notifications": "Невозможно включить уведомления",
|
||||
"Upload Failed": "Неудавшаяся загрузка",
|
||||
"Usage": "Использование",
|
||||
"Use with caution": "Используйте с осторожностью",
|
||||
"VoIP is unsupported": "VoIP не поддерживается",
|
||||
"es-pr": "Испанский (Пуэрто-Рико)",
|
||||
"es-py": "Испанский язык (Парагвай)",
|
||||
"es": "Испанский (Испания)",
|
||||
"es-sv": "Испанский (Сальвадор)",
|
||||
"es-uy": "Испанский (Уругвай)",
|
||||
"es-ve": "Испанский (Венесуэла)",
|
||||
"fa": "Фарси",
|
||||
"fo": "Фарезский",
|
||||
"fr-be": "Французский (Бельгия)",
|
||||
"fr-ca": "Французский (Канада)",
|
||||
"fr-ch": "Французский (Швейцария)",
|
||||
"ga": "Ирландский",
|
||||
"he": "Иврит",
|
||||
"hi": "Хинди",
|
||||
"id": "Индонезийский",
|
||||
"is": "Исландский",
|
||||
"ji": "Идиш",
|
||||
"lt": "Литовкий",
|
||||
"lv": "Латвийский",
|
||||
"ms": "Малайцы",
|
||||
"mt": "Мальтийский",
|
||||
"nl-be": "Голландский (Бельгия)",
|
||||
"rm": "Ретороманский",
|
||||
"sb": "Вендский",
|
||||
"sk": "Словацкий",
|
||||
"sl": "Словенский",
|
||||
"sq": "Албанский",
|
||||
"sr": "Сербский (Латиница)",
|
||||
"sv-fi": "Шведский (Финляндия)",
|
||||
"sz": "Саами (лопарский)",
|
||||
"tn": "Тсвана",
|
||||
"tr": "Турецкий",
|
||||
"ts": "Тсонга",
|
||||
"ur": "Урду",
|
||||
"ve": "Венда",
|
||||
"xh": "Коса",
|
||||
"zh-cn": "Китайский (PRC)",
|
||||
"zh-sg": "Китайский (Сингапур)",
|
||||
"zh-tw": "Китайский (Тайвань)",
|
||||
"zu": "Зулусский",
|
||||
"eu": "Баскский",
|
||||
"fr-lu": "Французский (Люксембург)",
|
||||
"gd": "Гэльский (Шотландия)",
|
||||
"it-ch": "Итальянский (Швейцария)",
|
||||
"ko": "Корейский (Johab)",
|
||||
"mk": "Македонский (FYROM)",
|
||||
"ro-mo": "Румынский (Республика Молдова)",
|
||||
"sx": "Суту",
|
||||
"zh-hk": "Китайский (Гонконг)",
|
||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "На +%(msisdn)s было отправлено текстовое сообщение. Пожалуйста, введите проверочный код из него",
|
||||
"and %(overflowCount)s others...": "и %(overflowCount)s других...",
|
||||
"Are you sure?": "Вы уверены?",
|
||||
"Autoplay GIFs and videos": "Проигрывать GIF и видео автоматически",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Невозможно соединиться с домашним сервером - проверьте своё соединение и убедитесь, что %(urlStart)s SSL-сертификат вашего домашнего сервера %(urlEnd)s включён в доверяемые",
|
||||
"changing room on a RoomView is not supported": "изменение комнаты в RoomView не поддерживается",
|
||||
"Click to mute audio": "Выключить звук",
|
||||
"Click to mute video": "Выключить звук у видео",
|
||||
"Click to unmute video": "Включить звук у видео",
|
||||
"Click to unmute audio": "Включить звук",
|
||||
"Decrypt %(text)s": "Расшифровать %(text)s",
|
||||
"Delete": "Удалить",
|
||||
"Devices": "Устройства",
|
||||
"Direct chats": "Личные чаты",
|
||||
"Disinvite": "Отозвать приглашение",
|
||||
"Don't send typing notifications": "Не оповещать, когда я печатаю",
|
||||
"Download %(text)s": "Загрузить %(text)s",
|
||||
"Enable encryption": "Включить шифрование",
|
||||
"Enter Code": "Ввести код",
|
||||
"Failed to ban user": "Не удалось забанить пользователя",
|
||||
"Failed to change power level": "Не удалось изменить уровень привилегий",
|
||||
"Failed to delete device": "Не удалось удалить устройство",
|
||||
"Failed to forget room %(errCode)s": "Не удалось забыть комнату %(errCode)s",
|
||||
"Failed to join room": "Не удалось присоединиться к комнате",
|
||||
"Failed to join the room": "Не удалось войти в комнату",
|
||||
"A registered account is required for this action": "Необходима зарегистрированная учетная запись для этого действия",
|
||||
"Access Token:": "Токен:",
|
||||
"Always show message timestamps": "Всегда показывать время сообщения",
|
||||
"Authentication": "Авторизация",
|
||||
"olm version:": "версия olm:",
|
||||
"%(items)s and %(remaining)s others": "%(items)s и %(remaining)s другие",
|
||||
"%(items)s and one other": "%(items)s и ещё один",
|
||||
"%(items)s and %(lastItem)s": "%(items)s и %(lastItem)s",
|
||||
"and one other...": "и ещё один...",
|
||||
"An error has occurred.": "Произошла ошибка.",
|
||||
"Attachment": "Вложение",
|
||||
"Ban": "Запретить",
|
||||
"Change Password": "Сменить пароль",
|
||||
"Command error": "Ошибка команды",
|
||||
"Confirm password": "Подтвердить пароль",
|
||||
"Current password": "Текущий пароль",
|
||||
"Email": "Электронная почта",
|
||||
"Failed to kick": "Не удалось выгнать",
|
||||
"Failed to load timeline position": "Не удалось узнать место во времени",
|
||||
"Failed to mute user": "Не удалось заглушить",
|
||||
"Failed to reject invite": "Не удалось отклонить приглашение",
|
||||
"Failed to save settings": "Не удалось сохранить настройки",
|
||||
"Failed to set display name": "Не удалось установить отображаемое имя",
|
||||
"Failed to toggle moderator status": "Не удалось изменить статус модератора",
|
||||
"Fill screen": "Заполнить экран",
|
||||
"Guest users can't upload files. Please register to upload": "Гости не могут посылать файлы. Пожалуйста, зарегистрируйтесь для отправки",
|
||||
"Hide read receipts": "Скрыть отметки о прочтении",
|
||||
"Hide Text Formatting Toolbar": "Скрыть панель форматирования текста",
|
||||
"Incorrect verification code": "Неверный код подтверждения",
|
||||
"Interface Language": "Язык интерфейса",
|
||||
"Invalid alias format": "Неверный формат привязки",
|
||||
"Invalid address format": "Неверный формат адреса",
|
||||
"'%(alias)s' is not a valid format for an address": "'%(alias)s' неверный формат для адреса",
|
||||
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' неверный формат для привязки",
|
||||
"Join Room": "Войти в комнату",
|
||||
"Kick": "Выгнать",
|
||||
"Level": "Уровень",
|
||||
"Local addresses for this room:": "Местный адрес этой комнаты:",
|
||||
"Markdown is disabled": "Markdown отключен",
|
||||
"Markdown is enabled": "Markdown включен",
|
||||
"matrix-react-sdk version:": "версия matrix-react-sdk:",
|
||||
"Never send encrypted messages to unverified devices in this room": "Никогда не отправлять зашифрованные сообщения непроверенным устройствам в этой комнате",
|
||||
"New address (e.g. #foo:%(localDomain)s)": "Новый адрес (например, #foo:%(localDomain)s)",
|
||||
"New passwords don't match": "Пароли не совпадают",
|
||||
"not set": "не установлено",
|
||||
"not specified": "не указано",
|
||||
"No devices with registered encryption keys": "Нет устройств с записанными ключами шифрования",
|
||||
"No more results": "Нет больше результатов",
|
||||
"No results": "Нет результатов",
|
||||
"OK": "Да",
|
||||
"Only people who have been invited": "Только приглашённые люди",
|
||||
"Passwords can't be empty": "Пароли не могут быть пустыми",
|
||||
"%(senderName)s placed a %(callType)s call.": "%(senderName)s выполнил %(callType)s вызов.",
|
||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Пожалуйста, проверьте вашу электронную почту и нажмите в ней ссылку. По завершении нажмите продолжить.",
|
||||
"Power level must be positive integer.": "Уровень силы должен быть положительным числом.",
|
||||
"Press": "Нажать",
|
||||
"Profile": "Профиль",
|
||||
"Reason": "Причина",
|
||||
"Registration required": "Требуется регистрация",
|
||||
"rejected": "отклонено",
|
||||
"%(targetName)s rejected the invitation.": "%(targetName)s отклонил приглашение.",
|
||||
"Reject invitation": "Отклонить приглашение",
|
||||
"Remove Contact Information?": "Убрать контактную информацию?",
|
||||
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s убрал своё отображаемое имя (%(oldDisplayName)s).",
|
||||
"%(senderName)s removed their profile picture.": "%(senderName)s убрал своё изображение.",
|
||||
"%(senderName)s requested a VoIP conference.": "%(senderName)s запросил голосовую конференц-связь.",
|
||||
"Report it": "Сообщить об этом",
|
||||
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "На данный момент сброс пароля сбросит все ключи шифрования на всех устройствах, зашифрованная история общения будет нечитаема, если вы сперва не скачаете ваши ключи от комнаты и не загрузите их после. В будущем это будет улучшено.",
|
||||
"restore": "восстановить",
|
||||
"Return to app": "Вернуться в приложение",
|
||||
"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 не получил разрешение для отправки уведомлений, пожалуйста, попробуйте снова",
|
||||
"riot-web version:": "версия riot-web:",
|
||||
"Room %(roomId)s not visible": "Комната %(roomId)s невидима",
|
||||
"Room Colour": "Цвет комнаты",
|
||||
"Room name (optional)": "Имя комнаты (необязательно)",
|
||||
"Rooms": "Комнаты",
|
||||
"Scroll to bottom of page": "Прокрутить вниз страницы",
|
||||
"Scroll to unread messages": "Прокрутить к непрочитанным сообщениям",
|
||||
"Search": "Поиск",
|
||||
"Search failed": "Поиск не удался",
|
||||
"Send a message (unencrypted)": "Отправить сообщение (не зашифровано)",
|
||||
"Send an encrypted message": "Отправить зашифрованное сообщение",
|
||||
"Sender device information": "Информация об устройстве отправителя",
|
||||
"Send Invites": "Отправить приглашения",
|
||||
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s отправил изображение.",
|
||||
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s отправил приглашение для %(targetDisplayName)s войти в комнату.",
|
||||
"sent a video": "отправил видео",
|
||||
"Show panel": "Показать панель",
|
||||
"Sign in": "Войти",
|
||||
"Sign out": "Выйти",
|
||||
"since the point in time of selecting this option": "с момента выбора этой настройки",
|
||||
"since they joined": "с момента входа",
|
||||
"since they were invited": "с момента приглашения",
|
||||
"Some of your messages have not been sent": "Некоторые из ваших сообщений не были отправлены",
|
||||
"Someone": "Кто-то",
|
||||
"Submit": "Отправить",
|
||||
"Success": "Успех",
|
||||
"tag as %(tagName)s": "отметить как %(tagName)s",
|
||||
"tag direct chat": "отметить прямое общение",
|
||||
"The default role for new room members is": "Роль по умолчанию для новых участников комнаты",
|
||||
"The main address for this room is": "Основной адрес для этой комнаты",
|
||||
"This action cannot be performed by a guest user. Please register to be able to do this": "Это действие не может быть выполнено гостем. Пожалуйста, зарегистрируйтесь для этого",
|
||||
"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' failed to upload": "Не удалось загрузить файл '%(fileName)s'",
|
||||
"The remote side failed to pick up": "Удалённая сторона не смогла ответить",
|
||||
"This room has no local addresses": "Эта комната не имеет местного адреса",
|
||||
"This room is not recognised.": "Эта комната не опознана.",
|
||||
"This room is private or inaccessible to guests. You may be able to join if you register": "Эта комната личная или недоступна для гостей. Мы может быть войдёте, если зарегистрируйтесь",
|
||||
"These are experimental features that may break in unexpected ways": "Это экспериментальные возможности, которые могут сломаться неожиданным образом",
|
||||
"This doesn't appear to be a valid email address": "Не похоже, что это правильный адрес электронной почты",
|
||||
"This is a preview of this room. Room interactions have been disabled": "Это просмотр данной комнаты. Взаимодействия с ней были отключены.",
|
||||
"This phone number is already in use": "Этот телефонный номер уже используется",
|
||||
"This room's internal ID is": "Внутренний ID этой комнаты",
|
||||
"times": "раз",
|
||||
"to demote": "для понижения",
|
||||
"to favourite": "для избранного",
|
||||
"to restore": "восстановить",
|
||||
"Turn Markdown off": "Выключить Markdown",
|
||||
"Turn Markdown on": "Включить Markdown",
|
||||
"Unknown command": "Неизвестная команда",
|
||||
"Unknown room %(roomId)s": "Неизвестная комната %(roomId)s",
|
||||
"You have been invited to join this room by %(inviterName)s": "Вы были приглашены войти в эту комнату от %(inviterName)s",
|
||||
"You seem to be uploading files, are you sure you want to quit?": "Похоже вы передаёте файлы, вы уверены, что хотите выйти?",
|
||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||
"Make Moderator": "Сделать модератором",
|
||||
"Room": "Комната",
|
||||
"Cancel": "Отмена",
|
||||
"bold": "жирный",
|
||||
"italic": "наклонный",
|
||||
"strike": "перечёркнутый",
|
||||
"underline": "подчёркнутый",
|
||||
"code": "текст",
|
||||
"quote": "цитата",
|
||||
"bullet": "пункт",
|
||||
"numbullet": "нумерация",
|
||||
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sвошли %(repeats)s раз",
|
||||
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)sвошёл %(repeats)s раз",
|
||||
"%(severalUsers)sjoined": "%(severalUsers)sвошли",
|
||||
"%(oneUser)sjoined": "%(oneUser)sвошёл",
|
||||
"%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sушли %(repeats)s раз",
|
||||
"%(oneUser)sleft %(repeats)s times": "%(oneUser)sушли %(repeats)s раз",
|
||||
"%(severalUsers)sleft": "%(severalUsers)sушли",
|
||||
"%(oneUser)sleft": "%(oneUser)sушёл",
|
||||
"%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sвошли и ушли %(repeats)s раз",
|
||||
"%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sвошёл и ушёл %(repeats)s раз",
|
||||
"%(severalUsers)sjoined and left": "%(severalUsers)sвошли и ушли",
|
||||
"%(oneUser)sjoined and left": "%(oneUser)sвошёл и ушёл",
|
||||
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sушли и перезашли %(repeats)s раз",
|
||||
"%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sушёл и перезашёл %(repeats)s раз",
|
||||
"%(severalUsers)sleft and rejoined": "%(severalUsers)sушли и перезашли",
|
||||
"%(oneUser)sleft and rejoined": "%(oneUser)sушёл и перезашёл",
|
||||
"%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)sотклонили приглашения %(repeats)s раз",
|
||||
"%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)sотклонил приглашения %(repeats)s раз",
|
||||
"%(severalUsers)srejected their invitations": "%(severalUsers)sотклонили приглашения",
|
||||
"%(oneUser)srejected their invitation": "%(oneUser)sотклонил приглашение",
|
||||
"were invited %(repeats)s times": "были приглашёны %(repeats)s раз",
|
||||
"was invited %(repeats)s times": "был приглашён %(repeats)s раз",
|
||||
"were invited": "были приглашёны",
|
||||
"were banned %(repeats)s times": "были запрещёны %(repeats)s раз",
|
||||
"was banned %(repeats)s times": "был запрещён %(repeats)s раз",
|
||||
"were banned": "были запрещёны",
|
||||
"were unbanned %(repeats)s times": "были разрешены %(repeats)s раз",
|
||||
"was unbanned %(repeats)s times": "был разрешён %(repeats)s раз",
|
||||
"were unbanned": "были разрешены",
|
||||
"were kicked %(repeats)s times": "были выгнаны %(repeats)s раз",
|
||||
"was kicked %(repeats)s times": "был выгнан %(repeats)s раз",
|
||||
"were kicked": "были выгнаны",
|
||||
"%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sизменили имена %(repeats)s раз",
|
||||
"%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sизменил имя %(repeats)s раз",
|
||||
"%(severalUsers)schanged their name": "%(severalUsers)sизменили имя",
|
||||
"%(oneUser)schanged their name": "%(oneUser)sизменил имя",
|
||||
"%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)sизменили своё изображение %(repeats)s раз",
|
||||
"%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)sизменил своё изображение %(repeats)s раз",
|
||||
"%(severalUsers)schanged their avatar": "%(severalUsers)sизменили своё изображение",
|
||||
"%(oneUser)schanged their avatar": "%(oneUser)sизменил своё изображение",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Не возможно подключиться к серверу через HTTP, когда в строке браузера HTTPS. Используйте HTTPS или %(urlStart) включив небезопасные скрипты %(urlEnd)"
|
||||
}
|
24
src/i18n/strings/zh_Hant.json
Normal file
24
src/i18n/strings/zh_Hant.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"An email has been sent to": "電郵已經發送至",
|
||||
"A new password must be entered.": "必須輸入新密碼。",
|
||||
"anyone": "任何人",
|
||||
"An error has occurred.": "發生了一個錯誤。",
|
||||
"Anyone who knows the room's link, apart from guests": "任何知道房間連結的人,但訪客除外",
|
||||
"Anyone who knows the room's link, including guests": "任何知道房間連結的人,包括訪客",
|
||||
"Are you sure?": "您確認嗎?",
|
||||
"Are you sure you want to reject the invitation?": "您確認要謝絕邀請嗎?",
|
||||
"Are you sure you want upload the following files?": "您確認要上傳以下文件嗎?",
|
||||
"Attachment": "附件",
|
||||
"Autoplay GIFs and videos": "自動播放GIF和影片",
|
||||
"%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s。",
|
||||
"Ban": "封禁",
|
||||
"Banned users": "已被封禁的使用者",
|
||||
"Blacklisted": "已列入黑名單",
|
||||
"Bug Report": "錯誤報告",
|
||||
"Call Timeout": "通話超時",
|
||||
"Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "無法連結主伺服器 - 請檢查網路狀況並確保您的 %(urlStart)s 主伺服器 SSL 證書 %(urlEnd)s 得到信任",
|
||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "當瀏覽器網址列里有 HTTPS URL 時,不能使用 HTTP 連結主伺服器。請採用 HTTPS 或者%(urlStart)s 允許不安全的腳本 %(urlEnd)s",
|
||||
"Can't load user settings": "無法載入使用者設定",
|
||||
"Change Password": "變更密碼",
|
||||
"%(targetName)s left the room.": "%(targetName)s 離開了聊天室。"
|
||||
}
|
|
@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var Skinner = require('./Skinner');
|
||||
import Skinner from './Skinner';
|
||||
import request from 'browser-request';
|
||||
import UserSettingsStore from './UserSettingsStore';
|
||||
|
||||
module.exports.loadSkin = function(skinObject) {
|
||||
Skinner.load(skinObject);
|
||||
|
|
163
src/languageHandler.js
Normal file
163
src/languageHandler.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
Copyright 2017 MTRNord and Cooperative EITA
|
||||
Copyright 2017 Vector Creations Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import request from 'browser-request';
|
||||
import counterpart from 'counterpart';
|
||||
import q from 'q';
|
||||
|
||||
import UserSettingsStore from './UserSettingsStore';
|
||||
|
||||
const i18nFolder = 'i18n/';
|
||||
|
||||
// We use english strings as keys, some of which contain full stops
|
||||
counterpart.setSeparator('|');
|
||||
// Fall back to English
|
||||
counterpart.setFallbackLocale('en');
|
||||
|
||||
// The translation function. This is just a simple wrapper to counterpart,
|
||||
// but exists mostly because we must use the same counterpart instance
|
||||
// between modules (ie. here (react-sdk) and the app (riot-web), and if we
|
||||
// just import counterpart and use it directly, we end up using a different
|
||||
// instance.
|
||||
export function _t(...args) {
|
||||
return counterpart.translate(...args);
|
||||
}
|
||||
|
||||
// Allow overriding the text displayed when no translation exists
|
||||
// Currently only use din unit tests to avoid having to load
|
||||
// the translations in riot-web
|
||||
export function setMissingEntryGenerator(f) {
|
||||
counterpart.setMissingEntryGenerator(f);
|
||||
}
|
||||
|
||||
export function setLanguage(preferredLangs) {
|
||||
if (!Array.isArray(preferredLangs)) {
|
||||
preferredLangs = [preferredLangs];
|
||||
}
|
||||
|
||||
let langToUse;
|
||||
let availLangs;
|
||||
return getLangsJson().then((result) => {
|
||||
availLangs = result;
|
||||
|
||||
for (let i = 0; i < preferredLangs.length; ++i) {
|
||||
if (availLangs.hasOwnProperty(preferredLangs[i])) {
|
||||
langToUse = preferredLangs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!langToUse) {
|
||||
throw new Error("Unable to find an appropriate language");
|
||||
}
|
||||
|
||||
return getLanguage(i18nFolder + availLangs[langToUse]);
|
||||
}).then((langData) => {
|
||||
counterpart.registerTranslations(langToUse, langData);
|
||||
counterpart.setLocale(langToUse);
|
||||
UserSettingsStore.setLocalSetting('language', langToUse);
|
||||
console.log("set language to " + langToUse);
|
||||
|
||||
// Set 'en' as fallback language:
|
||||
if (langToUse != "en") {
|
||||
return getLanguage(i18nFolder + availLangs['en']);
|
||||
}
|
||||
}).then((langData) => {
|
||||
if (langData) counterpart.registerTranslations('en', langData);
|
||||
});
|
||||
};
|
||||
|
||||
export function getAllLanguageKeysFromJson() {
|
||||
return getLangsJson().then((langs) => {
|
||||
return Object.keys(langs);
|
||||
});
|
||||
}
|
||||
|
||||
export function getLanguagesFromBrowser() {
|
||||
if (navigator.languages) return navigator.languages;
|
||||
if (navigator.language) return [navigator.language]
|
||||
return [navigator.userLanguage];
|
||||
};
|
||||
|
||||
/**
|
||||
* Turns a language string, normalises it,
|
||||
* (see normalizeLanguageKey) into an array of language strings
|
||||
* with fallback to generic languages
|
||||
* (eg. 'pt-BR' => ['pt-br', 'pt'])
|
||||
*
|
||||
* @param {string} language The input language string
|
||||
* @return {string[]} List of normalised languages
|
||||
*/
|
||||
export function getNormalizedLanguageKeys(language) {
|
||||
const languageKeys = [];
|
||||
const normalizedLanguage = this.normalizeLanguageKey(language);
|
||||
const languageParts = normalizedLanguage.split('-');
|
||||
if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
|
||||
languageKeys.push(languageParts[0]);
|
||||
} else {
|
||||
languageKeys.push(normalizedLanguage);
|
||||
if (languageParts.length == 2) {
|
||||
languageKeys.push(languageParts[0]);
|
||||
}
|
||||
}
|
||||
return languageKeys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a language string with underscores replaced with
|
||||
* hyphens, and lowercased.
|
||||
*/
|
||||
export function normalizeLanguageKey(language) {
|
||||
return language.toLowerCase().replace("_","-");
|
||||
};
|
||||
|
||||
export function getCurrentLanguage() {
|
||||
return counterpart.getLocale();
|
||||
}
|
||||
|
||||
function getLangsJson() {
|
||||
const deferred = q.defer();
|
||||
|
||||
request(
|
||||
{ method: "GET", url: i18nFolder + 'languages.json' },
|
||||
(err, response, body) => {
|
||||
if (err || response.status < 200 || response.status >= 300) {
|
||||
deferred.reject({err: err, response: response});
|
||||
return;
|
||||
}
|
||||
deferred.resolve(JSON.parse(body));
|
||||
}
|
||||
);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getLanguage(langPath) {
|
||||
const deferred = q.defer();
|
||||
|
||||
let response_return = {};
|
||||
request(
|
||||
{ method: "GET", url: langPath },
|
||||
(err, response, body) => {
|
||||
if (err || response.status < 200 || response.status >= 300) {
|
||||
deferred.reject({err: err, response: response});
|
||||
return;
|
||||
}
|
||||
|
||||
deferred.resolve(JSON.parse(body));
|
||||
}
|
||||
);
|
||||
return deferred.promise;
|
||||
}
|
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