Merge remote-tracking branch 'origin/develop' into dbkr/jitsi_makewidget

This commit is contained in:
David Baker 2018-06-22 11:31:54 +01:00
commit 6252f5b818
69 changed files with 1789 additions and 1252 deletions

View file

@ -392,6 +392,7 @@ limitations under the License.
overflow-x: overlay; overflow-x: overlay;
overflow-y: visible; overflow-y: visible;
max-height: 30vh; max-height: 30vh;
position: static;
} }
.mx_EventTile_content .markdown-body code { .mx_EventTile_content .markdown-body code {
@ -406,7 +407,7 @@ limitations under the License.
visibility: hidden; visibility: hidden;
cursor: pointer; cursor: pointer;
top: 6px; top: 6px;
right: 6px; right: 36px;
width: 19px; width: 19px;
height: 19px; height: 19px;
background-image: url($copy-button-url); background-image: url($copy-button-url);

View file

@ -243,6 +243,7 @@ function uploadFile(matrixClient, roomId, file, progressHandler) {
const blob = new Blob([encryptResult.data]); const blob = new Blob([encryptResult.data]);
return matrixClient.uploadContent(blob, { return matrixClient.uploadContent(blob, {
progressHandler: progressHandler, progressHandler: progressHandler,
includeFilename: false,
}).then(function(url) { }).then(function(url) {
// If the attachment is encrypted then bundle the URL along // If the attachment is encrypted then bundle the URL along
// with the information needed to decrypt the attachment and // with the information needed to decrypt the attachment and

View file

@ -0,0 +1,169 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
class DecryptionFailure {
constructor(failedEventId) {
this.failedEventId = failedEventId;
this.ts = Date.now();
}
}
export default class DecryptionFailureTracker {
// Array of items of type DecryptionFailure. Every `CHECK_INTERVAL_MS`, this list
// is checked for failures that happened > `GRACE_PERIOD_MS` ago. Those that did
// are added to `failuresToTrack`.
failures = [];
// Every TRACK_INTERVAL_MS (so as to spread the number of hits done on Analytics),
// one DecryptionFailure of this FIFO is removed and tracked.
failuresToTrack = [];
// Event IDs of failures that were tracked previously
trackedEventHashMap = {
// [eventId]: true
};
// Set to an interval ID when `start` is called
checkInterval = null;
trackInterval = null;
// Spread the load on `Analytics` by sending at most 1 event per
// `TRACK_INTERVAL_MS`.
static TRACK_INTERVAL_MS = 1000;
// Call `checkFailures` every `CHECK_INTERVAL_MS`.
static CHECK_INTERVAL_MS = 5000;
// Give events a chance to be decrypted by waiting `GRACE_PERIOD_MS` before moving
// the failure to `failuresToTrack`.
static GRACE_PERIOD_MS = 5000;
constructor(fn) {
if (!fn || typeof fn !== 'function') {
throw new Error('DecryptionFailureTracker requires tracking function');
}
this.trackDecryptionFailure = fn;
}
// loadTrackedEventHashMap() {
// this.trackedEventHashMap = JSON.parse(localStorage.getItem('mx-decryption-failure-event-id-hashes')) || {};
// }
// saveTrackedEventHashMap() {
// localStorage.setItem('mx-decryption-failure-event-id-hashes', JSON.stringify(this.trackedEventHashMap));
// }
eventDecrypted(e) {
if (e.isDecryptionFailure()) {
this.addDecryptionFailureForEvent(e);
} else {
// Could be an event in the failures, remove it
this.removeDecryptionFailuresForEvent(e);
}
}
addDecryptionFailureForEvent(e) {
this.failures.push(new DecryptionFailure(e.getId()));
}
removeDecryptionFailuresForEvent(e) {
this.failures = this.failures.filter((f) => f.failedEventId !== e.getId());
}
/**
* Start checking for and tracking failures.
*/
start() {
this.checkInterval = setInterval(
() => this.checkFailures(Date.now()),
DecryptionFailureTracker.CHECK_INTERVAL_MS,
);
this.trackInterval = setInterval(
() => this.trackFailure(),
DecryptionFailureTracker.TRACK_INTERVAL_MS,
);
}
/**
* Clear state and stop checking for and tracking failures.
*/
stop() {
clearInterval(this.checkInterval);
clearInterval(this.trackInterval);
this.failures = [];
this.failuresToTrack = [];
}
/**
* Mark failures that occured before nowTs - GRACE_PERIOD_MS as failures that should be
* tracked. Only mark one failure per event ID.
* @param {number} nowTs the timestamp that represents the time now.
*/
checkFailures(nowTs) {
const failuresGivenGrace = [];
const failuresNotReady = [];
while (this.failures.length > 0) {
const f = this.failures.shift();
if (nowTs > f.ts + DecryptionFailureTracker.GRACE_PERIOD_MS) {
failuresGivenGrace.push(f);
} else {
failuresNotReady.push(f);
}
}
this.failures = failuresNotReady;
// Only track one failure per event
const dedupedFailuresMap = failuresGivenGrace.reduce(
(map, failure) => {
if (!this.trackedEventHashMap[failure.failedEventId]) {
return map.set(failure.failedEventId, failure);
} else {
return map;
}
},
// Use a map to preseve key ordering
new Map(),
);
const trackedEventIds = [...dedupedFailuresMap.keys()];
this.trackedEventHashMap = trackedEventIds.reduce(
(result, eventId) => ({...result, [eventId]: true}),
this.trackedEventHashMap,
);
// Commented out for now for expediency, we need to consider unbound nature of storing
// this in localStorage
// this.saveTrackedEventHashMap();
const dedupedFailures = dedupedFailuresMap.values();
this.failuresToTrack = [...this.failuresToTrack, ...dedupedFailures];
}
/**
* If there is a failure that should be tracked, call the given trackDecryptionFailure
* function with the first failure in the FIFO of failures that should be tracked.
*/
trackFailure() {
if (this.failuresToTrack.length > 0) {
this.trackDecryptionFailure(this.failuresToTrack.shift());
}
}
}

View file

@ -14,28 +14,31 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import MatrixClientPeg from "./MatrixClientPeg";
import dis from "./dispatcher"; import React from 'react';
import Tinter from "./Tinter"; import MatrixClientPeg from './MatrixClientPeg';
import dis from './dispatcher';
import Tinter from './Tinter';
import sdk from './index'; import sdk from './index';
import { _t } from './languageHandler'; import {_t, _td} from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; import SettingsStore, {SettingLevel} from './settings/SettingsStore';
class Command { class Command {
constructor(name, paramArgs, runFn) { constructor({name, args='', description, runFn}) {
this.name = name; this.command = '/' + name;
this.paramArgs = paramArgs; this.args = args;
this.description = description;
this.runFn = runFn; this.runFn = runFn;
} }
getCommand() { getCommand() {
return "/" + this.name; return this.command;
} }
getCommandWithArgs() { getCommandWithArgs() {
return this.getCommand() + " " + this.paramArgs; return this.getCommand() + " " + this.args;
} }
run(roomId, args) { run(roomId, args) {
@ -47,16 +50,12 @@ class Command {
} }
} }
function reject(msg) { function reject(error) {
return { return {error};
error: msg,
};
} }
function success(promise) { function success(promise) {
return { return {promise};
promise: promise,
};
} }
/* Disable the "unexpected this" error for these commands - all of the run /* Disable the "unexpected this" error for these commands - all of the run
@ -65,352 +64,408 @@ function success(promise) {
/* eslint-disable babel/no-invalid-this */ /* eslint-disable babel/no-invalid-this */
const commands = { export const CommandMap = {
ddg: new Command("ddg", "<query>", function(roomId, args) { ddg: new Command({
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); name: 'ddg',
// TODO Don't explain this away, actually show a search UI here. args: '<query>',
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { description: _td('Searches DuckDuckGo for results'),
title: _t('/ddg is not a command'), runFn: function(roomId, args) {
description: _t('To use it, just wait for autocomplete results to load and tab through them.'), const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
}); // TODO Don't explain this away, actually show a search UI here.
return success(); Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
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: new Command("nick", "<display_name>", function(roomId, args) { name: 'nick',
if (args) { args: '<display_name>',
return success( description: _td('Changes your display nickname'),
MatrixClientPeg.get().setDisplayName(args), runFn: function(roomId, args) {
); if (args) {
} return success(MatrixClientPeg.get().setDisplayName(args));
return reject(this.getUsage());
}),
// Changes the colorscheme of your current room
tint: new Command("tint", "<color1> [<color2>]", function(roomId, args) {
if (args) {
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]);
const colorScheme = {};
colorScheme.primary_color = matches[1];
if (matches[4]) {
colorScheme.secondary_color = matches[4];
} else {
colorScheme.secondary_color = colorScheme.primary_color;
}
return success(
SettingsStore.setValue("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, colorScheme),
);
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
// Change the room topic tint: new Command({
topic: new Command("topic", "<topic>", function(roomId, args) { name: 'tint',
if (args) { args: '<color1> [<color2>]',
return success( description: _td('Changes colour scheme of current room'),
MatrixClientPeg.get().setRoomTopic(roomId, args), runFn: function(roomId, args) {
); if (args) {
} const matches = args.match(/^(#([\da-fA-F]{3}|[\da-fA-F]{6}))( +(#([\da-fA-F]{3}|[\da-fA-F]{6})))?$/);
return reject(this.getUsage()); if (matches) {
}), Tinter.tint(matches[1], matches[4]);
const colorScheme = {};
// Invite a user colorScheme.primary_color = matches[1];
invite: new Command("invite", "<userId>", function(roomId, args) { if (matches[4]) {
if (args) { colorScheme.secondary_color = matches[4];
const matches = args.match(/^(\S+)$/); } else {
if (matches) { colorScheme.secondary_color = colorScheme.primary_color;
return success(
MatrixClientPeg.get().invite(roomId, matches[1]),
);
}
}
return reject(this.getUsage());
}),
// Join a room
join: new Command("join", "#alias:domain", function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
let roomAlias = matches[1];
if (roomAlias[0] !== '#') {
return reject(this.getUsage());
}
if (!roomAlias.match(/:/)) {
roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
dis.dispatch({
action: 'view_room',
room_alias: roomAlias,
auto_join: true,
});
return success();
}
}
return reject(this.getUsage());
}),
part: new Command("part", "[#alias:domain]", function(roomId, args) {
let targetRoomId;
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
let roomAlias = matches[1];
if (roomAlias[0] !== '#') {
return reject(this.getUsage());
}
if (!roomAlias.match(/:/)) {
roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
// Try to find a room with this alias
const rooms = MatrixClientPeg.get().getRooms();
for (let i = 0; i < rooms.length; i++) {
const aliasEvents = rooms[i].currentState.getStateEvents(
"m.room.aliases",
);
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;
}
}
if (targetRoomId) { break; }
} }
if (targetRoomId) { break; } return success(
} SettingsStore.setValue('roomColor', roomId, SettingLevel.ROOM_ACCOUNT, colorScheme),
if (!targetRoomId) { );
return reject(_t("Unrecognised room alias:") + ' ' + roomAlias);
} }
} }
} return reject(this.getUsage());
if (!targetRoomId) targetRoomId = roomId; },
return success(
MatrixClientPeg.get().leave(targetRoomId).then(
function() {
dis.dispatch({action: 'view_next_room'});
},
),
);
}), }),
// Kick a user from the room with an optional reason topic: new Command({
kick: new Command("kick", "<userId> [<reason>]", function(roomId, args) { name: 'topic',
if (args) { args: '<topic>',
const matches = args.match(/^(\S+?)( +(.*))?$/); description: _td('Sets the room topic'),
if (matches) { runFn: function(roomId, args) {
return success( if (args) {
MatrixClientPeg.get().kick(roomId, matches[1], matches[3]), return success(MatrixClientPeg.get().setRoomTopic(roomId, args));
);
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}),
invite: new Command({
name: 'invite',
args: '<user-id>',
description: _td('Invites user with given id to current room'),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
return success(MatrixClientPeg.get().invite(roomId, matches[1]));
}
}
return reject(this.getUsage());
},
}),
join: new Command({
name: 'join',
args: '<room-alias>',
description: _td('Joins room with given alias'),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
let roomAlias = matches[1];
if (roomAlias[0] !== '#') return reject(this.getUsage());
if (!roomAlias.includes(':')) {
roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
dis.dispatch({
action: 'view_room',
room_alias: roomAlias,
auto_join: true,
});
return success();
}
}
return reject(this.getUsage());
},
}),
part: new Command({
name: 'part',
args: '[<room-alias>]',
description: _td('Leave room'),
runFn: function(roomId, args) {
const cli = MatrixClientPeg.get();
let targetRoomId;
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
let roomAlias = matches[1];
if (roomAlias[0] !== '#') return reject(this.getUsage());
if (!roomAlias.includes(':')) {
roomAlias += ':' + cli.getDomain();
}
// Try to find a room with this alias
const rooms = cli.getRooms();
for (let i = 0; i < rooms.length; i++) {
const aliasEvents = rooms[i].currentState.getStateEvents('m.room.aliases');
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;
}
}
if (targetRoomId) break;
}
if (targetRoomId) break;
}
if (!targetRoomId) return reject(_t('Unrecognised room alias:') + ' ' + roomAlias);
}
}
if (!targetRoomId) targetRoomId = roomId;
return success(
cli.leave(targetRoomId).then(function() {
dis.dispatch({action: 'view_next_room'});
}),
);
},
}),
kick: new Command({
name: 'kick',
args: '<user-id> [reason]',
description: _td('Kicks user with given id'),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+?)( +(.*))?$/);
if (matches) {
return success(MatrixClientPeg.get().kick(roomId, matches[1], matches[3]));
}
}
return reject(this.getUsage());
},
}), }),
// Ban a user from the room with an optional reason // Ban a user from the room with an optional reason
ban: new Command("ban", "<userId> [<reason>]", function(roomId, args) { ban: new Command({
if (args) { name: 'ban',
const matches = args.match(/^(\S+?)( +(.*))?$/); args: '<user-id> [reason]',
if (matches) { description: _td('Bans user with given id'),
return success( runFn: function(roomId, args) {
MatrixClientPeg.get().ban(roomId, matches[1], matches[3]), if (args) {
); const matches = args.match(/^(\S+?)( +(.*))?$/);
if (matches) {
return success(MatrixClientPeg.get().ban(roomId, matches[1], matches[3]));
}
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
// Unban a user from the room // Unban a user from ythe room
unban: new Command("unban", "<userId>", function(roomId, args) { unban: new Command({
if (args) { name: 'unban',
const matches = args.match(/^(\S+)$/); args: '<user-id>',
if (matches) { description: _td('Unbans user with given id'),
// Reset the user membership to "leave" to unban him runFn: function(roomId, args) {
return success( if (args) {
MatrixClientPeg.get().unban(roomId, matches[1]), const matches = args.match(/^(\S+)$/);
); if (matches) {
// Reset the user membership to "leave" to unban him
return success(MatrixClientPeg.get().unban(roomId, matches[1]));
}
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
ignore: new Command("ignore", "<userId>", function(roomId, args) { ignore: new Command({
if (args) { name: 'ignore',
const matches = args.match(/^(\S+)$/); args: '<user-id>',
if (matches) { description: _td('Ignores a user, hiding their messages from you'),
const userId = matches[1]; runFn: function(roomId, args) {
const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers(); if (args) {
ignoredUsers.push(userId); // de-duped internally in the js-sdk const cli = MatrixClientPeg.get();
return success(
MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => { const matches = args.match(/^(\S+)$/);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); if (matches) {
Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, { const userId = matches[1];
title: _t("Ignored user"), const ignoredUsers = cli.getIgnoredUsers();
description: ( ignoredUsers.push(userId); // de-duped internally in the js-sdk
<div> return success(
<p>{ _t("You are now ignoring %(userId)s", {userId: userId}) }</p> cli.setIgnoredUsers(ignoredUsers).then(() => {
</div> const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
), Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, {
hasCancelButton: false, title: _t('Ignored user'),
}); description: <div>
}), <p>{ _t('You are now ignoring %(userId)s', {userId}) }</p>
); </div>,
hasCancelButton: false,
});
}),
);
}
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
unignore: new Command("unignore", "<userId>", function(roomId, args) { unignore: new Command({
if (args) { name: 'unignore',
const matches = args.match(/^(\S+)$/); args: '<user-id>',
if (matches) { description: _td('Stops ignoring a user, showing their messages going forward'),
const userId = matches[1]; runFn: function(roomId, args) {
const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers(); if (args) {
const index = ignoredUsers.indexOf(userId); const cli = MatrixClientPeg.get();
if (index !== -1) ignoredUsers.splice(index, 1);
return success( const matches = args.match(/^(\S+)$/);
MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => { if (matches) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const userId = matches[1];
Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, { const ignoredUsers = cli.getIgnoredUsers();
title: _t("Unignored user"), const index = ignoredUsers.indexOf(userId);
description: ( if (index !== -1) ignoredUsers.splice(index, 1);
<div> return success(
<p>{ _t("You are no longer ignoring %(userId)s", {userId: userId}) }</p> cli.setIgnoredUsers(ignoredUsers).then(() => {
</div> const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
), Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, {
hasCancelButton: false, title: _t('Unignored user'),
}); description: <div>
}), <p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p>
); </div>,
hasCancelButton: false,
});
}),
);
}
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
// Define the power level of a user // Define the power level of a user
op: new Command("op", "<userId> [<power level>]", function(roomId, args) { op: new Command({
if (args) { name: 'op',
const matches = args.match(/^(\S+?)( +(-?\d+))?$/); args: '<user-id> [<power-level>]',
let powerLevel = 50; // default power level for op description: _td('Define the power level of a user'),
if (matches) { runFn: function(roomId, args) {
const userId = matches[1]; if (args) {
if (matches.length === 4 && undefined !== matches[3]) { const matches = args.match(/^(\S+?)( +(-?\d+))?$/);
powerLevel = parseInt(matches[3]); let powerLevel = 50; // default power level for op
} if (matches) {
if (!isNaN(powerLevel)) { const userId = matches[1];
const room = MatrixClientPeg.get().getRoom(roomId); if (matches.length === 4 && undefined !== matches[3]) {
if (!room) { powerLevel = parseInt(matches[3]);
return reject("Bad room ID: " + roomId); }
if (!isNaN(powerLevel)) {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (!room) return reject('Bad room ID: ' + roomId);
const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent));
} }
const powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", "",
);
return success(
MatrixClientPeg.get().setPowerLevel(
roomId, userId, powerLevel, powerLevelEvent,
),
);
} }
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
// Reset the power level of a user // Reset the power level of a user
deop: new Command("deop", "<userId>", function(roomId, args) { deop: new Command({
if (args) { name: 'deop',
const matches = args.match(/^(\S+)$/); args: '<user-id>',
if (matches) { description: _td('Deops user with given id'),
const room = MatrixClientPeg.get().getRoom(roomId); runFn: function(roomId, args) {
if (!room) { if (args) {
return reject("Bad room ID: " + roomId); const matches = args.match(/^(\S+)$/);
} if (matches) {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (!room) return reject('Bad room ID: ' + roomId);
const powerLevelEvent = room.currentState.getStateEvents( const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
"m.room.power_levels", "", return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent));
); }
return success(
MatrixClientPeg.get().setPowerLevel(
roomId, args, undefined, powerLevelEvent,
),
);
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}), }),
// Open developer tools devtools: new Command({
devtools: new Command("devtools", "", function(roomId) { name: 'devtools',
const DevtoolsDialog = sdk.getComponent("dialogs.DevtoolsDialog"); description: _td('Opens the Developer Tools dialog'),
Modal.createDialog(DevtoolsDialog, { roomId }); runFn: function(roomId) {
return success(); const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
Modal.createDialog(DevtoolsDialog, {roomId});
return success();
},
}), }),
// Verify a user, device, and pubkey tuple // Verify a user, device, and pubkey tuple
verify: new Command("verify", "<userId> <deviceId> <deviceSigningKey>", function(roomId, args) { verify: new Command({
if (args) { name: 'verify',
const matches = args.match(/^(\S+) +(\S+) +(\S+)$/); args: '<user-id> <device-id> <device-signing-key>',
if (matches) { description: _td('Verifies a user, device, and pubkey tuple'),
const userId = matches[1]; runFn: function(roomId, args) {
const deviceId = matches[2]; if (args) {
const fingerprint = matches[3]; const matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
if (matches) {
const cli = MatrixClientPeg.get();
return success( const userId = matches[1];
// Promise.resolve to handle transition from static result to promise; can be removed const deviceId = matches[2];
// in future const fingerprint = matches[3];
Promise.resolve(MatrixClientPeg.get().getStoredDevice(userId, deviceId)).then((device) => {
if (!device) {
throw new Error(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`);
}
if (device.isVerified()) { return success(
if (device.getFingerprint() === fingerprint) { // Promise.resolve to handle transition from static result to promise; can be removed
throw new Error(_t(`Device already verified!`)); // in future
} else { Promise.resolve(cli.getStoredDevice(userId, deviceId)).then((device) => {
throw new Error(_t(`WARNING: Device already verified, but keys do NOT MATCH!`)); if (!device) {
throw new Error(_t('Unknown (user, device) pair:') + ` (${userId}, ${deviceId})`);
} }
}
if (device.getFingerprint() !== fingerprint) { if (device.isVerified()) {
const fprint = device.getFingerprint(); if (device.getFingerprint() === fingerprint) {
throw new Error( throw new Error(_t('Device already verified!'));
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' + } else {
' %(deviceId)s is "%(fprint)s" which does not match the provided key' + throw new Error(_t('WARNING: Device already verified, but keys do NOT MATCH!'));
' "%(fingerprint)s". This could mean your communications are being intercepted!', }
{deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint})); }
}
return MatrixClientPeg.get().setDeviceVerified(userId, deviceId, true); if (device.getFingerprint() !== fingerprint) {
}).then(() => { const fprint = device.getFingerprint();
// Tell the user we verified everything throw new Error(
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, { ' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
title: _t("Verified key"), '"%(fingerprint)s". This could mean your communications are being intercepted!',
description: ( {
<div> fprint,
<p> userId,
{ deviceId,
_t("The signing key you provided matches the signing key you received " + fingerprint,
"from %(userId)s's device %(deviceId)s. Device marked as verified.", }));
{userId: userId, deviceId: deviceId}) }
}
</p> return cli.setDeviceVerified(userId, deviceId, true);
</div> }).then(() => {
), // Tell the user we verified everything
hasCancelButton: false, const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
}); Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, {
}), title: _t('Verified key'),
); description: <div>
<p>
{
_t('The signing key you provided matches the signing key you received ' +
'from %(userId)s\'s device %(deviceId)s. Device marked as verified.',
{userId, deviceId})
}
</p>
</div>,
hasCancelButton: false,
});
}),
);
}
} }
} return reject(this.getUsage());
return reject(this.getUsage()); },
}),
// Command definitions for autocompletion ONLY:
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
me: new Command({
name: 'me',
args: '<message>',
description: _td('Displays action'),
}), }),
}; };
/* eslint-enable babel/no-invalid-this */ /* eslint-enable babel/no-invalid-this */
@ -421,50 +476,39 @@ const aliases = {
j: "join", j: "join",
}; };
module.exports = { /**
/** * Process the given text for /commands and perform them.
* Process the given text for /commands and perform them. * @param {string} roomId The room in which the command was performed.
* @param {string} roomId The room in which the command was performed. * @param {string} input The raw text input by the user.
* @param {string} input The raw text input by the user. * @return {Object|null} An object with the property 'error' if there was an error
* @return {Object|null} An object with the property 'error' if there was an error * processing the command, or 'promise' if a request was sent out.
* processing the command, or 'promise' if a request was sent out. * Returns null if the input didn't match a command.
* Returns null if the input didn't match a command. */
*/ export function processCommandInput(roomId, input) {
processInput: function(roomId, input) { // trim any trailing whitespace, as it can confuse the parser for
// trim any trailing whitespace, as it can confuse the parser for // IRC-style commands
// IRC-style commands input = input.replace(/\s+$/, '');
input = input.replace(/\s+$/, ""); if (input[0] !== '/' || input[1] === '/') return null; // not a command
if (input[0] === "/" && input[1] !== "/") {
const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
let cmd;
let args;
if (bits) {
cmd = bits[1].substring(1).toLowerCase();
args = bits[3];
} else {
cmd = input;
}
if (cmd === "me") return null;
if (aliases[cmd]) {
cmd = aliases[cmd];
}
if (commands[cmd]) {
return commands[cmd].run(roomId, args);
} else {
return reject(_t("Unrecognised command:") + ' ' + input);
}
}
return null; // not a command
},
getCommandList: function() { const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
// Return all the commands plus /me and /markdown which aren't handled like normal commands let cmd;
const cmds = Object.keys(commands).sort().map(function(cmdKey) { let args;
return commands[cmdKey]; if (bits) {
}); cmd = bits[1].substring(1).toLowerCase();
cmds.push(new Command("me", "<action>", function() {})); args = bits[3];
cmds.push(new Command("markdown", "<on|off>", function() {})); } else {
cmd = input;
}
return cmds; if (aliases[cmd]) {
}, cmd = aliases[cmd];
}; }
if (CommandMap[cmd]) {
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
if (!CommandMap[cmd].runFn) return null;
return CommandMap[cmd].run(roomId, args);
} else {
return reject(_t('Unrecognised command:') + ' ' + input);
}
}

View file

@ -2,6 +2,7 @@
Copyright 2016 Aviral Dasgupta Copyright 2016 Aviral Dasgupta
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd Copyright 2017 New Vector Ltd
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,103 +18,16 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import { _t, _td } from '../languageHandler'; import {_t} from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import FuzzyMatcher from './FuzzyMatcher'; import FuzzyMatcher from './FuzzyMatcher';
import {TextualCompletion} from './Components'; import {TextualCompletion} from './Components';
import {CommandMap} from '../SlashCommands';
import type {SelectionRange} from "./Autocompleter";
// TODO merge this with the factory mechanics of SlashCommands? const COMMANDS = Object.values(CommandMap);
// 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',
args: '<message>',
description: _td('Displays action'),
},
{
command: '/ban',
args: '<user-id> [reason]',
description: _td('Bans user with given id'),
},
{
command: '/unban',
args: '<user-id>',
description: _td('Unbans user with given id'),
},
{
command: '/op',
args: '<user-id> [<power-level>]',
description: _td('Define the power level of a user'),
},
{
command: '/deop',
args: '<user-id>',
description: _td('Deops user with given id'),
},
{
command: '/invite',
args: '<user-id>',
description: _td('Invites user with given id to current room'),
},
{
command: '/join',
args: '<room-alias>',
description: _td('Joins room with given alias'),
},
{
command: '/part',
args: '[<room-alias>]',
description: _td('Leave room'),
},
{
command: '/topic',
args: '<topic>',
description: _td('Sets the room topic'),
},
{
command: '/kick',
args: '<user-id> [reason]',
description: _td('Kicks user with given id'),
},
{
command: '/nick',
args: '<display-name>',
description: _td('Changes your display nickname'),
},
{
command: '/ddg',
args: '<query>',
description: _td('Searches DuckDuckGo for results'),
},
{
command: '/tint',
args: '<color1> [<color2>]',
description: _td('Changes colour scheme of current room'),
},
{
command: '/verify',
args: '<user-id> <device-id> <device-signing-key>',
description: _td('Verifies a user, device, and pubkey tuple'),
},
{
command: '/ignore',
args: '<user-id>',
description: _td('Ignores a user, hiding their messages from you'),
},
{
command: '/unignore',
args: '<user-id>',
description: _td('Stops ignoring a user, showing their messages going forward'),
},
{
command: '/devtools',
args: '',
description: _td('Opens the Developer Tools dialog'),
},
// Omitting `/markdown` as it only seems to apply to OldComposer
];
const COMMAND_RE = /(^\/\w*)/g; const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
export default class CommandProvider extends AutocompleteProvider { export default class CommandProvider extends AutocompleteProvider {
constructor() { constructor() {
@ -123,23 +37,36 @@ export default class CommandProvider extends AutocompleteProvider {
}); });
} }
async getCompletions(query: string, selection: {start: number, end: number}) { async getCompletions(query: string, selection: SelectionRange, force?: boolean) {
let completions = [];
const {command, range} = this.getCurrentCommand(query, selection); const {command, range} = this.getCurrentCommand(query, selection);
if (command) { if (!command) return [];
completions = this.matcher.match(command[0]).map((result) => {
return { let matches = [];
completion: result.command + ' ', if (command[0] !== command[1]) {
component: (<TextualCompletion // The input looks like a command with arguments, perform exact match
title={result.command} const name = command[1].substr(1); // strip leading `/`
subtitle={result.args} if (CommandMap[name]) {
description={_t(result.description)} matches = [CommandMap[name]];
/>), }
range, } else {
}; if (query === '/') {
}); // If they have just entered `/` show everything
matches = COMMANDS;
} else {
// otherwise fuzzy match against all of the fields
matches = this.matcher.match(command[1]);
}
} }
return completions;
return matches.map((result) => ({
// If the command is the same as the one they entered, we don't want to discard their arguments
completion: result.command === command[1] ? command[0] : (result.command + ' '),
component: <TextualCompletion
title={result.command}
subtitle={result.args}
description={_t(result.description)} />,
range,
}));
} }
getName() { getName() {

View file

@ -68,8 +68,8 @@ const FilePanel = React.createClass({
"room": { "room": {
"timeline": { "timeline": {
"contains_url": true, "contains_url": true,
"not_types": [ "types": [
"m.sticker", "m.room.message",
], ],
}, },
}, },

View file

@ -1059,7 +1059,7 @@ export default React.createClass({
<input type="radio" <input type="radio"
value={GROUP_JOINPOLICY_INVITE} value={GROUP_JOINPOLICY_INVITE}
checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_INVITE} checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_INVITE}
onClick={this._onJoinableChange} onChange={this._onJoinableChange}
/> />
<div className="mx_GroupView_label_text"> <div className="mx_GroupView_label_text">
{ _t('Only people who have been invited') } { _t('Only people who have been invited') }
@ -1071,7 +1071,7 @@ export default React.createClass({
<input type="radio" <input type="radio"
value={GROUP_JOINPOLICY_OPEN} value={GROUP_JOINPOLICY_OPEN}
checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_OPEN} checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_OPEN}
onClick={this._onJoinableChange} onChange={this._onJoinableChange}
/> />
<div className="mx_GroupView_label_text"> <div className="mx_GroupView_label_text">
{ _t('Everyone') } { _t('Everyone') }
@ -1134,10 +1134,6 @@ export default React.createClass({
let avatarNode; let avatarNode;
let nameNode; let nameNode;
let shortDescNode; let shortDescNode;
const bodyNodes = [
this._getMembershipSection(),
this._getGroupSection(),
];
const rightButtons = []; const rightButtons = [];
if (this.state.editing && this.state.isUserPrivileged) { if (this.state.editing && this.state.isUserPrivileged) {
let avatarImage; let avatarImage;
@ -1282,7 +1278,8 @@ export default React.createClass({
</div> </div>
</div> </div>
<GeminiScrollbarWrapper className="mx_GroupView_body"> <GeminiScrollbarWrapper className="mx_GroupView_body">
{ bodyNodes } { this._getMembershipSection() }
{ this._getGroupSection() }
</GeminiScrollbarWrapper> </GeminiScrollbarWrapper>
</div> </div>
); );

View file

@ -82,17 +82,26 @@ var LeftPanel = React.createClass({
_onKeyDown: function(ev) { _onKeyDown: function(ev) {
if (!this.focusedElement) return; if (!this.focusedElement) return;
let handled = false; let handled = true;
switch (ev.keyCode) { switch (ev.keyCode) {
case KeyCode.TAB:
this._onMoveFocus(ev.shiftKey);
break;
case KeyCode.UP: case KeyCode.UP:
this._onMoveFocus(true); this._onMoveFocus(true);
handled = true;
break; break;
case KeyCode.DOWN: case KeyCode.DOWN:
this._onMoveFocus(false); this._onMoveFocus(false);
handled = true;
break; break;
case KeyCode.ENTER:
this._onMoveFocus(false);
if (this.focusedElement) {
this.focusedElement.click();
}
break;
default:
handled = false;
} }
if (handled) { if (handled) {
@ -102,37 +111,33 @@ var LeftPanel = React.createClass({
}, },
_onMoveFocus: function(up) { _onMoveFocus: function(up) {
var element = this.focusedElement; let element = this.focusedElement;
// unclear why this isn't needed // unclear why this isn't needed
// var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending; // var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
// this.focusDirection = up; // this.focusDirection = up;
var descending = false; // are we currently descending or ascending through the DOM tree? let descending = false; // are we currently descending or ascending through the DOM tree?
var classes; let classes;
do { do {
var child = up ? element.lastElementChild : element.firstElementChild; const child = up ? element.lastElementChild : element.firstElementChild;
var sibling = up ? element.previousElementSibling : element.nextElementSibling; const sibling = up ? element.previousElementSibling : element.nextElementSibling;
if (descending) { if (descending) {
if (child) { if (child) {
element = child; element = child;
} } else if (sibling) {
else if (sibling) {
element = sibling; element = sibling;
} } else {
else {
descending = false; descending = false;
element = element.parentElement; element = element.parentElement;
} }
} } else {
else {
if (sibling) { if (sibling) {
element = sibling; element = sibling;
descending = true; descending = true;
} } else {
else {
element = element.parentElement; element = element.parentElement;
} }
} }
@ -144,8 +149,7 @@ var LeftPanel = React.createClass({
descending = true; descending = true;
} }
} }
} while (element && !(
} while(element && !(
classes.contains("mx_RoomTile") || classes.contains("mx_RoomTile") ||
classes.contains("mx_SearchBox_search") || classes.contains("mx_SearchBox_search") ||
classes.contains("mx_RoomSubList_ellipsis"))); classes.contains("mx_RoomSubList_ellipsis")));

View file

@ -23,6 +23,7 @@ import PropTypes from 'prop-types';
import Matrix from "matrix-js-sdk"; import Matrix from "matrix-js-sdk";
import Analytics from "../../Analytics"; import Analytics from "../../Analytics";
import DecryptionFailureTracker from "../../DecryptionFailureTracker";
import MatrixClientPeg from "../../MatrixClientPeg"; import MatrixClientPeg from "../../MatrixClientPeg";
import PlatformPeg from "../../PlatformPeg"; import PlatformPeg from "../../PlatformPeg";
import SdkConfig from "../../SdkConfig"; import SdkConfig from "../../SdkConfig";
@ -1303,6 +1304,21 @@ export default React.createClass({
} }
}); });
const dft = new DecryptionFailureTracker((failure) => {
// TODO: Pass reason for failure as third argument to trackEvent
Analytics.trackEvent('E2E', 'Decryption failure');
});
// Shelved for later date when we have time to think about persisting history of
// tracked events across sessions.
// dft.loadTrackedEventHashMap();
dft.start();
// When logging out, stop tracking failures and destroy state
cli.on("Session.logged_out", () => dft.stop());
cli.on("Event.decrypted", (e) => dft.eventDecrypted(e));
const krh = new KeyRequestHandler(cli); const krh = new KeyRequestHandler(cli);
cli.on("crypto.roomKeyRequest", (req) => { cli.on("crypto.roomKeyRequest", (req) => {
krh.handleKeyRequest(req); krh.handleKeyRequest(req);

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -25,6 +26,9 @@ import sdk from '../../index';
import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientPeg from '../../MatrixClientPeg';
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
const continuedTypes = ['m.sticker', 'm.room.message'];
/* (almost) stateless UI component which builds the event tiles in the room timeline. /* (almost) stateless UI component which builds the event tiles in the room timeline.
*/ */
module.exports = React.createClass({ module.exports = React.createClass({
@ -189,7 +193,7 @@ module.exports = React.createClass({
/** /**
* Page up/down. * Page up/down.
* *
* mult: -1 to page up, +1 to page down * @param {number} mult: -1 to page up, +1 to page down
*/ */
scrollRelative: function(mult) { scrollRelative: function(mult) {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
@ -199,6 +203,8 @@ module.exports = React.createClass({
/** /**
* Scroll up/down in response to a scroll key * Scroll up/down in response to a scroll key
*
* @param {KeyboardEvent} ev: the keyboard event to handle
*/ */
handleScrollKey: function(ev) { handleScrollKey: function(ev) {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
@ -257,6 +263,7 @@ module.exports = React.createClass({
this.eventNodes = {}; this.eventNodes = {};
let visible = false;
let i; let i;
// first figure out which is the last event in the list which we're // first figure out which is the last event in the list which we're
@ -297,7 +304,7 @@ module.exports = React.createClass({
// if the readmarker has moved, cancel any active ghost. // if the readmarker has moved, cancel any active ghost.
if (this.currentReadMarkerEventId && this.props.readMarkerEventId && if (this.currentReadMarkerEventId && this.props.readMarkerEventId &&
this.props.readMarkerVisible && this.props.readMarkerVisible &&
this.currentReadMarkerEventId != this.props.readMarkerEventId) { this.currentReadMarkerEventId !== this.props.readMarkerEventId) {
this.currentGhostEventId = null; this.currentGhostEventId = null;
} }
@ -404,8 +411,8 @@ module.exports = React.createClass({
let isVisibleReadMarker = false; let isVisibleReadMarker = false;
if (eventId == this.props.readMarkerEventId) { if (eventId === this.props.readMarkerEventId) {
var visible = this.props.readMarkerVisible; visible = this.props.readMarkerVisible;
// if the read marker comes at the end of the timeline (except // if the read marker comes at the end of the timeline (except
// for local echoes, which are excluded from RMs, because they // for local echoes, which are excluded from RMs, because they
@ -423,11 +430,11 @@ module.exports = React.createClass({
// XXX: there should be no need for a ghost tile - we should just use a // XXX: there should be no need for a ghost tile - we should just use a
// a dispatch (user_activity_end) to start the RM animation. // a dispatch (user_activity_end) to start the RM animation.
if (eventId == this.currentGhostEventId) { if (eventId === this.currentGhostEventId) {
// if we're showing an animation, continue to show it. // if we're showing an animation, continue to show it.
ret.push(this._getReadMarkerGhostTile()); ret.push(this._getReadMarkerGhostTile());
} else if (!isVisibleReadMarker && } else if (!isVisibleReadMarker &&
eventId == this.currentReadMarkerEventId) { eventId === this.currentReadMarkerEventId) {
// there is currently a read-up-to marker at this point, but no // there is currently a read-up-to marker at this point, but no
// more. Show an animation of it disappearing. // more. Show an animation of it disappearing.
ret.push(this._getReadMarkerGhostTile()); ret.push(this._getReadMarkerGhostTile());
@ -449,16 +456,17 @@ module.exports = React.createClass({
// Some events should appear as continuations from previous events of // Some events should appear as continuations from previous events of
// different types. // different types.
const continuedTypes = ['m.sticker', 'm.room.message'];
const eventTypeContinues = const eventTypeContinues =
prevEvent !== null && prevEvent !== null &&
continuedTypes.includes(mxEv.getType()) && continuedTypes.includes(mxEv.getType()) &&
continuedTypes.includes(prevEvent.getType()); continuedTypes.includes(prevEvent.getType());
if (prevEvent !== null // if there is a previous event and it has the same sender as this event
&& prevEvent.sender && mxEv.sender // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL
&& mxEv.sender.userId === prevEvent.sender.userId if (prevEvent !== null && prevEvent.sender && mxEv.sender && mxEv.sender.userId === prevEvent.sender.userId &&
&& (mxEv.getType() == prevEvent.getType() || eventTypeContinues)) { (mxEv.getType() === prevEvent.getType() || eventTypeContinues) &&
(mxEv.getTs() - prevEvent.getTs() <= CONTINUATION_MAX_INTERVAL)) {
continuation = true; continuation = true;
} }
@ -493,7 +501,7 @@ module.exports = React.createClass({
} }
const eventId = mxEv.getId(); const eventId = mxEv.getId();
const highlight = (eventId == this.props.highlightedEventId); const highlight = (eventId === this.props.highlightedEventId);
// we can't use local echoes as scroll tokens, because their event IDs change. // we can't use local echoes as scroll tokens, because their event IDs change.
// Local echos have a send "status". // Local echos have a send "status".
@ -632,7 +640,8 @@ module.exports = React.createClass({
render: function() { render: function() {
const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
const Spinner = sdk.getComponent("elements.Spinner"); const Spinner = sdk.getComponent("elements.Spinner");
let topSpinner, bottomSpinner; let topSpinner;
let bottomSpinner;
if (this.props.backPaginating) { if (this.props.backPaginating) {
topSpinner = <li key="_topSpinner"><Spinner /></li>; topSpinner = <li key="_topSpinner"><Spinner /></li>;
} }

View file

@ -70,7 +70,7 @@ export default withMatrixClient(React.createClass({
if (this.state.groups) { if (this.state.groups) {
const groupNodes = []; const groupNodes = [];
this.state.groups.forEach((g) => { this.state.groups.forEach((g) => {
groupNodes.push(<GroupTile groupId={g} />); groupNodes.push(<GroupTile key={g} groupId={g} />);
}); });
contentHeader = groupNodes.length > 0 ? <h3>{ _t('Your Communities') }</h3> : <div />; contentHeader = groupNodes.length > 0 ? <h3>{ _t('Your Communities') }</h3> : <div />;
content = groupNodes.length > 0 ? content = groupNodes.length > 0 ?
@ -124,7 +124,7 @@ export default withMatrixClient(React.createClass({
) } ) }
</div> </div>
</div> </div>
<div className="mx_MyGroups_joinBox mx_MyGroups_headerCard"> {/*<div className="mx_MyGroups_joinBox mx_MyGroups_headerCard">
<AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onJoinGroupClick}> <AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onJoinGroupClick}>
<TintableSvg src="img/icons-create-room.svg" width="50" height="50" /> <TintableSvg src="img/icons-create-room.svg" width="50" height="50" />
</AccessibleButton> </AccessibleButton>
@ -140,7 +140,7 @@ export default withMatrixClient(React.createClass({
{ 'i': (sub) => <i>{ sub }</i> }) { 'i': (sub) => <i>{ sub }</i> })
} }
</div> </div>
</div> </div>*/}
</div> </div>
<div className="mx_MyGroups_content"> <div className="mx_MyGroups_content">
{ contentHeader } { contentHeader }

View file

@ -913,6 +913,8 @@ module.exports = React.createClass({
}, },
uploadFile: async function(file) { uploadFile: async function(file) {
dis.dispatch({action: 'focus_composer'});
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({action: 'view_set_mxid'}); dis.dispatch({action: 'view_set_mxid'});
return; return;

View file

@ -429,7 +429,6 @@ module.exports = React.createClass({
"push notifications on other devices until you log back in to them", "push notifications on other devices until you log back in to them",
) + ".", ) + ".",
}); });
dis.dispatch({action: 'password_changed'});
}, },
_onAddEmailEditFinished: function(value, shouldSubmit) { _onAddEmailEditFinished: function(value, shouldSubmit) {

View file

@ -253,13 +253,11 @@ module.exports = React.createClass({
</div> </div>
); );
if (SettingsStore.isFeatureEnabled("feature_rich_quoting")) { replyButton = (
replyButton = ( <div className="mx_MessageContextMenu_field" onClick={this.onReplyClick}>
<div className="mx_MessageContextMenu_field" onClick={this.onReplyClick}> { _t('Reply') }
{ _t('Reply') } </div>
</div> );
);
}
if (this.state.canPin) { if (this.state.canPin) {
pinButton = ( pinButton = (

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd Copyright 2017, 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,7 +17,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index'; import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import Promise from 'bluebird'; import Promise from 'bluebird';
@ -27,6 +27,13 @@ import GroupStore from '../../../stores/GroupStore';
const TRUNCATE_QUERY_LIST = 40; const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
const addressTypeName = {
'mx-user-id': _td("Matrix ID"),
'mx-room-id': _td("Matrix Room ID"),
'email': _td("email address"),
};
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "AddressPickerDialog", displayName: "AddressPickerDialog",
@ -66,7 +73,7 @@ module.exports = React.createClass({
// List of UserAddressType objects representing // List of UserAddressType objects representing
// the list of addresses we're going to invite // the list of addresses we're going to invite
userList: [], selectedList: [],
// Whether a search is ongoing // Whether a search is ongoing
busy: false, busy: false,
@ -76,10 +83,9 @@ module.exports = React.createClass({
serverSupportsUserDirectory: true, serverSupportsUserDirectory: true,
// The query being searched for // The query being searched for
query: "", query: "",
// List of UserAddressType objects representing // List of UserAddressType objects representing the set of
// the set of auto-completion results for the current search // auto-completion results for the current search query.
// query. suggestedList: [],
queryList: [],
}; };
}, },
@ -91,14 +97,14 @@ module.exports = React.createClass({
}, },
onButtonClick: function() { onButtonClick: function() {
let userList = this.state.userList.slice(); let selectedList = this.state.selectedList.slice();
// Check the text input field to see if user has an unconverted address // Check the text input field to see if user has an unconverted address
// If there is and it's valid add it to the local userList // If there is and it's valid add it to the local selectedList
if (this.refs.textinput.value !== '') { if (this.refs.textinput.value !== '') {
userList = this._addInputToList(); selectedList = this._addInputToList();
if (userList === null) return; if (selectedList === null) return;
} }
this.props.onFinished(true, userList); this.props.onFinished(true, selectedList);
}, },
onCancel: function() { onCancel: function() {
@ -118,18 +124,18 @@ module.exports = React.createClass({
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.moveSelectionDown(); if (this.addressSelector) this.addressSelector.moveSelectionDown();
} else if (this.state.queryList.length > 0 && (e.keyCode === 188 || e.keyCode === 13 || e.keyCode === 9)) { // comma or enter or tab } else if (this.state.suggestedList.length > 0 && (e.keyCode === 188 || e.keyCode === 13 || e.keyCode === 9)) { // comma or enter or tab
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.chooseSelection(); if (this.addressSelector) this.addressSelector.chooseSelection();
} else if (this.refs.textinput.value.length === 0 && this.state.userList.length && e.keyCode === 8) { // backspace } else if (this.refs.textinput.value.length === 0 && this.state.selectedList.length && e.keyCode === 8) { // backspace
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this.onDismissed(this.state.userList.length - 1)(); this.onDismissed(this.state.selectedList.length - 1)();
} else if (e.keyCode === 13) { // enter } else if (e.keyCode === 13) { // enter
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.refs.textinput.value == '') { if (this.refs.textinput.value === '') {
// if there's nothing in the input box, submit the form // if there's nothing in the input box, submit the form
this.onButtonClick(); this.onButtonClick();
} else { } else {
@ -148,7 +154,7 @@ module.exports = React.createClass({
clearTimeout(this.queryChangedDebouncer); clearTimeout(this.queryChangedDebouncer);
} }
// Only do search if there is something to search // Only do search if there is something to search
if (query.length > 0 && query != '@' && query.length >= 2) { if (query.length > 0 && query !== '@' && query.length >= 2) {
this.queryChangedDebouncer = setTimeout(() => { this.queryChangedDebouncer = setTimeout(() => {
if (this.props.pickerType === 'user') { if (this.props.pickerType === 'user') {
if (this.props.groupId) { if (this.props.groupId) {
@ -170,7 +176,7 @@ module.exports = React.createClass({
}, QUERY_USER_DIRECTORY_DEBOUNCE_MS); }, QUERY_USER_DIRECTORY_DEBOUNCE_MS);
} else { } else {
this.setState({ this.setState({
queryList: [], suggestedList: [],
query: "", query: "",
searchError: null, searchError: null,
}); });
@ -179,11 +185,11 @@ module.exports = React.createClass({
onDismissed: function(index) { onDismissed: function(index) {
return () => { return () => {
const userList = this.state.userList.slice(); const selectedList = this.state.selectedList.slice();
userList.splice(index, 1); selectedList.splice(index, 1);
this.setState({ this.setState({
userList: userList, selectedList,
queryList: [], suggestedList: [],
query: "", query: "",
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this._cancelThreepidLookup) this._cancelThreepidLookup();
@ -197,11 +203,11 @@ module.exports = React.createClass({
}, },
onSelected: function(index) { onSelected: function(index) {
const userList = this.state.userList.slice(); const selectedList = this.state.selectedList.slice();
userList.push(this.state.queryList[index]); selectedList.push(this.state.suggestedList[index]);
this.setState({ this.setState({
userList: userList, selectedList,
queryList: [], suggestedList: [],
query: "", query: "",
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this._cancelThreepidLookup) this._cancelThreepidLookup();
@ -379,10 +385,10 @@ module.exports = React.createClass({
}, },
_processResults: function(results, query) { _processResults: function(results, query) {
const queryList = []; const suggestedList = [];
results.forEach((result) => { results.forEach((result) => {
if (result.room_id) { if (result.room_id) {
queryList.push({ suggestedList.push({
addressType: 'mx-room-id', addressType: 'mx-room-id',
address: result.room_id, address: result.room_id,
displayName: result.name, displayName: result.name,
@ -399,7 +405,7 @@ module.exports = React.createClass({
// Return objects, structure of which is defined // Return objects, structure of which is defined
// by UserAddressType // by UserAddressType
queryList.push({ suggestedList.push({
addressType: 'mx-user-id', addressType: 'mx-user-id',
address: result.user_id, address: result.user_id,
displayName: result.display_name, displayName: result.display_name,
@ -413,18 +419,18 @@ module.exports = React.createClass({
// a perfectly valid address if there are close matches. // a perfectly valid address if there are close matches.
const addrType = getAddressType(query); const addrType = getAddressType(query);
if (this.props.validAddressTypes.includes(addrType)) { if (this.props.validAddressTypes.includes(addrType)) {
queryList.unshift({ suggestedList.unshift({
addressType: addrType, addressType: addrType,
address: query, address: query,
isKnown: false, isKnown: false,
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this._cancelThreepidLookup) this._cancelThreepidLookup();
if (addrType == 'email') { if (addrType === 'email') {
this._lookupThreepid(addrType, query).done(); this._lookupThreepid(addrType, query).done();
} }
} }
this.setState({ this.setState({
queryList, suggestedList,
error: false, error: false,
}, () => { }, () => {
if (this.addressSelector) this.addressSelector.moveSelectionTop(); if (this.addressSelector) this.addressSelector.moveSelectionTop();
@ -442,14 +448,14 @@ module.exports = React.createClass({
if (!this.props.validAddressTypes.includes(addrType)) { if (!this.props.validAddressTypes.includes(addrType)) {
this.setState({ error: true }); this.setState({ error: true });
return null; return null;
} else if (addrType == 'mx-user-id') { } else if (addrType === 'mx-user-id') {
const user = MatrixClientPeg.get().getUser(addrObj.address); const user = MatrixClientPeg.get().getUser(addrObj.address);
if (user) { if (user) {
addrObj.displayName = user.displayName; addrObj.displayName = user.displayName;
addrObj.avatarMxc = user.avatarUrl; addrObj.avatarMxc = user.avatarUrl;
addrObj.isKnown = true; addrObj.isKnown = true;
} }
} else if (addrType == 'mx-room-id') { } else if (addrType === 'mx-room-id') {
const room = MatrixClientPeg.get().getRoom(addrObj.address); const room = MatrixClientPeg.get().getRoom(addrObj.address);
if (room) { if (room) {
addrObj.displayName = room.name; addrObj.displayName = room.name;
@ -458,15 +464,15 @@ module.exports = React.createClass({
} }
} }
const userList = this.state.userList.slice(); const selectedList = this.state.selectedList.slice();
userList.push(addrObj); selectedList.push(addrObj);
this.setState({ this.setState({
userList: userList, selectedList,
queryList: [], suggestedList: [],
query: "", query: "",
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this._cancelThreepidLookup) this._cancelThreepidLookup();
return userList; return selectedList;
}, },
_lookupThreepid: function(medium, address) { _lookupThreepid: function(medium, address) {
@ -492,7 +498,7 @@ module.exports = React.createClass({
if (res === null) return null; if (res === null) return null;
if (cancelled) return null; if (cancelled) return null;
this.setState({ this.setState({
queryList: [{ suggestedList: [{
// a UserAddressType // a UserAddressType
addressType: medium, addressType: medium,
address: address, address: address,
@ -510,15 +516,27 @@ module.exports = React.createClass({
const AddressSelector = sdk.getComponent("elements.AddressSelector"); const AddressSelector = sdk.getComponent("elements.AddressSelector");
this.scrollElement = null; this.scrollElement = null;
// map addressType => set of addresses to avoid O(n*m) operation
const selectedAddresses = {};
this.state.selectedList.forEach(({address, addressType}) => {
if (!selectedAddresses[addressType]) selectedAddresses[addressType] = new Set();
selectedAddresses[addressType].add(address);
});
// Filter out any addresses in the above already selected addresses (matching both type and address)
const filteredSuggestedList = this.state.suggestedList.filter(({address, addressType}) => {
return !(selectedAddresses[addressType] && selectedAddresses[addressType].has(address));
});
const query = []; const query = [];
// create the invite list // create the invite list
if (this.state.userList.length > 0) { if (this.state.selectedList.length > 0) {
const AddressTile = sdk.getComponent("elements.AddressTile"); const AddressTile = sdk.getComponent("elements.AddressTile");
for (let i = 0; i < this.state.userList.length; i++) { for (let i = 0; i < this.state.selectedList.length; i++) {
query.push( query.push(
<AddressTile <AddressTile
key={i} key={i}
address={this.state.userList[i]} address={this.state.selectedList[i]}
canDismiss={true} canDismiss={true}
onDismissed={this.onDismissed(i)} onDismissed={this.onDismissed(i)}
showAddress={this.props.pickerType === 'user'} />, showAddress={this.props.pickerType === 'user'} />,
@ -528,7 +546,7 @@ module.exports = React.createClass({
// Add the query at the end // Add the query at the end
query.push( query.push(
<textarea key={this.state.userList.length} <textarea key={this.state.selectedList.length}
rows="1" rows="1"
id="textinput" id="textinput"
ref="textinput" ref="textinput"
@ -543,34 +561,22 @@ module.exports = React.createClass({
let error; let error;
let addressSelector; let addressSelector;
if (this.state.error) { if (this.state.error) {
let tryUsing = ''; const validTypeDescriptions = this.props.validAddressTypes.map((t) => _t(addressTypeName[t]));
const validTypeDescriptions = this.props.validAddressTypes.map((t) => {
return {
'mx-user-id': _t("Matrix ID"),
'mx-room-id': _t("Matrix Room ID"),
'email': _t("email address"),
}[t];
});
tryUsing = _t("Try using one of the following valid address types: %(validTypesList)s.", {
validTypesList: validTypeDescriptions.join(", "),
});
error = <div className="mx_ChatInviteDialog_error"> error = <div className="mx_ChatInviteDialog_error">
{ _t("You have entered an invalid address.") } { _t("You have entered an invalid address.") }
<br /> <br />
{ tryUsing } { _t("Try using one of the following valid address types: %(validTypesList)s.", {
validTypesList: validTypeDescriptions.join(", "),
}) }
</div>; </div>;
} else if (this.state.searchError) { } else if (this.state.searchError) {
error = <div className="mx_ChatInviteDialog_error">{ this.state.searchError }</div>; error = <div className="mx_ChatInviteDialog_error">{ this.state.searchError }</div>;
} else if ( } else if (this.state.query.length > 0 && filteredSuggestedList.length === 0 && !this.state.busy) {
this.state.query.length > 0 &&
this.state.queryList.length === 0 &&
!this.state.busy
) {
error = <div className="mx_ChatInviteDialog_error">{ _t("No results") }</div>; error = <div className="mx_ChatInviteDialog_error">{ _t("No results") }</div>;
} else { } else {
addressSelector = ( addressSelector = (
<AddressSelector ref={(ref) => {this.addressSelector = ref;}} <AddressSelector ref={(ref) => {this.addressSelector = ref;}}
addressList={this.state.queryList} addressList={filteredSuggestedList}
showAddress={this.props.pickerType === 'user'} showAddress={this.props.pickerType === 'user'}
onSelected={this.onSelected} onSelected={this.onSelected}
truncateAt={TRUNCATE_QUERY_LIST} truncateAt={TRUNCATE_QUERY_LIST}

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -28,6 +29,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.onFinished = this.onFinished.bind(this);
this.onRoomTileClick = this.onRoomTileClick.bind(this); this.onRoomTileClick = this.onRoomTileClick.bind(this);
this.state = { this.state = {
@ -53,10 +55,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
const room = client.getRoom(roomId); const room = client.getRoom(roomId);
if (room) { if (room) {
const me = room.getMember(client.credentials.userId); const me = room.getMember(client.credentials.userId);
const highlight = ( const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === "invite";
room.getUnreadNotificationCount('highlight') > 0 ||
me.membership == "invite"
);
tiles.push( tiles.push(
<RoomTile key={room.roomId} room={room} <RoomTile key={room.roomId} room={room}
transparent={true} transparent={true}
@ -64,7 +63,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
selected={false} selected={false}
unread={Unread.doesRoomHaveUnreadMessages(room)} unread={Unread.doesRoomHaveUnreadMessages(room)}
highlight={highlight} highlight={highlight}
isInvite={me.membership == "invite"} isInvite={me.membership === "invite"}
onClick={this.onRoomTileClick} onClick={this.onRoomTileClick}
/>, />,
); );
@ -110,6 +109,10 @@ export default class ChatCreateOrReuseDialog extends React.Component {
this.props.onExistingRoomSelected(roomId); this.props.onExistingRoomSelected(roomId);
} }
onFinished() {
this.props.onFinished(false);
}
render() { render() {
let title = ''; let title = '';
let content = null; let content = null;
@ -170,14 +173,14 @@ export default class ChatCreateOrReuseDialog extends React.Component {
{ profile } { profile }
</div> </div>
<DialogButtons primaryButton={_t('Start Chatting')} <DialogButtons primaryButton={_t('Start Chatting')}
onPrimaryButtonClick={this.props.onNewDMClick} focus="true" /> onPrimaryButtonClick={this.props.onNewDMClick} focus={true} />
</div>; </div>;
} }
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog className='mx_ChatCreateOrReuseDialog' <BaseDialog className='mx_ChatCreateOrReuseDialog'
onFinished={this.props.onFinished.bind(false)} onFinished={this.onFinished}
title={title} title={title}
contentId='mx_Dialog_content' contentId='mx_Dialog_content'
> >

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -36,7 +37,7 @@ export default React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
emailAddress: null, emailAddress: '',
emailBusy: false, emailBusy: false,
}; };
}, },
@ -127,6 +128,7 @@ export default React.createClass({
const EditableText = sdk.getComponent('elements.EditableText'); const EditableText = sdk.getComponent('elements.EditableText');
const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText
initialValue={this.state.emailAddress}
className="mx_SetEmailDialog_email_input" className="mx_SetEmailDialog_email_input"
autoFocus="true" autoFocus="true"
placeholder={_t("Email address")} placeholder={_t("Email address")}

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -79,15 +80,11 @@ export default React.createClass({
Modal.createDialog(WarmFuzzy, { Modal.createDialog(WarmFuzzy, {
didSetEmail: res.didSetEmail, didSetEmail: res.didSetEmail,
onFinished: () => { onFinished: () => {
this._onContinueClicked(); this.props.onFinished();
}, },
}); });
}, },
_onContinueClicked: function() {
this.props.onFinished(true);
},
_onPasswordChangeError: function(err) { _onPasswordChangeError: function(err) {
let errMsg = err.error || ""; let errMsg = err.error || "";
if (err.httpStatus === 403) { if (err.httpStatus === 403) {

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,15 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict'; import React from 'react';
const React = require('react');
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const KEY_TAB = 9;
const KEY_SHIFT = 16;
const KEY_WINDOWS = 91;
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'EditableText', displayName: 'EditableText',
@ -66,9 +61,7 @@ module.exports = React.createClass({
}, },
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps: function(nextProps) {
if (nextProps.initialValue !== this.props.initialValue || if (nextProps.initialValue !== this.props.initialValue || nextProps.initialValue !== this.value) {
nextProps.initialValue !== this.value
) {
this.value = nextProps.initialValue; this.value = nextProps.initialValue;
if (this.refs.editable_div) { if (this.refs.editable_div) {
this.showPlaceholder(!this.value); this.showPlaceholder(!this.value);
@ -139,7 +132,7 @@ module.exports = React.createClass({
this.showPlaceholder(false); this.showPlaceholder(false);
} }
if (ev.key == "Enter") { if (ev.key === "Enter") {
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
} }
@ -156,9 +149,9 @@ module.exports = React.createClass({
this.value = ev.target.textContent; this.value = ev.target.textContent;
} }
if (ev.key == "Enter") { if (ev.key === "Enter") {
this.onFinish(ev); this.onFinish(ev);
} else if (ev.key == "Escape") { } else if (ev.key === "Escape") {
this.cancelEdit(); this.cancelEdit();
} }
@ -193,7 +186,7 @@ module.exports = React.createClass({
const submit = (ev.key === "Enter") || shouldSubmit; const submit = (ev.key === "Enter") || shouldSubmit;
this.setState({ this.setState({
phase: this.Phases.Display, phase: this.Phases.Display,
}, function() { }, () => {
if (this.value !== this.props.initialValue) { if (this.value !== this.props.initialValue) {
self.onValueChanged(submit); self.onValueChanged(submit);
} }
@ -204,23 +197,35 @@ module.exports = React.createClass({
const sel = window.getSelection(); const sel = window.getSelection();
sel.removeAllRanges(); sel.removeAllRanges();
if (this.props.blurToCancel) {this.cancelEdit();} else {this.onFinish(ev, this.props.blurToSubmit);} if (this.props.blurToCancel) {
this.cancelEdit();
} else {
this.onFinish(ev, this.props.blurToSubmit);
}
this.showPlaceholder(!this.value); this.showPlaceholder(!this.value);
}, },
render: function() { render: function() {
let editable_el; const {className, editable, initialValue, label, labelClassName} = this.props;
let editableEl;
if (!this.props.editable || (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value)) { if (!editable || (this.state.phase === this.Phases.Display && (label || labelClassName) && !this.value)) {
// show the label // show the label
editable_el = <div className={this.props.className + " " + this.props.labelClassName} onClick={this.onClickDiv}>{ this.props.label || this.props.initialValue }</div>; editableEl = <div className={className + " " + labelClassName} onClick={this.onClickDiv}>
{ label || initialValue }
</div>;
} else { } else {
// show the content editable div, but manually manage its contents as react and contentEditable don't play nice together // show the content editable div, but manually manage its contents as react and contentEditable don't play nice together
editable_el = <div ref="editable_div" contentEditable="true" className={this.props.className} editableEl = <div ref="editable_div"
onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} onFocus={this.onFocus} onBlur={this.onBlur}></div>; contentEditable={true}
className={className}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur} />;
} }
return editable_el; return editableEl;
}, },
}); });

View file

@ -36,7 +36,7 @@ function getOrCreateContainer() {
} }
// Greater than that of the ContextualMenu // Greater than that of the ContextualMenu
const PE_Z_INDEX = 3000; const PE_Z_INDEX = 5000;
/* /*
* Class of component that renders its children in a separate ReactDOM virtual tree * Class of component that renders its children in a separate ReactDOM virtual tree

View file

@ -160,7 +160,7 @@ export default class ReplyThread extends React.Component {
} }
static makeThread(parentEv, onWidgetLoad, ref) { static makeThread(parentEv, onWidgetLoad, ref) {
if (!SettingsStore.isFeatureEnabled("feature_rich_quoting") || !ReplyThread.getParentEventId(parentEv)) { if (!ReplyThread.getParentEventId(parentEv)) {
return <div />; return <div />;
} }
return <ReplyThread parentEv={parentEv} onWidgetLoad={onWidgetLoad} ref={ref} />; return <ReplyThread parentEv={parentEv} onWidgetLoad={onWidgetLoad} ref={ref} />;

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017 New Vector Ltd. Copyright 2017 New Vector Ltd.
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -103,14 +104,27 @@ export default React.createClass({
} }
}, },
onContextButtonClick: function(e) { _openContextMenu: function(x, y, chevronOffset) {
e.preventDefault();
e.stopPropagation();
// Hide the (...) immediately // Hide the (...) immediately
this.setState({ hover: false }); this.setState({ hover: false });
const TagTileContextMenu = sdk.getComponent('context_menus.TagTileContextMenu'); const TagTileContextMenu = sdk.getComponent('context_menus.TagTileContextMenu');
ContextualMenu.createMenu(TagTileContextMenu, {
chevronOffset: chevronOffset,
left: x,
top: y,
tag: this.props.tag,
onFinished: () => {
this.setState({ menuDisplayed: false });
},
});
this.setState({ menuDisplayed: true });
},
onContextButtonClick: function(e) {
e.preventDefault();
e.stopPropagation();
const elementRect = e.target.getBoundingClientRect(); const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page // The window X and Y offsets are to adjust position when zoomed in to page
@ -119,17 +133,14 @@ export default React.createClass({
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset); let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
const self = this; this._openContextMenu(x, y, chevronOffset);
ContextualMenu.createMenu(TagTileContextMenu, { },
chevronOffset: chevronOffset,
left: x, onContextMenu: function(e) {
top: y, e.preventDefault();
tag: this.props.tag,
onFinished: function() { const chevronOffset = 12;
self.setState({ menuDisplayed: false }); this._openContextMenu(e.clientX, e.clientY - (chevronOffset + 8), chevronOffset);
},
});
this.setState({ menuDisplayed: true });
}, },
onMouseOver: function() { onMouseOver: function() {
@ -164,7 +175,7 @@ export default React.createClass({
<div className="mx_TagTile_context_button" onClick={this.onContextButtonClick}> <div className="mx_TagTile_context_button" onClick={this.onContextButtonClick}>
{ "\u00B7\u00B7\u00B7" } { "\u00B7\u00B7\u00B7" }
</div> : <div />; </div> : <div />;
return <AccessibleButton className={className} onClick={this.onClick}> return <AccessibleButton className={className} onClick={this.onClick} onContextMenu={this.onContextMenu}>
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}> <div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
<BaseAvatar <BaseAvatar
name={name} name={name}

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,28 +15,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
import React from 'react'; import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
export default React.createClass({ export default React.createClass({
onUpdateClicked: function() { onUpdateClicked: function() {
const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog'); const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog');
Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog, { Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog);
onFinished: (passwordChanged) => {
if (!passwordChanged) {
return;
}
// Notify SessionStore that the user's password was changed
dis.dispatch({
action: 'password_changed',
});
},
});
}, },
render: function() { render: function() {

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2017, 2018 New Vector Ltd Copyright 2017, 2018 New Vector Ltd
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -20,8 +21,9 @@ import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index'; import sdk from '../../../index';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import * as ContextualMenu from "../../structures/ContextualMenu";
import classNames from 'classnames'; import classNames from 'classnames';
import MatrixClientPeg from "../../../MatrixClientPeg";
import {createMenu} from "../../structures/ContextualMenu";
export default React.createClass({ export default React.createClass({
displayName: 'GroupInviteTile', displayName: 'GroupInviteTile',
@ -66,29 +68,11 @@ export default React.createClass({
}); });
}, },
onBadgeClicked: function(e) { _showContextMenu: function(x, y, chevronOffset) {
// Prevent the RoomTile onClick event firing as well const GroupInviteTileContextMenu = sdk.getComponent('context_menus.GroupInviteTileContextMenu');
e.stopPropagation();
// Only allow none guests to access the context menu createMenu(GroupInviteTileContextMenu, {
if (this.context.matrixClient.isGuest()) return; chevronOffset,
// If the badge is clicked, then no longer show tooltip
if (this.props.collapsed) {
this.setState({ hover: false });
}
const RoomTileContextMenu = sdk.getComponent('context_menus.GroupInviteTileContextMenu');
const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = elementRect.right + window.pageXOffset + 3;
const chevronOffset = 12;
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
ContextualMenu.createMenu(RoomTileContextMenu, {
chevronOffset: chevronOffset,
left: x, left: x,
top: y, top: y,
group: this.props.group, group: this.props.group,
@ -99,6 +83,38 @@ export default React.createClass({
this.setState({ menuDisplayed: true }); this.setState({ menuDisplayed: true });
}, },
onContextMenu: function(e) {
// Prevent the RoomTile onClick event firing as well
e.preventDefault();
// Only allow non-guests to access the context menu
if (MatrixClientPeg.get().isGuest()) return;
const chevronOffset = 12;
this._showContextMenu(e.clientX, e.clientY - (chevronOffset + 8), chevronOffset);
},
onBadgeClicked: function(e) {
// Prevent the RoomTile onClick event firing as well
e.stopPropagation();
// Only allow non-guests to access the context menu
if (MatrixClientPeg.get().isGuest()) return;
// If the badge is clicked, then no longer show tooltip
if (this.props.collapsed) {
this.setState({ hover: false });
}
const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = elementRect.right + window.pageXOffset + 3;
const chevronOffset = 12;
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
this._showContextMenu(x, y, chevronOffset);
},
render: function() { render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const EmojiText = sdk.getComponent('elements.EmojiText'); const EmojiText = sdk.getComponent('elements.EmojiText');
@ -139,7 +155,12 @@ export default React.createClass({
}); });
return ( return (
<AccessibleButton className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> <AccessibleButton className={classes}
onClick={this.onClick}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onContextMenu={this.onContextMenu}
>
<div className="mx_RoomTile_avatar"> <div className="mx_RoomTile_avatar">
{ av } { av }
</div> </div>

View file

@ -69,7 +69,7 @@ export default React.createClass({
render() { render() {
const GroupTile = sdk.getComponent('groups.GroupTile'); const GroupTile = sdk.getComponent('groups.GroupTile');
const input = <input type="checkbox" const input = <input type="checkbox"
onClick={this._onPublicityToggle} onChange={this._onPublicityToggle}
checked={this.state.isGroupPublicised} checked={this.state.isGroupPublicised}
/>; />;
const labelText = !this.state.ready ? _t("Loading...") : const labelText = !this.state.ready ? _t("Loading...") :

View file

@ -22,6 +22,7 @@ import sdk from '../../../index';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import FlairStore from '../../../stores/FlairStore'; import FlairStore from '../../../stores/FlairStore';
function nop() {}
const GroupTile = React.createClass({ const GroupTile = React.createClass({
displayName: 'GroupTile', displayName: 'GroupTile',
@ -81,7 +82,7 @@ const GroupTile = React.createClass({
) : null; ) : null;
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273 // XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156 // instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown}> return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown} onClick={nop}>
<Droppable droppableId="my-groups-droppable" type="draggable-TagTile"> <Droppable droppableId="my-groups-droppable" type="draggable-TagTile">
{ (droppableProvided, droppableSnapshot) => ( { (droppableProvided, droppableSnapshot) => (
<div ref={droppableProvided.innerRef}> <div ref={droppableProvided.innerRef}>

View file

@ -327,6 +327,7 @@ module.exports = React.createClass({
// will have the correct name when the user tries to download it. // will have the correct name when the user tries to download it.
// We can't provide a Content-Disposition header like we would for HTTP. // We can't provide a Content-Disposition header like we would for HTTP.
download: fileName, download: fileName,
rel: "noopener",
target: "_blank", target: "_blank",
textContent: _t("Download %(text)s", { text: text }), textContent: _t("Download %(text)s", { text: text }),
}, "*"); }, "*");

View file

@ -1,6 +1,7 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd Copyright 2018 New Vector Ltd
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,8 +16,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk'; import { MatrixClient } from 'matrix-js-sdk';
@ -29,9 +28,7 @@ import Promise from 'bluebird';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
export default class extends React.Component { export default class MImageBody extends React.Component {
displayName: 'MImageBody'
static propTypes = { static propTypes = {
/* the MatrixEvent to show */ /* the MatrixEvent to show */
mxEvent: PropTypes.object.isRequired, mxEvent: PropTypes.object.isRequired,
@ -41,11 +38,11 @@ export default class extends React.Component {
/* the maximum image height to use */ /* the maximum image height to use */
maxImageHeight: PropTypes.number, maxImageHeight: PropTypes.number,
} };
static contextTypes = { static contextTypes = {
matrixClient: PropTypes.instanceOf(MatrixClient), matrixClient: PropTypes.instanceOf(MatrixClient),
} };
constructor(props) { constructor(props) {
super(props); super(props);
@ -90,7 +87,7 @@ export default class extends React.Component {
} }
onClick(ev) { onClick(ev) {
if (ev.button == 0 && !ev.metaKey) { if (ev.button === 0 && !ev.metaKey) {
ev.preventDefault(); ev.preventDefault();
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
const httpUrl = this._getContentUrl(); const httpUrl = this._getContentUrl();
@ -177,9 +174,7 @@ export default class extends React.Component {
return this.state.decryptedThumbnailUrl; return this.state.decryptedThumbnailUrl;
} }
return this.state.decryptedUrl; return this.state.decryptedUrl;
} else if (content.info && } else if (content.info && content.info.mimetype === "image/svg+xml" && content.info.thumbnail_url) {
content.info.mimetype == "image/svg+xml" &&
content.info.thumbnail_url) {
// special case to return client-generated thumbnails for SVGs, if any, // special case to return client-generated thumbnails for SVGs, if any,
// given we deliberately don't thumbnail them serverside to prevent // given we deliberately don't thumbnail them serverside to prevent
// billion lol attacks and similar // billion lol attacks and similar
@ -299,7 +294,7 @@ export default class extends React.Component {
// which has the same width as the timeline // which has the same width as the timeline
// mx_MImageBody_thumbnail resizes img to exactly container size // mx_MImageBody_thumbnail resizes img to exactly container size
img = <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image" img = <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
style={{ "max-width": maxWidth + "px" }} style={{ maxWidth: maxWidth + "px" }}
alt={content.body} alt={content.body}
onError={this.onImageError} onError={this.onImageError}
onLoad={this.onImageLoad} onLoad={this.onImageLoad}
@ -308,14 +303,14 @@ export default class extends React.Component {
} }
const thumbnail = ( const thumbnail = (
<div className="mx_MImageBody_thumbnail_container" style={{ "max-height": maxHeight + "px" }} > <div className="mx_MImageBody_thumbnail_container" style={{ maxHeight: maxHeight + "px" }} >
{ /* Calculate aspect ratio, using %padding will size _container correctly */ } { /* Calculate aspect ratio, using %padding will size _container correctly */ }
<div style={{ paddingBottom: (100 * infoHeight / infoWidth) + '%' }}></div> <div style={{ paddingBottom: (100 * infoHeight / infoWidth) + '%' }} />
{ showPlaceholder && { showPlaceholder &&
<div className="mx_MImageBody_thumbnail" style={{ <div className="mx_MImageBody_thumbnail" style={{
// Constrain width here so that spinner appears central to the loaded thumbnail // Constrain width here so that spinner appears central to the loaded thumbnail
"max-width": infoWidth + "px", maxWidth: infoWidth + "px",
}}> }}>
<div className="mx_MImageBody_thumbnail_spinner"> <div className="mx_MImageBody_thumbnail_spinner">
{ placeholder } { placeholder }

View file

@ -36,6 +36,7 @@ import * as ContextualMenu from '../../structures/ContextualMenu';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
import ReplyThread from "../elements/ReplyThread"; import ReplyThread from "../elements/ReplyThread";
import {host as matrixtoHost} from '../../../matrix-to';
linkifyMatrix(linkify); linkifyMatrix(linkify);
@ -304,7 +305,7 @@ module.exports = React.createClass({
// never preview matrix.to links (if anything we should give a smart // never preview matrix.to links (if anything we should give a smart
// preview of the room/user they point to: nobody needs to be reminded // preview of the room/user they point to: nobody needs to be reminded
// what the matrix.to site looks like). // what the matrix.to site looks like).
if (host == 'matrix.to') return false; if (host === matrixtoHost) return false;
if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) { if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) {
// it's a "foo.pl" style link // it's a "foo.pl" style link
@ -422,8 +423,7 @@ module.exports = React.createClass({
const mxEvent = this.props.mxEvent; const mxEvent = this.props.mxEvent;
const content = mxEvent.getContent(); const content = mxEvent.getContent();
const stripReply = SettingsStore.isFeatureEnabled("feature_rich_quoting") && const stripReply = ReplyThread.getParentEventId(mxEvent);
ReplyThread.getParentEventId(mxEvent);
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, { let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
disableBigEmoji: SettingsStore.getValue('TextualBody.disableBigEmoji'), disableBigEmoji: SettingsStore.getValue('TextualBody.disableBigEmoji'),
// Part of Replies fallback support // Part of Replies fallback support

View file

@ -28,7 +28,7 @@ import Promise from 'bluebird';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
import SlashCommands from '../../../SlashCommands'; import {processCommandInput} from '../../../SlashCommands';
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard'; import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import sdk from '../../../index'; import sdk from '../../../index';
@ -45,8 +45,7 @@ import Markdown from '../../../Markdown';
import ComposerHistoryManager from '../../../ComposerHistoryManager'; import ComposerHistoryManager from '../../../ComposerHistoryManager';
import MessageComposerStore from '../../../stores/MessageComposerStore'; import MessageComposerStore from '../../../stores/MessageComposerStore';
import {MATRIXTO_URL_PATTERN, MATRIXTO_MD_LINK_PATTERN} from '../../../linkify-matrix'; import {MATRIXTO_MD_LINK_PATTERN} from '../../../linkify-matrix';
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
const REGEX_MATRIXTO_MARKDOWN_GLOBAL = new RegExp(MATRIXTO_MD_LINK_PATTERN, 'g'); const REGEX_MATRIXTO_MARKDOWN_GLOBAL = new RegExp(MATRIXTO_MD_LINK_PATTERN, 'g');
import {asciiRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort} from 'emojione'; import {asciiRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort} from 'emojione';
@ -722,7 +721,7 @@ export default class MessageComposerInput extends React.Component {
// Some commands (/join) require pills to be replaced with their text content // Some commands (/join) require pills to be replaced with their text content
const commandText = this.removeMDLinks(contentState, ['#']); const commandText = this.removeMDLinks(contentState, ['#']);
const cmd = SlashCommands.processInput(this.props.room.roomId, commandText); const cmd = processCommandInput(this.props.room.roomId, commandText);
if (cmd) { if (cmd) {
if (!cmd.error) { if (!cmd.error) {
this.historyManager.save(contentState, this.state.isRichtextEnabled ? 'html' : 'markdown'); this.historyManager.save(contentState, this.state.isRichtextEnabled ? 'html' : 'markdown');
@ -1182,7 +1181,7 @@ export default class MessageComposerInput extends React.Component {
return ( return (
<div className="mx_MessageComposer_input_wrapper"> <div className="mx_MessageComposer_input_wrapper">
<div className="mx_MessageComposer_autocomplete_wrapper"> <div className="mx_MessageComposer_autocomplete_wrapper">
{ SettingsStore.isFeatureEnabled("feature_rich_quoting") && <ReplyPreview /> } <ReplyPreview />
<Autocomplete <Autocomplete
ref={(e) => this.autocomplete = e} ref={(e) => this.autocomplete = e}
room={this.props.room} room={this.props.room}

View file

@ -1,6 +1,7 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd Copyright 2017 New Vector Ltd
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,19 +16,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
const React = require('react'); import React from 'react';
const ReactDOM = require("react-dom");
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const classNames = require('classnames'); import classNames from 'classnames';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
const MatrixClientPeg = require('../../../MatrixClientPeg'); import MatrixClientPeg from '../../../MatrixClientPeg';
import DMRoomMap from '../../../utils/DMRoomMap'; import DMRoomMap from '../../../utils/DMRoomMap';
const sdk = require('../../../index'); import sdk from '../../../index';
const ContextualMenu = require('../../structures/ContextualMenu'); import {createMenu} from '../../structures/ContextualMenu';
const RoomNotifs = require('../../../RoomNotifs'); import * as RoomNotifs from '../../../RoomNotifs';
const FormattingUtils = require('../../../utils/FormattingUtils'); import * as FormattingUtils from '../../../utils/FormattingUtils';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import ActiveRoomObserver from '../../../ActiveRoomObserver'; import ActiveRoomObserver from '../../../ActiveRoomObserver';
import RoomViewStore from '../../../stores/RoomViewStore'; import RoomViewStore from '../../../stores/RoomViewStore';
@ -72,16 +71,12 @@ module.exports = React.createClass({
}, },
_shouldShowMentionBadge: function() { _shouldShowMentionBadge: function() {
return this.state.notifState != RoomNotifs.MUTE; return this.state.notifState !== RoomNotifs.MUTE;
}, },
_isDirectMessageRoom: function(roomId) { _isDirectMessageRoom: function(roomId) {
const dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId); const dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
if (dmRooms) { return Boolean(dmRooms);
return true;
} else {
return false;
}
}, },
onRoomTimeline: function(ev, room) { onRoomTimeline: function(ev, room) {
@ -99,7 +94,7 @@ module.exports = React.createClass({
}, },
onAccountData: function(accountDataEvent) { onAccountData: function(accountDataEvent) {
if (accountDataEvent.getType() == 'm.push_rules') { if (accountDataEvent.getType() === 'm.push_rules') {
this.setState({ this.setState({
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId), notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
}); });
@ -187,6 +182,32 @@ module.exports = React.createClass({
this.badgeOnMouseLeave(); this.badgeOnMouseLeave();
}, },
_showContextMenu: function(x, y, chevronOffset) {
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
createMenu(RoomTileContextMenu, {
chevronOffset,
left: x,
top: y,
room: this.props.room,
onFinished: () => {
this.setState({ menuDisplayed: false });
this.props.refreshSubList();
},
});
this.setState({ menuDisplayed: true });
},
onContextMenu: function(e) {
// Prevent the RoomTile onClick event firing as well
e.preventDefault();
// Only allow non-guests to access the context menu
if (MatrixClientPeg.get().isGuest()) return;
const chevronOffset = 12;
this._showContextMenu(e.clientX, e.clientY - (chevronOffset + 8), chevronOffset);
},
badgeOnMouseEnter: function() { badgeOnMouseEnter: function() {
// Only allow non-guests to access the context menu // Only allow non-guests to access the context menu
// and only change it if it needs to change // and only change it if it needs to change
@ -200,37 +221,25 @@ module.exports = React.createClass({
}, },
onBadgeClicked: function(e) { onBadgeClicked: function(e) {
// Only allow none guests to access the context menu
if (!MatrixClientPeg.get().isGuest()) {
// If the badge is clicked, then no longer show tooltip
if (this.props.collapsed) {
this.setState({ hover: false });
}
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = elementRect.right + window.pageXOffset + 3;
const chevronOffset = 12;
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
const self = this;
ContextualMenu.createMenu(RoomTileContextMenu, {
chevronOffset: chevronOffset,
left: x,
top: y,
room: this.props.room,
onFinished: function() {
self.setState({ menuDisplayed: false });
self.props.refreshSubList();
},
});
this.setState({ menuDisplayed: true });
}
// Prevent the RoomTile onClick event firing as well // Prevent the RoomTile onClick event firing as well
e.stopPropagation(); e.stopPropagation();
// Only allow non-guests to access the context menu
if (MatrixClientPeg.get().isGuest()) return;
// If the badge is clicked, then no longer show tooltip
if (this.props.collapsed) {
this.setState({ hover: false });
}
const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = elementRect.right + window.pageXOffset + 3;
const chevronOffset = 12;
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
this._showContextMenu(x, y, chevronOffset);
}, },
render: function() { render: function() {
@ -250,7 +259,7 @@ module.exports = React.createClass({
'mx_RoomTile_unread': this.props.unread, 'mx_RoomTile_unread': this.props.unread,
'mx_RoomTile_unreadNotify': notifBadges, 'mx_RoomTile_unreadNotify': notifBadges,
'mx_RoomTile_highlight': mentionBadges, 'mx_RoomTile_highlight': mentionBadges,
'mx_RoomTile_invited': (me && me.membership == 'invite'), 'mx_RoomTile_invited': (me && me.membership === 'invite'),
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed, 'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
'mx_RoomTile_noBadges': !badges, 'mx_RoomTile_noBadges': !badges,
'mx_RoomTile_transparent': this.props.transparent, 'mx_RoomTile_transparent': this.props.transparent,
@ -268,7 +277,6 @@ module.exports = React.createClass({
let name = this.state.roomName; let name = this.state.roomName;
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
let badge;
let badgeContent; let badgeContent;
if (this.state.badgeHover || this.state.menuDisplayed) { if (this.state.badgeHover || this.state.menuDisplayed) {
@ -280,7 +288,7 @@ module.exports = React.createClass({
badgeContent = '\u200B'; badgeContent = '\u200B';
} }
badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>; const badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>;
const EmojiText = sdk.getComponent('elements.EmojiText'); const EmojiText = sdk.getComponent('elements.EmojiText');
let label; let label;
@ -312,16 +320,22 @@ module.exports = React.createClass({
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
let directMessageIndicator; let dmIndicator;
if (this._isDirectMessageRoom(this.props.room.roomId)) { if (this._isDirectMessageRoom(this.props.room.roomId)) {
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />; dmIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
} }
return <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> return <AccessibleButton tabIndex="0"
className={classes}
onClick={this.onClick}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onContextMenu={this.onContextMenu}
>
<div className={avatarClasses}> <div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container"> <div className="mx_RoomTile_avatar_container">
<RoomAvatar room={this.props.room} width={24} height={24} /> <RoomAvatar room={this.props.room} width={24} height={24} />
{ directMessageIndicator } { dmIndicator }
</div> </div>
</div> </div>
<div className="mx_RoomTile_nameContainer"> <div className="mx_RoomTile_nameContainer">

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,36 +15,28 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict'; import React from 'react';
const React = require('react'); import sdk from '../../../index';
const sdk = require('../../../index'); import MatrixClientPeg from '../../../MatrixClientPeg';
const MatrixClientPeg = require("../../../MatrixClientPeg");
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ChangeDisplayName', displayName: 'ChangeDisplayName',
_getDisplayName: function() { _getDisplayName: async function() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
return cli.getProfileInfo(cli.credentials.userId).then(function(result) { try {
let displayname = result.displayname; const res = await cli.getProfileInfo(cli.getUserId());
if (!displayname) { return res.displayname;
if (MatrixClientPeg.get().isGuest()) { } catch (e) {
displayname = "Guest " + MatrixClientPeg.get().getUserIdLocalpart();
} else {
displayname = MatrixClientPeg.get().getUserIdLocalpart();
}
}
return displayname;
}, function(error) {
throw new Error("Failed to fetch display name"); throw new Error("Failed to fetch display name");
}); }
}, },
_changeDisplayName: function(new_displayname) { _changeDisplayName: function(newDisplayname) {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
return cli.setDisplayName(new_displayname).catch(function(e) { return cli.setDisplayName(newDisplayname).catch(function(e) {
throw new Error("Failed to set display name"); throw new Error("Failed to set display name", e);
}); });
}, },

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,14 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
const React = require('react'); const React = require('react');
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const MatrixClientPeg = require("../../../MatrixClientPeg"); const MatrixClientPeg = require("../../../MatrixClientPeg");
const Modal = require("../../../Modal"); const Modal = require("../../../Modal");
const sdk = require("../../../index"); const sdk = require("../../../index");
import dis from "../../../dispatcher";
import Promise from 'bluebird'; import Promise from 'bluebird';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -143,6 +143,9 @@ module.exports = React.createClass({
}); });
cli.setPassword(authDict, newPassword).then(() => { cli.setPassword(authDict, newPassword).then(() => {
// Notify SessionStore that the user's password was changed
dis.dispatch({action: 'password_changed'});
if (this.props.shouldAskForEmail) { if (this.props.shouldAskForEmail) {
return this._optionallySetEmail().then((confirmed) => { return this._optionallySetEmail().then((confirmed) => {
this.props.onFinished({ this.props.onFinished({

View file

@ -136,7 +136,6 @@
"Missing room_id in request": "Липсва room_id в заявката", "Missing room_id in request": "Липсва room_id в заявката",
"Room %(roomId)s not visible": "Стая %(roomId)s не е видима", "Room %(roomId)s not visible": "Стая %(roomId)s не е видима",
"Missing user_id in request": "Липсва user_id в заявката", "Missing user_id in request": "Липсва user_id в заявката",
"Failed to lookup current room": "Неуспешно намиране на текущата стая",
"/ddg is not a command": "/ddg не е команда", "/ddg is not a command": "/ddg не е команда",
"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.": "За използване, изчакайте зареждането на списъка с предложения и изберете от него.",
"Unrecognised room alias:": "Непознат псевдоним на стая:", "Unrecognised room alias:": "Непознат псевдоним на стая:",
@ -204,9 +203,7 @@
"Not a valid Riot keyfile": "Невалиден файл с ключ за Riot", "Not a valid Riot keyfile": "Невалиден файл с ключ за Riot",
"Authentication check failed: incorrect password?": "Неуспешна автентикация: неправилна парола?", "Authentication check failed: incorrect password?": "Неуспешна автентикация: неправилна парола?",
"Failed to join room": "Неуспешно присъединяване към стаята", "Failed to join room": "Неуспешно присъединяване към стаята",
"Message Replies": "Отговори на съобщението",
"Message Pinning": "Функция за закачане на съобщения", "Message Pinning": "Функция за закачане на съобщения",
"Tag Panel": "Панел с етикети",
"Disable Emoji suggestions while typing": "Изключване на предложенията за емотиконите при писане", "Disable Emoji suggestions while typing": "Изключване на предложенията за емотиконите при писане",
"Use compact timeline layout": "Използване на компактно оформление за списъка със съобщения", "Use compact timeline layout": "Използване на компактно оформление за списъка със съобщения",
"Hide removed messages": "Скриване на премахнати съобщения", "Hide removed messages": "Скриване на премахнати съобщения",
@ -491,7 +488,6 @@
"Decrypt %(text)s": "Разшифровай %(text)s", "Decrypt %(text)s": "Разшифровай %(text)s",
"Download %(text)s": "Изтегли %(text)s", "Download %(text)s": "Изтегли %(text)s",
"(could not connect media)": "(неуспешно свързване на медийните устройства)", "(could not connect media)": "(неуспешно свързване на медийните устройства)",
"Must be viewing a room": "Трябва да извършите това в стая",
"Usage": "Употреба", "Usage": "Употреба",
"Remove from community": "Премахни от общността", "Remove from community": "Премахни от общността",
"Disinvite this user from community?": "Оттегляне на поканата към този потребител от общността?", "Disinvite this user from community?": "Оттегляне на поканата към този потребител от общността?",
@ -520,8 +516,6 @@
"Community %(groupId)s not found": "Общност %(groupId)s не е намерена", "Community %(groupId)s not found": "Общност %(groupId)s не е намерена",
"Create a new community": "Създаване на нова общност", "Create a new community": "Създаване на нова общност",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Създайте общност, за да групирате потребители и стаи! Изградете персонализирана начална страница, за да маркирате своето пространство в Matrix Вселената.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Създайте общност, за да групирате потребители и стаи! Изградете персонализирана начална страница, за да маркирате своето пространство в Matrix Вселената.",
"Join an existing community": "Присъединяване към съществуваща общност",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "За да се присъедините към вече съществуваща общност, трябва да знаете нейния идентификатор; той изглежда нещо подобно на <i>+example:matrix.org</i>.",
"Unknown (user, device) pair:": "Непозната двойка (потребител, устройство):", "Unknown (user, device) pair:": "Непозната двойка (потребител, устройство):",
"The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Подписващият ключ, който сте предоставили, съвпада с подписващия ключ, който сте получили от устройството %(deviceId)s на %(userId)s. Устройството е маркирано като потвърдено.", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Подписващият ключ, който сте предоставили, съвпада с подписващия ключ, който сте получили от устройството %(deviceId)s на %(userId)s. Устройството е маркирано като потвърдено.",
"Hide avatars in user and room mentions": "Скриване на аватара на потребители и стаи при споменаването им", "Hide avatars in user and room mentions": "Скриване на аватара на потребители и стаи при споменаването им",
@ -1186,5 +1180,6 @@
"Terms and Conditions": "Правила и условия", "Terms and Conditions": "Правила и условия",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "За да продължите да ползвате %(homeserverDomain)s е необходимо да прегледате и да се съгласите с правилата и условията за ползване.", "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "За да продължите да ползвате %(homeserverDomain)s е необходимо да прегледате и да се съгласите с правилата и условията за ползване.",
"Review terms and conditions": "Прегледай правилата и условията", "Review terms and conditions": "Прегледай правилата и условията",
"Failed to indicate account erasure": "Неуспешно указване на желанието за изтриване на акаунта" "Failed to indicate account erasure": "Неуспешно указване на желанието за изтриване на акаунта",
"Try the app first": "Първо пробвайте приложението"
} }

View file

@ -134,10 +134,8 @@
"You are not in this room.": "No heu entrat a aquesta sala.", "You are not in this room.": "No heu entrat a aquesta sala.",
"You do not have permission to do that in this room.": "No teniu el permís per realitzar aquesta acció en aquesta sala.", "You do not have permission to do that in this room.": "No teniu el permís per realitzar aquesta acció en aquesta sala.",
"Missing room_id in request": "Falta l'ID de la sala en la vostra sol·licitud", "Missing room_id in request": "Falta l'ID de la sala en la vostra sol·licitud",
"Must be viewing a room": "Hauríeu de veure una sala",
"Room %(roomId)s not visible": "La sala %(roomId)s no és visible", "Room %(roomId)s not visible": "La sala %(roomId)s no és visible",
"Missing user_id in request": "Falta l'ID d'usuari a la vostre sol·licitud", "Missing user_id in request": "Falta l'ID d'usuari a la vostre sol·licitud",
"Failed to lookup current room": "No s'ha pogut buscar la sala actual",
"Usage": "Ús", "Usage": "Ús",
"/ddg is not a command": "/ddg no és un comandament", "/ddg is not a command": "/ddg no és un comandament",
"To use it, just wait for autocomplete results to load and tab through them.": "Per utilitzar-lo, simplement espereu que es completin els resultats automàticament i seleccioneu-ne el desitjat.", "To use it, just wait for autocomplete results to load and tab through them.": "Per utilitzar-lo, simplement espereu que es completin els resultats automàticament i seleccioneu-ne el desitjat.",
@ -211,9 +209,7 @@
"Not a valid Riot keyfile": "El fitxer no és un fitxer de claus de Riot valid", "Not a valid Riot keyfile": "El fitxer no és un fitxer de claus de Riot valid",
"Authentication check failed: incorrect password?": "Ha fallat l'autenticació: heu introduït correctament la contrasenya?", "Authentication check failed: incorrect password?": "Ha fallat l'autenticació: heu introduït correctament la contrasenya?",
"Failed to join room": "No s'ha pogut entrar a la sala", "Failed to join room": "No s'ha pogut entrar a la sala",
"Message Replies": "Respostes del missatge",
"Message Pinning": "Fixació de missatges", "Message Pinning": "Fixació de missatges",
"Tag Panel": "Tauler d'etiquetes",
"Disable Emoji suggestions while typing": "Desactiva els suggeriments d'Emoji mentre s'escriu", "Disable Emoji suggestions while typing": "Desactiva els suggeriments d'Emoji mentre s'escriu",
"Use compact timeline layout": "Utilitza el disseny compacte de la línia de temps", "Use compact timeline layout": "Utilitza el disseny compacte de la línia de temps",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Amaga els missatges d'entrada i sortida (no afecta a les invitacions, expulsions o prohibicions)", "Hide join/leave messages (invites/kicks/bans unaffected)": "Amaga els missatges d'entrada i sortida (no afecta a les invitacions, expulsions o prohibicions)",
@ -771,8 +767,6 @@
"Error whilst fetching joined communities": "S'ha produït un error en buscar comunitats unides", "Error whilst fetching joined communities": "S'ha produït un error en buscar comunitats unides",
"Create a new community": "Crea una nova comunitat", "Create a new community": "Crea una nova comunitat",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crea una comunitat per agrupar usuaris i sales! Creeu una pàgina d'inici personalitzada per definir el vostre espai a l'univers Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crea una comunitat per agrupar usuaris i sales! Creeu una pàgina d'inici personalitzada per definir el vostre espai a l'univers Matrix.",
"Join an existing community": "Uneix-te a una comunitat existent",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Per unir-se a una comunitat existent, haureu de conèixer l'identificador de la comunitat; això es veurà com <i>+exemple:matrix.org</i>.",
"You have no visible notifications": "No teniu cap notificació visible", "You have no visible notifications": "No teniu cap notificació visible",
"Scroll to bottom of page": "Desplaça't fins a la part inferior de la pàgina", "Scroll to bottom of page": "Desplaça't fins a la part inferior de la pàgina",
"Message not sent due to unknown devices being present": "El missatge no s'ha enviat perquè hi ha dispositius desconeguts presents", "Message not sent due to unknown devices being present": "El missatge no s'ha enviat perquè hi ha dispositius desconeguts presents",

View file

@ -632,9 +632,7 @@
"Show these rooms to non-members on the community page and room list?": "Zobrazovat tyto místnosti na domovské stránce skupiny a v seznamu místností i pro nečleny?", "Show these rooms to non-members on the community page and room list?": "Zobrazovat tyto místnosti na domovské stránce skupiny a v seznamu místností i pro nečleny?",
"Restricted": "Omezené", "Restricted": "Omezené",
"Missing room_id in request": "V zadání chybí room_id", "Missing room_id in request": "V zadání chybí room_id",
"Must be viewing a room": "Musí být zobrazena místnost",
"Missing user_id in request": "V zadání chybí user_id", "Missing user_id in request": "V zadání chybí user_id",
"Failed to lookup current room": "Nepodařilo se vyhledat aktuální místnost",
"(could not connect media)": "(média se nepodařilo spojit)", "(could not connect media)": "(média se nepodařilo spojit)",
"%(senderName)s placed a %(callType)s call.": "%(senderName)s uskutečnil %(callType)s hovor.", "%(senderName)s placed a %(callType)s call.": "%(senderName)s uskutečnil %(callType)s hovor.",
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s zpřístupnil budoucí historii místnosti neznámým (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s zpřístupnil budoucí historii místnosti neznámým (%(visibility)s).",
@ -859,8 +857,6 @@
"Error whilst fetching joined communities": "Při získávání vašich skupin se vyskytla chyba", "Error whilst fetching joined communities": "Při získávání vašich skupin se vyskytla chyba",
"Create a new community": "Vytvořit novou skupinu", "Create a new community": "Vytvořit novou skupinu",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Vytvořte skupinu s cílem seskupit uživatele a místnosti! Vytvořte si vlastní domovskou stránku a vymezte tak váš prostor ve světe Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Vytvořte skupinu s cílem seskupit uživatele a místnosti! Vytvořte si vlastní domovskou stránku a vymezte tak váš prostor ve světe Matrix.",
"Join an existing community": "Vstoupit do existující skupiny",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Aby jste mohli vstoupit do existující skupiny, musíte znát její identifikátor; Měl by vypadat asi takto <i>+priklad:matrix.org</i>.",
"You have no visible notifications": "Nejsou dostupná žádná oznámení", "You have no visible notifications": "Nejsou dostupná žádná oznámení",
"Connectivity to the server has been lost.": "Spojení se serverem bylo přerušené.", "Connectivity to the server has been lost.": "Spojení se serverem bylo přerušené.",
"Sent messages will be stored until your connection has returned.": "Odeslané zprávy zůstanou uložené, dokud se spojení znovu neobnoví.", "Sent messages will be stored until your connection has returned.": "Odeslané zprávy zůstanou uložené, dokud se spojení znovu neobnoví.",
@ -918,7 +914,6 @@
"Claimed Ed25519 fingerprint key": "Údajný klíč s otiskem prstu Ed25519", "Claimed Ed25519 fingerprint key": "Údajný klíč s otiskem prstu Ed25519",
"This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Tento proces vás provede importem šifrovacích klíčů, které jste si stáhli z jiného Matrix klienta. Po úspěšném naimportování budete v tomto klientovi moci dešifrovat všechny zprávy, které jste mohli dešifrovat v původním klientovi.", "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Tento proces vás provede importem šifrovacích klíčů, které jste si stáhli z jiného Matrix klienta. Po úspěšném naimportování budete v tomto klientovi moci dešifrovat všechny zprávy, které jste mohli dešifrovat v původním klientovi.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Stažený soubor je chráněn heslem. Soubor můžete naimportovat pouze pokud zadáte odpovídající heslo.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Stažený soubor je chráněn heslem. Soubor můžete naimportovat pouze pokud zadáte odpovídající heslo.",
"Tag Panel": "Připnout panel",
"Call Failed": "Hovor selhal", "Call Failed": "Hovor selhal",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "V této místnosti jsou neznámá zařízení: Pokud budete pokračovat bez jejich ověření, někdo může Váš hovor odposlouchávat.", "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "V této místnosti jsou neznámá zařízení: Pokud budete pokračovat bez jejich ověření, někdo může Váš hovor odposlouchávat.",
"Review Devices": "Ověřit zařízení", "Review Devices": "Ověřit zařízení",

View file

@ -193,10 +193,8 @@
"You are not in this room.": "Du er ikke i dette rum.", "You are not in this room.": "Du er ikke i dette rum.",
"You do not have permission to do that in this room.": "Du har ikke tilladelse til at gøre dét i dette rum.", "You do not have permission to do that in this room.": "Du har ikke tilladelse til at gøre dét i dette rum.",
"Missing room_id in request": "Mangler room_id i forespørgsel", "Missing room_id in request": "Mangler room_id i forespørgsel",
"Must be viewing a room": "Du skal være i gang med at se på rummet",
"Room %(roomId)s not visible": "rum %(roomId)s ikke synligt", "Room %(roomId)s not visible": "rum %(roomId)s ikke synligt",
"Missing user_id in request": "Manglende user_id i forespørgsel", "Missing user_id in request": "Manglende user_id i forespørgsel",
"Failed to lookup current room": "Kunne ikke slå nuværende rum op",
"Usage": "Brug", "Usage": "Brug",
"/ddg is not a command": "/ddg er ikke en kommando", "/ddg is not a command": "/ddg er ikke en kommando",
"To use it, just wait for autocomplete results to load and tab through them.": "For at bruge det skal du bare vente på autocomplete resultaterne indlæser og tab'e igennem dem.", "To use it, just wait for autocomplete results to load and tab through them.": "For at bruge det skal du bare vente på autocomplete resultaterne indlæser og tab'e igennem dem.",

View file

@ -249,7 +249,6 @@
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s hat das Thema geändert in \"%(topic)s\".", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s hat das Thema geändert in \"%(topic)s\".",
"/ddg is not a command": "/ddg ist kein Kommando", "/ddg is not a command": "/ddg ist kein Kommando",
"%(senderName)s ended the call.": "%(senderName)s hat den Anruf beendet.", "%(senderName)s ended the call.": "%(senderName)s hat den Anruf beendet.",
"Failed to lookup current room": "Fehler beim Nachschlagen des Raums",
"Failed to send request.": "Anfrage konnte nicht gesendet werden.", "Failed to send request.": "Anfrage konnte nicht gesendet werden.",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s",
"%(senderName)s invited %(targetName)s.": "%(senderName)s hat %(targetName)s eingeladen.", "%(senderName)s invited %(targetName)s.": "%(senderName)s hat %(targetName)s eingeladen.",
@ -264,7 +263,6 @@
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s hat den zukünftigen Chatverlauf sichtbar gemacht für unbekannt (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s hat den zukünftigen Chatverlauf sichtbar gemacht für unbekannt (%(visibility)s).",
"Missing room_id in request": "Fehlende room_id in Anfrage", "Missing room_id in request": "Fehlende room_id in Anfrage",
"Missing user_id in request": "Fehlende user_id in Anfrage", "Missing user_id in request": "Fehlende user_id in Anfrage",
"Must be viewing a room": "Muss einen Raum ansehen",
"(not supported by this browser)": "(wird von diesem Browser nicht unterstützt)", "(not supported by this browser)": "(wird von diesem Browser nicht unterstützt)",
"%(senderName)s placed a %(callType)s call.": "%(senderName)s startete einen %(callType)s-Anruf.", "%(senderName)s placed a %(callType)s call.": "%(senderName)s startete einen %(callType)s-Anruf.",
"Power level must be positive integer.": "Berechtigungslevel muss eine positive ganze Zahl sein.", "Power level must be positive integer.": "Berechtigungslevel muss eine positive ganze Zahl sein.",
@ -784,8 +782,6 @@
"Failed to load %(groupId)s": "'%(groupId)s' konnte nicht geladen werden", "Failed to load %(groupId)s": "'%(groupId)s' konnte nicht geladen werden",
"Error whilst fetching joined communities": "Fehler beim Laden beigetretener Communities", "Error whilst fetching joined communities": "Fehler beim Laden beigetretener Communities",
"Create a new community": "Neue Community erstellen", "Create a new community": "Neue Community erstellen",
"Join an existing community": "Einer bestehenden Community beitreten",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Um einer bereits bestehenden Community beitreten zu können, musst dir deren Community-ID bekannt sein. Diese sieht z. B. aus wie <i>+example:matrix.org</i>.",
"Your Communities": "Deine Communities", "Your Communities": "Deine Communities",
"You're not currently a member of any communities.": "Du gehörst aktuell keiner Community an.", "You're not currently a member of any communities.": "Du gehörst aktuell keiner Community an.",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Erstelle eine Community, um Benutzer und Räume miteinander zu verbinden! Erstelle zusätzlich eine eigene Homepage, um deinen individuellen Bereich im Matrix-Universum zu gestalten.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Erstelle eine Community, um Benutzer und Räume miteinander zu verbinden! Erstelle zusätzlich eine eigene Homepage, um deinen individuellen Bereich im Matrix-Universum zu gestalten.",
@ -950,8 +946,6 @@
"Your homeserver's URL": "Die URL deines Homeservers", "Your homeserver's URL": "Die URL deines Homeservers",
"Your identity server's URL": "Die URL deines Identitätsservers", "Your identity server's URL": "Die URL deines Identitätsservers",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
"Tag Panel": "Beschriftungsfeld",
"Message Replies": "Antworten auf Nachrichten",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Du wirst nicht in der Lage sein, die Änderung zurückzusetzen, da du dich degradierst. Wenn du der letze Nutzer mit Berechtigungen bist, wird es unmöglich sein die Privilegien zurückzubekommen.", "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Du wirst nicht in der Lage sein, die Änderung zurückzusetzen, da du dich degradierst. Wenn du der letze Nutzer mit Berechtigungen bist, wird es unmöglich sein die Privilegien zurückzubekommen.",
"Community IDs cannot not be empty.": "Community-IDs können nicht leer sein.", "Community IDs cannot not be empty.": "Community-IDs können nicht leer sein.",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Geräte anzeigen</showDevicesText>, <sendAnywayText>trotzdem senden</sendAnywayText> oder <cancelText>abbrechen</cancelText>.", "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Geräte anzeigen</showDevicesText>, <sendAnywayText>trotzdem senden</sendAnywayText> oder <cancelText>abbrechen</cancelText>.",
@ -1186,5 +1180,20 @@
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Du kannst diesen Raum nicht verlassen, da dieser Raum für wichtige Nachrichten vom Heimserver verwendet wird.", "This room is used for important messages from the Homeserver, so you cannot leave it.": "Du kannst diesen Raum nicht verlassen, da dieser Raum für wichtige Nachrichten vom Heimserver verwendet wird.",
"Terms and Conditions": "Geschäftsbedingungen", "Terms and Conditions": "Geschäftsbedingungen",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Um den %(homeserverDomain)s -Heimserver weiter zu verwenden, musst du die Geschäftsbedingungen sichten und ihnen zustimmen.", "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Um den %(homeserverDomain)s -Heimserver weiter zu verwenden, musst du die Geschäftsbedingungen sichten und ihnen zustimmen.",
"Review terms and conditions": "Geschäftsbedingungen anzeigen" "Review terms and conditions": "Geschäftsbedingungen anzeigen",
"Encrypting": "Verschlüssele",
"Encrypted, not sent": "Verschlüsselt, nicht gesendet",
"Share Link to User": "Sende Link an Benutzer",
"Share room": "Teile Raum",
"Share Room": "Teile Raum",
"Link to most recent message": "Link zur aktuellsten Nachricht",
"Share User": "Teile Benutzer",
"Share Community": "Teile Community",
"Share Room Message": "Teile Raumnachricht",
"Link to selected message": "Link zur ausgewählten Nachricht",
"COPY": "KOPIEREN",
"Share Message": "Teile Nachricht",
"No Audio Outputs detected": "Keine Ton-Ausgabe erkannt",
"Audio Output": "Ton-Ausgabe",
"Try the app first": "App erst ausprobieren"
} }

View file

@ -107,7 +107,7 @@
"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 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": "Δεν ήταν δυνατή η επιβεβαίωση της διεύθυνσης ηλεκτρονικής αλληλογραφίας: βεβαιωθείτε οτι κάνατε κλικ στον σύνδεσμο που σας στάλθηκε",
"Favourite": "Αγαπημένο", "Favourite": "Αγαπημένο",
"Favourites": "Αγαπημένα", "Favourites": "Αγαπημένα",
"Fill screen": "Γέμισε την οθόνη", "Fill screen": "Γέμισε την οθόνη",
@ -264,7 +264,7 @@
"Room %(roomId)s not visible": "Το δωμάτιο %(roomId)s δεν είναι ορατό", "Room %(roomId)s not visible": "Το δωμάτιο %(roomId)s δεν είναι ορατό",
"%(roomName)s does not exist.": "Το %(roomName)s δεν υπάρχει.", "%(roomName)s does not exist.": "Το %(roomName)s δεν υπάρχει.",
"Searches DuckDuckGo for results": "Γίνεται αναζήτηση στο DuckDuckGo για αποτελέσματα", "Searches DuckDuckGo for results": "Γίνεται αναζήτηση στο DuckDuckGo για αποτελέσματα",
"Seen by %(userName)s at %(dateTime)s": "Διαβάστηκε από %(userName)s στις %(dateTime)s", "Seen by %(userName)s at %(dateTime)s": "Διαβάστηκε από τον/την %(userName)s στις %(dateTime)s",
"Send anyway": "Αποστολή ούτως ή άλλως", "Send anyway": "Αποστολή ούτως ή άλλως",
"Send Invites": "Αποστολή προσκλήσεων", "Send Invites": "Αποστολή προσκλήσεων",
"Send Reset Email": "Αποστολή μηνύματος επαναφοράς", "Send Reset Email": "Αποστολή μηνύματος επαναφοράς",
@ -424,7 +424,6 @@
"Failed to ban user": "Δεν ήταν δυνατό ο αποκλεισμός του χρήστη", "Failed to ban user": "Δεν ήταν δυνατό ο αποκλεισμός του χρήστη",
"Failed to change power level": "Δεν ήταν δυνατή η αλλαγή του επιπέδου δύναμης", "Failed to change power level": "Δεν ήταν δυνατή η αλλαγή του επιπέδου δύναμης",
"Failed to fetch avatar URL": "Δεν ήταν δυνατή η ανάκτηση της διεύθυνσης εικόνας", "Failed to fetch avatar URL": "Δεν ήταν δυνατή η ανάκτηση της διεύθυνσης εικόνας",
"Failed to lookup current room": "Δεν ήταν δυνατή η εύρεση του τρέχοντος δωματίου",
"Failed to unban": "Δεν ήταν δυνατή η άρση του αποκλεισμού", "Failed to unban": "Δεν ήταν δυνατή η άρση του αποκλεισμού",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s από %(fromPowerLevel)s σε %(toPowerLevel)s", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s από %(fromPowerLevel)s σε %(toPowerLevel)s",
"Guest access is disabled on this Home Server.": "Έχει απενεργοποιηθεί η πρόσβαση στους επισκέπτες σε αυτόν τον διακομιστή.", "Guest access is disabled on this Home Server.": "Έχει απενεργοποιηθεί η πρόσβαση στους επισκέπτες σε αυτόν τον διακομιστή.",
@ -447,7 +446,6 @@
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "Ο %(senderName)s έκανε το μελλοντικό ιστορικό του δωματίου δημόσιο άγνωστο (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "Ο %(senderName)s έκανε το μελλοντικό ιστορικό του δωματίου δημόσιο άγνωστο (%(visibility)s).",
"Missing user_id in request": "Λείπει το user_id στο αίτημα", "Missing user_id in request": "Λείπει το user_id στο αίτημα",
"Mobile phone number (optional)": "Αριθμός κινητού τηλεφώνου (προαιρετικό)", "Mobile phone number (optional)": "Αριθμός κινητού τηλεφώνου (προαιρετικό)",
"Must be viewing a room": "Πρέπει να βλέπετε ένα δωμάτιο",
"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 from this device": "Να μη γίνει ποτέ αποστολή κρυπτογραφημένων μηνυμάτων σε ανεπιβεβαίωτες συσκευές, σε αυτό το δωμάτιο, από αυτή τη συσκευή", "Never send encrypted messages to unverified devices in this room from this device": "Να μη γίνει ποτέ αποστολή κρυπτογραφημένων μηνυμάτων σε ανεπιβεβαίωτες συσκευές, σε αυτό το δωμάτιο, από αυτή τη συσκευή",
"not set": "δεν έχει οριστεί", "not set": "δεν έχει οριστεί",
@ -465,7 +463,7 @@
"%(senderName)s removed their profile picture.": "Ο %(senderName)s αφαίρεσε τη φωτογραφία του προφίλ του.", "%(senderName)s removed their profile picture.": "Ο %(senderName)s αφαίρεσε τη φωτογραφία του προφίλ του.",
"%(senderName)s requested a VoIP conference.": "Ο %(senderName)s αιτήθηκε μια συνδιάσκεψη VoIP.", "%(senderName)s requested a VoIP conference.": "Ο %(senderName)s αιτήθηκε μια συνδιάσκεψη VoIP.",
"Riot does not have permission to send you notifications - please check your browser settings": "Το Riot δεν έχει δικαιώματα για αποστολή ειδοποιήσεων - παρακαλούμε ελέγξτε τις ρυθμίσεις του περιηγητή σας", "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 was not given permission to send notifications - please try again": "Δεν δόθηκαν δικαιώματα αποστολής ειδοποιήσεων στο Riot - παρακαλούμε προσπαθήστε ξανά",
"Room contains unknown devices": "Το δωμάτιο περιέχει άγνωστες συσκευές", "Room contains unknown devices": "Το δωμάτιο περιέχει άγνωστες συσκευές",
"%(roomName)s is not accessible at this time.": "Το %(roomName)s δεν είναι προσβάσιμο αυτή τη στιγμή.", "%(roomName)s is not accessible at this time.": "Το %(roomName)s δεν είναι προσβάσιμο αυτή τη στιγμή.",
"Scroll to bottom of page": "Μετάβαση στο τέλος της σελίδας", "Scroll to bottom of page": "Μετάβαση στο τέλος της σελίδας",
@ -772,5 +770,85 @@
"e.g. <CurrentPageURL>": "π.χ. <CurrentPageURL>", "e.g. <CurrentPageURL>": "π.χ. <CurrentPageURL>",
"Your device resolution": "Η ανάλυση της συσκευής σας", "Your device resolution": "Η ανάλυση της συσκευής σας",
"The information being sent to us to help make Riot.im better includes:": "Οι πληροφορίες που στέλνονται σε εμάς με σκοπό την βελτίωση του Riot.im περιλαμβάνουν:", "The information being sent to us to help make Riot.im better includes:": "Οι πληροφορίες που στέλνονται σε εμάς με σκοπό την βελτίωση του Riot.im περιλαμβάνουν:",
"Call Failed": "Η κλήση απέτυχε" "Call Failed": "Η κλήση απέτυχε",
"Whether or not you're logged in (we don't record your user name)": "Εάν είστε συνδεδεμένος/η ή όχι (δεν καταγράφουμε το όνομα χρήστη σας)",
"e.g. %(exampleValue)s": "π.χ. %(exampleValue)s",
"Review Devices": "Ανασκόπηση συσκευών",
"Call Anyway": "Κλήση όπως και να 'χει",
"Answer Anyway": "Απάντηση όπως και να 'χει",
"Call": "Κλήση",
"Answer": "Απάντηση",
"AM": "ΠΜ",
"PM": "ΜΜ",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
"Who would you like to add to this community?": "Ποιον/α θα θέλατε να προσθέσετε σε αυτή την κοινότητα;",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Προσοχή: κάθε άτομο που προσθέτετε στην κοινότητα θε είναι δημοσίως ορατό σε οποιονδήποτε γνωρίζει το αναγνωριστικό της κοινότητας",
"Invite new community members": "Προσκαλέστε νέα μέλη στην κοινότητα",
"Name or matrix ID": "Όνομα ή αναγνωριστικό του matrix",
"Invite to Community": "Πρόσκληση στην κοινότητα",
"Which rooms would you like to add to this community?": "Ποια δωμάτια θα θέλατε να προσθέσετε σε αυτή την κοινότητα;",
"Add rooms to the community": "Προσθήκη δωματίων στην κοινότητα",
"Add to community": "Προσθήκη στην κοινότητα",
"Failed to invite the following users to %(groupId)s:": "Αποτυχία πρόσκλησης των ακόλουθων χρηστών στο %(groupId)s :",
"Failed to invite users to community": "Αποτυχία πρόσκλησης χρηστών στην κοινότητα",
"Failed to invite users to %(groupId)s": "Αποτυχία πρόσκλησης χρηστών στο %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "Αποτυχία προσθήκης των ακόλουθων δωματίων στο %(groupId)s:",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Υπάρχουν άγνωστες συσκευές στο δωμάτιο: εάν συνεχίσετε χωρίς να τις επιβεβαιώσετε, θα μπορούσε κάποιος να κρυφακούει την κλήση σας.",
"Show these rooms to non-members on the community page and room list?": "Εμφάνιση αυτών των δωματίων σε μη-μέλη στην σελίδα της κοινότητας και στη λίστα δωματίων;",
"Room name or alias": "Όνομα η ψευδώνυμο δωματίου",
"Restricted": "Περιορισμένο",
"Unable to create widget.": "Αδυναμία δημιουργίας widget.",
"Reload widget": "Ανανέωση widget",
"You are not in this room.": "Δεν είστε μέλος αυτού του δωματίου.",
"You do not have permission to do that in this room.": "Δεν έχετε την άδεια να το κάνετε αυτό σε αυτό το δωμάτιο.",
"You are now ignoring %(userId)s": "Τώρα αγνοείτε τον/την %(userId)s",
"You are no longer ignoring %(userId)s": "Δεν αγνοείτε πια τον/την %(userId)s",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "Ο/Η %(oldDisplayName)s άλλαξε το εμφανιζόμενο όνομά του/της σε %(displayName)s.",
"%(senderName)s changed the pinned messages for the room.": "Ο/Η %(senderName)s άλλαξε τα καρφιτσωμένα μηνύματα του δωματίου.",
"%(widgetName)s widget modified by %(senderName)s": "Έγινε αλλαγή στο widget %(widgetName)s από τον/την %(senderName)s",
"%(widgetName)s widget added by %(senderName)s": "Προστέθηκε το widget %(widgetName)s από τον/την %(senderName)s",
"%(widgetName)s widget removed by %(senderName)s": "Το widget %(widgetName)s αφαιρέθηκε από τον/την %(senderName)s",
"%(names)s and %(count)s others are typing|other": "Ο/Η %(names)s και άλλοι/ες %(count)s πληκτρολογούν",
"%(names)s and %(count)s others are typing|one": "Ο/Η %(names)s και άλλος ένας πληκτρολογούν",
"Message Replies": "Απαντήσεις",
"Message Pinning": "Καρφίτσωμα Μηνυμάτων",
"Hide avatar changes": "Απόκρυψη αλλαγών εικονιδίων χρηστών",
"Hide display name changes": "Απόκρυψη αλλαγών εμφανιζόμενων ονομάτων",
"Hide avatars in user and room mentions": "Απόκρυψη εικονιδίων στις αναφορές χρηστών και δωματίων",
"Enable URL previews for this room (only affects you)": "Ενεργοποίηση προεπισκόπισης URL για αυτό το δωμάτιο (επηρεάζει μόνο εσάς)",
"Delete %(count)s devices|other": "Διαγραφή %(count)s συσκευών",
"Delete %(count)s devices|one": "Διαγραφή συσκευής",
"Select devices": "Επιλογή συσκευών",
"Cannot add any more widgets": "Δεν είναι δυνατή η προσθήκη άλλων widget",
"The maximum permitted number of widgets have already been added to this room.": "Ο μέγιστος επιτρεπτός αριθμός widget έχει ήδη προστεθεί σε αυτό το δωμάτιο.",
"Add a widget": "Προσθήκη widget",
"%(senderName)s sent an image": "Ο/Η %(senderName)s έστειλε μία εικόνα",
"%(senderName)s sent a video": "Ο/Η %(senderName)s έστειλε ένα βίντεο",
"%(senderName)s uploaded a file": "Ο/Η %(senderName)s αναφόρτωσε ένα αρχείο",
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Εάν οι άλλες συσκευές σας δεν έχουν το κλειδί για αυτό το μήνυμα, τότε δεν θα μπορείτε να το αποκρυπτογραφήσετε.",
"Disinvite this user?": "Ακύρωση πρόσκλησης αυτού του χρήστη;",
"Mention": "Αναφορά",
"Invite": "Πρόσκληση",
"User Options": "Επιλογές Χρήστη",
"Send an encrypted reply…": "Αποστολή κρυπτογραφημένης απάντησης…",
"Send a reply (unencrypted)…": "Αποστολή απάντησης (μη κρυπτογραφημένης)…",
"Send an encrypted message…": "Αποστολή κρυπτογραφημένου μηνύματος…",
"Send a message (unencrypted)…": "Αποστολή μηνύματος (μη κρυπτογραφημένου)…",
"Unable to reply": "Αδυναμία απάντησης",
"Unpin Message": "Ξεκαρφίτσωμα μηνύματος",
"Jump to message": "Πηγαίντε στο μήνυμα",
"No pinned messages.": "Κανένα καρφιτσωμένο μήνυμα.",
"Loading...": "Φόρτωση...",
"Pinned Messages": "Καρφιτσωμένα Μηνύματα",
"%(duration)ss": "%(duration)sδ",
"%(duration)sm": "%(duration)sλ",
"%(duration)sh": "%(duration)sω",
"%(duration)sd": "%(duration)sμ",
"Online for %(duration)s": "Σε σύνδεση για %(duration)s",
"Idle for %(duration)s": "Αδρανής για %(duration)s",
"Offline for %(duration)s": "Εκτός σύνδεσης για %(duration)s",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Διαβάστηκε από τον/την %(displayName)s (%(userName)s) στις %(dateTime)s",
"Room Notification": "Ειδοποίηση Δωματίου",
"Notify the whole room": "Ειδοποιήστε όλο το δωμάτιο",
"Sets the room topic": "Ορίζει το θέμα του δωματίου"
} }

View file

@ -114,19 +114,36 @@
"Room %(roomId)s not visible": "Room %(roomId)s not visible", "Room %(roomId)s not visible": "Room %(roomId)s not visible",
"Missing user_id in request": "Missing user_id in request", "Missing user_id in request": "Missing user_id in request",
"Usage": "Usage", "Usage": "Usage",
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
"/ddg is not a command": "/ddg is not a command", "/ddg is not a command": "/ddg is not a command",
"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.", "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.",
"Changes your display nickname": "Changes your display nickname",
"Changes colour scheme of current room": "Changes colour scheme of current room",
"Sets the room topic": "Sets the room topic",
"Invites user with given id to current room": "Invites user with given id to current room",
"Joins room with given alias": "Joins room with given alias",
"Leave room": "Leave room",
"Unrecognised room alias:": "Unrecognised room alias:", "Unrecognised room alias:": "Unrecognised room alias:",
"Kicks user with given id": "Kicks user with given id",
"Bans user with given id": "Bans user with given id",
"Unbans user with given id": "Unbans user with given id",
"Ignores a user, hiding their messages from you": "Ignores a user, hiding their messages from you",
"Ignored user": "Ignored user", "Ignored user": "Ignored user",
"You are now ignoring %(userId)s": "You are now ignoring %(userId)s", "You are now ignoring %(userId)s": "You are now ignoring %(userId)s",
"Stops ignoring a user, showing their messages going forward": "Stops ignoring a user, showing their messages going forward",
"Unignored user": "Unignored user", "Unignored user": "Unignored user",
"You are no longer ignoring %(userId)s": "You are no longer ignoring %(userId)s", "You are no longer ignoring %(userId)s": "You are no longer ignoring %(userId)s",
"Define the power level of a user": "Define the power level of a user",
"Deops user with given id": "Deops user with given id",
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
"Verifies a user, device, and pubkey tuple": "Verifies a user, device, and pubkey tuple",
"Unknown (user, device) pair:": "Unknown (user, device) pair:", "Unknown (user, device) pair:": "Unknown (user, device) pair:",
"Device already verified!": "Device already verified!", "Device already verified!": "Device already verified!",
"WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!", "WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!",
"Verified key": "Verified key", "Verified key": "Verified key",
"The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.",
"Displays action": "Displays action",
"Unrecognised command:": "Unrecognised command:", "Unrecognised command:": "Unrecognised command:",
"Reason": "Reason", "Reason": "Reason",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
@ -186,7 +203,6 @@
"Not a valid Riot keyfile": "Not a valid Riot keyfile", "Not a valid Riot keyfile": "Not a valid Riot keyfile",
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
"Failed to join room": "Failed to join room", "Failed to join room": "Failed to join room",
"Message Replies": "Message Replies",
"Message Pinning": "Message Pinning", "Message Pinning": "Message Pinning",
"Jitsi Conference Calling": "Jitsi Conference Calling", "Jitsi Conference Calling": "Jitsi Conference Calling",
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing", "Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
@ -499,7 +515,6 @@
"Muted Users": "Muted Users", "Muted Users": "Muted Users",
"Banned users": "Banned users", "Banned users": "Banned users",
"This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
"Leave room": "Leave room",
"Favourite": "Favourite", "Favourite": "Favourite",
"Tagged as: ": "Tagged as: ", "Tagged as: ": "Tagged as: ",
"To link to a room it must have <a>an address</a>.": "To link to a room it must have <a>an address</a>.", "To link to a room it must have <a>an address</a>.": "To link to a room it must have <a>an address</a>.",
@ -750,8 +765,8 @@
"Matrix ID": "Matrix ID", "Matrix ID": "Matrix ID",
"Matrix Room ID": "Matrix Room ID", "Matrix Room ID": "Matrix Room ID",
"email address": "email address", "email address": "email address",
"Try using one of the following valid address types: %(validTypesList)s.": "Try using one of the following valid address types: %(validTypesList)s.",
"You have entered an invalid address.": "You have entered an invalid address.", "You have entered an invalid address.": "You have entered an invalid address.",
"Try using one of the following valid address types: %(validTypesList)s.": "Try using one of the following valid address types: %(validTypesList)s.",
"Preparing to send logs": "Preparing to send logs", "Preparing to send logs": "Preparing to send logs",
"Logs sent": "Logs sent", "Logs sent": "Logs sent",
"Thank you!": "Thank you!", "Thank you!": "Thank you!",
@ -976,8 +991,6 @@
"Error whilst fetching joined communities": "Error whilst fetching joined communities", "Error whilst fetching joined communities": "Error whilst fetching joined communities",
"Create a new community": "Create a new community", "Create a new community": "Create a new community",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
"Join an existing community": "Join an existing community",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.",
"You have no visible notifications": "You have no visible notifications", "You have no visible notifications": "You have no visible notifications",
"Members": "Members", "Members": "Members",
"%(count)s Members|other": "%(count)s Members", "%(count)s Members|other": "%(count)s Members",
@ -1146,22 +1159,6 @@
"You need to enter a user name.": "You need to enter a user name.", "You need to enter a user name.": "You need to enter a user name.",
"An unknown error occurred.": "An unknown error occurred.", "An unknown error occurred.": "An unknown error occurred.",
"I already have an account": "I already have an account", "I already have an account": "I already have an account",
"Displays action": "Displays action",
"Bans user with given id": "Bans user with given id",
"Unbans user with given id": "Unbans user with given id",
"Define the power level of a user": "Define the power level of a user",
"Deops user with given id": "Deops user with given id",
"Invites user with given id to current room": "Invites user with given id to current room",
"Joins room with given alias": "Joins room with given alias",
"Sets the room topic": "Sets the room topic",
"Kicks user with given id": "Kicks user with given id",
"Changes your display nickname": "Changes your display nickname",
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
"Changes colour scheme of current room": "Changes colour scheme of current room",
"Verifies a user, device, and pubkey tuple": "Verifies a user, device, and pubkey tuple",
"Ignores a user, hiding their messages from you": "Ignores a user, hiding their messages from you",
"Stops ignoring a user, showing their messages going forward": "Stops ignoring a user, showing their messages going forward",
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
"Commands": "Commands", "Commands": "Commands",
"Results from DuckDuckGo": "Results from DuckDuckGo", "Results from DuckDuckGo": "Results from DuckDuckGo",
"Emoji": "Emoji", "Emoji": "Emoji",

View file

@ -135,7 +135,6 @@
"Failed to kick": "Failed to kick", "Failed to kick": "Failed to kick",
"Failed to leave room": "Failed to leave room", "Failed to leave room": "Failed to leave room",
"Failed to load timeline position": "Failed to load timeline position", "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 mute user": "Failed to mute user",
"Failed to reject invite": "Failed to reject invite", "Failed to reject invite": "Failed to reject invite",
"Failed to reject invitation": "Failed to reject invitation", "Failed to reject invitation": "Failed to reject invitation",
@ -227,7 +226,6 @@
"Mobile phone number": "Mobile phone number", "Mobile phone number": "Mobile phone number",
"Mobile phone number (optional)": "Mobile phone number (optional)", "Mobile phone number (optional)": "Mobile phone number (optional)",
"Moderator": "Moderator", "Moderator": "Moderator",
"Must be viewing a room": "Must be viewing a room",
"Mute": "Mute", "Mute": "Mute",
"Name": "Name", "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 from this device": "Never send encrypted messages to unverified devices from this device",

View file

@ -90,10 +90,8 @@
"You are not in this room.": "Vi ne estas en tiu ĉi ĉambro.", "You are not in this room.": "Vi ne estas en tiu ĉi ĉambro.",
"You do not have permission to do that in this room.": "Vi ne havas permeson fari tion en tiu ĉi ĉambro.", "You do not have permission to do that in this room.": "Vi ne havas permeson fari tion en tiu ĉi ĉambro.",
"Missing room_id in request": "En peto mankas «room_id»", "Missing room_id in request": "En peto mankas «room_id»",
"Must be viewing a room": "Necesas vidi ĉambron",
"Room %(roomId)s not visible": "Ĉambro %(roomId)s ne videblas", "Room %(roomId)s not visible": "Ĉambro %(roomId)s ne videblas",
"Missing user_id in request": "En peto mankas «user_id»", "Missing user_id in request": "En peto mankas «user_id»",
"Failed to lookup current room": "Malsukcesis trovi nunan ĉambron",
"Usage": "Uzo", "Usage": "Uzo",
"/ddg is not a command": "/ddg ne estas komando", "/ddg is not a command": "/ddg ne estas komando",
"To use it, just wait for autocomplete results to load and tab through them.": "Por uzi ĝin, atendu aperon de sugestaj rezultoj, kaj tabu tra ili.", "To use it, just wait for autocomplete results to load and tab through them.": "Por uzi ĝin, atendu aperon de sugestaj rezultoj, kaj tabu tra ili.",
@ -165,7 +163,6 @@
"Authentication check failed: incorrect password?": "Aŭtentiga kontrolo malsukcesis: ĉu pro malĝusta pasvorto?", "Authentication check failed: incorrect password?": "Aŭtentiga kontrolo malsukcesis: ĉu pro malĝusta pasvorto?",
"Failed to join room": "Malsukcesis aliĝi al ĉambro", "Failed to join room": "Malsukcesis aliĝi al ĉambro",
"Message Pinning": "Fikso de mesaĝoj", "Message Pinning": "Fikso de mesaĝoj",
"Tag Panel": "Etikeda panelo",
"Disable Emoji suggestions while typing": "Malŝalti mienetajn sugestojn dum tajpado", "Disable Emoji suggestions while typing": "Malŝalti mienetajn sugestojn dum tajpado",
"Use compact timeline layout": "Uzi densan okazordan aranĝon", "Use compact timeline layout": "Uzi densan okazordan aranĝon",
"Hide removed messages": "Kaŝi forigitajn mesaĝojn", "Hide removed messages": "Kaŝi forigitajn mesaĝojn",
@ -746,8 +743,6 @@
"Error whilst fetching joined communities": "Okazis eraro dum venigado de viaj komunumoj", "Error whilst fetching joined communities": "Okazis eraro dum venigado de viaj komunumoj",
"Create a new community": "Krei novan komunumon", "Create a new community": "Krei novan komunumon",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Kreu komunumon por kunigi uzantojn kaj ĉambrojn! Fari propran hejmpaĝon por montri vian spacon en la universo de Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Kreu komunumon por kunigi uzantojn kaj ĉambrojn! Fari propran hejmpaĝon por montri vian spacon en la universo de Matrix.",
"Join an existing community": "Aliĝi al jama komunumo",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Por aliĝi al jama komunumo, vi devos scii ĝian komunuman identigilon; ĝi aspektas proksimume tiel ĉi: <i>+ekzemplo:matrix.org</i>.",
"You have no visible notifications": "Neniuj videblaj sciigoj", "You have no visible notifications": "Neniuj videblaj sciigoj",
"Scroll to bottom of page": "Rulumi al susbo de la paĝo", "Scroll to bottom of page": "Rulumi al susbo de la paĝo",
"Message not sent due to unknown devices being present": "Mesaĝoj ne sendiĝis pro ĉeesto de nekonataj aparatoj", "Message not sent due to unknown devices being present": "Mesaĝoj ne sendiĝis pro ĉeesto de nekonataj aparatoj",

View file

@ -108,7 +108,6 @@
"Failed to kick": "Falló al expulsar", "Failed to kick": "Falló al expulsar",
"Failed to leave room": "Falló al dejar la sala", "Failed to leave room": "Falló al dejar la sala",
"Failed to load timeline position": "Falló al cargar el historico", "Failed to load timeline position": "Falló al cargar el historico",
"Failed to lookup current room": "Falló al buscar la actual sala",
"Failed to mute user": "Falló al silenciar el usuario", "Failed to mute user": "Falló al silenciar el usuario",
"Failed to reject invite": "Falló al rechazar invitación", "Failed to reject invite": "Falló al rechazar invitación",
"Failed to reject invitation": "Falló al rechazar la invitación", "Failed to reject invitation": "Falló al rechazar la invitación",
@ -313,7 +312,6 @@
"Mobile phone number": "Número de teléfono móvil", "Mobile phone number": "Número de teléfono móvil",
"Mobile phone number (optional)": "Número de teléfono móvil (opcional)", "Mobile phone number (optional)": "Número de teléfono móvil (opcional)",
"Moderator": "Moderador", "Moderator": "Moderador",
"Must be viewing a room": "Debe estar viendo una sala",
"Mute": "Silenciar", "Mute": "Silenciar",
"%(serverName)s Matrix ID": "%(serverName)s ID de Matrix", "%(serverName)s Matrix ID": "%(serverName)s ID de Matrix",
"Name": "Nombre", "Name": "Nombre",

View file

@ -140,7 +140,6 @@
"Identity Server is": "Identitate zerbitzaria:", "Identity Server is": "Identitate zerbitzaria:",
"Mobile phone number (optional)": "Mugikor zenbakia (aukerazkoa)", "Mobile phone number (optional)": "Mugikor zenbakia (aukerazkoa)",
"Moderator": "Moderatzailea", "Moderator": "Moderatzailea",
"Must be viewing a room": "Gela bat ikusten egon behar da",
"Account": "Kontua", "Account": "Kontua",
"Access Token:": "Sarbide tokena:", "Access Token:": "Sarbide tokena:",
"Active call (%(roomName)s)": "Dei aktiboa (%(roomName)s)", "Active call (%(roomName)s)": "Dei aktiboa (%(roomName)s)",
@ -243,7 +242,6 @@
"Failed to kick": "Huts egin du kanporatzean", "Failed to kick": "Huts egin du kanporatzean",
"Failed to leave room": "Huts egin du gelatik ateratzean", "Failed to leave room": "Huts egin du gelatik ateratzean",
"Failed to load timeline position": "Huts egin du denbora-lerroko puntua kargatzean", "Failed to load timeline position": "Huts egin du denbora-lerroko puntua kargatzean",
"Failed to lookup current room": "Huts egin du uneko gela bilatzean",
"Failed to mute user": "Huts egin du erabiltzailea mututzean", "Failed to mute user": "Huts egin du erabiltzailea mututzean",
"Failed to reject invite": "Huts egin du gonbidapena baztertzean", "Failed to reject invite": "Huts egin du gonbidapena baztertzean",
"Failed to reject invitation": "Huts egin du gonbidapena baztertzean", "Failed to reject invitation": "Huts egin du gonbidapena baztertzean",
@ -586,7 +584,7 @@
"Please enter the code it contains:": "Sartu dakarren kodea:", "Please enter the code it contains:": "Sartu dakarren kodea:",
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Ez baduzu e-mail helbide bat zehazten, ezin izango duzu zure pasahitza berrezarri. Ziur zaude?", "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Ez baduzu e-mail helbide bat zehazten, ezin izango duzu zure pasahitza berrezarri. Ziur zaude?",
"You are registering with %(SelectedTeamName)s": "%(SelectedTeamName)s erabiliz erregistratzen ari zara", "You are registering with %(SelectedTeamName)s": "%(SelectedTeamName)s erabiliz erregistratzen ari zara",
"Default server": "Zerbitzari lenetetsia", "Default server": "Zerbitzari lehenetsia",
"Custom server": "Zerbitzari aukeratua", "Custom server": "Zerbitzari aukeratua",
"Home server URL": "Hasiera zerbitzariaren URLa", "Home server URL": "Hasiera zerbitzariaren URLa",
"Identity server URL": "Identitate zerbitzariaren URLa", "Identity server URL": "Identitate zerbitzariaren URLa",
@ -722,7 +720,6 @@
"%(names)s and %(count)s others are typing|one": "%(names)s eta beste bat idazten ari dira", "%(names)s and %(count)s others are typing|one": "%(names)s eta beste bat idazten ari dira",
"Send": "Bidali", "Send": "Bidali",
"Message Pinning": "Mezuak finkatzea", "Message Pinning": "Mezuak finkatzea",
"Tag Panel": "Etiketen panela",
"Hide avatar changes": "Ezkutatu abatar aldaketak", "Hide avatar changes": "Ezkutatu abatar aldaketak",
"Hide display name changes": "Ezkutatu pantaila izenen aldaketak", "Hide display name changes": "Ezkutatu pantaila izenen aldaketak",
"Disable big emoji in chat": "Desgaitu emoji handiak txatean", "Disable big emoji in chat": "Desgaitu emoji handiak txatean",
@ -807,7 +804,6 @@
"Old cryptography data detected": "Kriptografia datu zaharrak atzeman dira", "Old cryptography data detected": "Kriptografia datu zaharrak atzeman dira",
"Your Communities": "Zure komunitateak", "Your Communities": "Zure komunitateak",
"Create a new community": "Sortu komunitate berria", "Create a new community": "Sortu komunitate berria",
"Join an existing community": "Elkartu badagoen komunitate batetara",
"Warning": "Abisua", "Warning": "Abisua",
"Please note you are logging into the %(hs)s server, not matrix.org.": "Kontuan izan %(hs)s zerbitzarira elkartu zarela, ez matrix.org.", "Please note you are logging into the %(hs)s server, not matrix.org.": "Kontuan izan %(hs)s zerbitzarira elkartu zarela, ez matrix.org.",
"Sign in to get started": "Hasi saioa hasteko", "Sign in to get started": "Hasi saioa hasteko",
@ -844,8 +840,8 @@
"were unbanned %(count)s times|one": "debekua kendu zaie", "were unbanned %(count)s times|one": "debekua kendu zaie",
"was unbanned %(count)s times|other": "%(count)s aldiz kendu zaio debekua", "was unbanned %(count)s times|other": "%(count)s aldiz kendu zaio debekua",
"was unbanned %(count)s times|one": "debekua kendu zaio", "was unbanned %(count)s times|one": "debekua kendu zaio",
"were kicked %(count)s times|other": "%(count)s kanporatu zaie", "were kicked %(count)s times|other": "%(count)s aldiz kanporatu zaie",
"were kicked %(count)s times|one": "kanporatu zaie", "were kicked %(count)s times|one": "(r) kanporatu zaie",
"was kicked %(count)s times|other": "%(count)s aldiz kanporatu zaio", "was kicked %(count)s times|other": "%(count)s aldiz kanporatu zaio",
"was kicked %(count)s times|one": "kanporatu zaio", "was kicked %(count)s times|one": "kanporatu zaio",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s erabiltzaileek bere izena aldatu dute %(count)s aldiz", "%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s erabiltzaileek bere izena aldatu dute %(count)s aldiz",
@ -926,7 +922,6 @@
"Custom of %(powerLevel)s": "%(powerLevel)s pertsonalizatua", "Custom of %(powerLevel)s": "%(powerLevel)s pertsonalizatua",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Riot bertsio zahar batek datuak antzeman dira. Honek bertsio zaharrean muturretik muturrerako zifratzea ez funtzionatzea eragingo du. Azkenaldian bertsio zaharrean bidali edo jasotako zifratutako mezuak agian ezin izango dira deszifratu bertsio honetan. Honek ere Bertsio honekin egindako mezu trukeak huts egitea ekar dezake. Arazoak badituzu, amaitu saioa eta hasi berriro saioa. Mezuen historiala gordetzeko, esportatu eta berriro inportatu zure gakoak.", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Riot bertsio zahar batek datuak antzeman dira. Honek bertsio zaharrean muturretik muturrerako zifratzea ez funtzionatzea eragingo du. Azkenaldian bertsio zaharrean bidali edo jasotako zifratutako mezuak agian ezin izango dira deszifratu bertsio honetan. Honek ere Bertsio honekin egindako mezu trukeak huts egitea ekar dezake. Arazoak badituzu, amaitu saioa eta hasi berriro saioa. Mezuen historiala gordetzeko, esportatu eta berriro inportatu zure gakoak.",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Sortu komunitate bat erabiltzaileak eta gelak biltzeko! Sortu zure hasiera orria eta markatu zure espazioa Matrix unibertsoan.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Sortu komunitate bat erabiltzaileak eta gelak biltzeko! Sortu zure hasiera orria eta markatu zure espazioa Matrix unibertsoan.",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Bdagoen komunitate batera elkartzeko, komunitatearen identifikatzailea jakin behar duzu; honen antza izango du <i>+adibidea:matrix.org</i>.",
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Ez dago beste inor hemen! <inviteText>Beste batzuk gonbidatu</inviteText> nahi dituzu edo <nowarnText>gela hutsik dagoela abisatzeari utzi</nowarnText>?", "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Ez dago beste inor hemen! <inviteText>Beste batzuk gonbidatu</inviteText> nahi dituzu edo <nowarnText>gela hutsik dagoela abisatzeari utzi</nowarnText>?",
"Light theme": "Itxura argia", "Light theme": "Itxura argia",
"Dark theme": "Itxura iluna", "Dark theme": "Itxura iluna",
@ -937,7 +932,6 @@
"%(count)s of your messages have not been sent.|one": "Zure mezua ez da bidali.", "%(count)s of your messages have not been sent.|one": "Zure mezua ez da bidali.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Birbidali guztiak</resendText> edo <cancelText>baztertu guztiak</cancelText> orain. Mezuak banaka birbidali edo baztertu ditzakezu ere.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Birbidali guztiak</resendText> edo <cancelText>baztertu guztiak</cancelText> orain. Mezuak banaka birbidali edo baztertu ditzakezu ere.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Birbidali mezua</resendText> edo <cancelText>baztertu mezua</cancelText> orain.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Birbidali mezua</resendText> edo <cancelText>baztertu mezua</cancelText> orain.",
"Message Replies": "Mezuei erantzunak",
"Send an encrypted reply…": "Bidali zifratutako erantzun bat…", "Send an encrypted reply…": "Bidali zifratutako erantzun bat…",
"Send a reply (unencrypted)…": "Bidali erantzun bat (zifratu gabea)…", "Send a reply (unencrypted)…": "Bidali erantzun bat (zifratu gabea)…",
"Send an encrypted message…": "Bidali zifratutako mezu bat…", "Send an encrypted message…": "Bidali zifratutako mezu bat…",
@ -1186,5 +1180,20 @@
"Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.": "Matrix-eko mezuen ikusgaitasuna e-mail sistemaren antekoa da. Guk zure mezuak ahaztean ez dizkiogu erabiltzaile berriei edo izena eman ez dutenei erakutsiko, baina jada zure mezuak jaso dituzten erregistratutako erabiltzaileen bere kopia izaten jarraituko dute.", "Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.": "Matrix-eko mezuen ikusgaitasuna e-mail sistemaren antekoa da. Guk zure mezuak ahaztean ez dizkiogu erabiltzaile berriei edo izena eman ez dutenei erakutsiko, baina jada zure mezuak jaso dituzten erregistratutako erabiltzaileen bere kopia izaten jarraituko dute.",
"Please forget all messages I have sent when my account is deactivated (<b>Warning:</b> this will cause future users to see an incomplete view of conversations)": "Ahaztu bidali ditudan mezu guztiak kontua desaktibatzean (Abisua: Honekin etorkizuneko erabiltzaileek elkarrizketaren bertsio ez oso bat ikusiko dute)", "Please forget all messages I have sent when my account is deactivated (<b>Warning:</b> this will cause future users to see an incomplete view of conversations)": "Ahaztu bidali ditudan mezu guztiak kontua desaktibatzean (Abisua: Honekin etorkizuneko erabiltzaileek elkarrizketaren bertsio ez oso bat ikusiko dute)",
"Can't leave Server Notices room": "Ezin zara Server Notices gelatik atera", "Can't leave Server Notices room": "Ezin zara Server Notices gelatik atera",
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Gela hau mezu hasiera zerbitzariaren garrantzitsuak bidaltzeko erabiltzen da, eta ezin zara atera." "This room is used for important messages from the Homeserver, so you cannot leave it.": "Gela hau mezu hasiera zerbitzariaren garrantzitsuak bidaltzeko erabiltzen da, eta ezin zara atera.",
"Try the app first": "Probatu aplikazioa aurretik",
"Encrypting": "Zifratzen",
"Encrypted, not sent": "Zifratua, bidali gabe",
"Share Link to User": "Partekatu esteka erabiltzailearekin",
"Share room": "Partekatu gela",
"Share Room": "Partekatu gela",
"Link to most recent message": "Esteka azken mezura",
"Share User": "Partekatu erabiltzailea",
"Share Community": "Partekatu komunitatea",
"Share Room Message": "Partekatu gelako mezua",
"Link to selected message": "Esteka hautatutako mezura",
"COPY": "KOPIATU",
"Share Message": "Partekatu mezua",
"No Audio Outputs detected": "Ez da audio irteerarik antzeman",
"Audio Output": "Audio irteera"
} }

View file

@ -450,7 +450,6 @@
"End-to-end encryption is in beta and may not be reliable": "Päästä päähän salaus on vielä testausvaiheessa ja saattaa toimia epävarmasti", "End-to-end encryption is in beta and may not be reliable": "Päästä päähän salaus on vielä testausvaiheessa ja saattaa toimia epävarmasti",
"Error: Problem communicating with the given homeserver.": "Virhe: Ongelma yhteydenpidossa kotipalvelimeen.", "Error: Problem communicating with the given homeserver.": "Virhe: Ongelma yhteydenpidossa kotipalvelimeen.",
"Existing Call": "Käynnissä oleva puhelu", "Existing Call": "Käynnissä oleva puhelu",
"Failed to lookup current room": "Nykyisen huoneen löytäminen epäonnistui",
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.": "Liity käyttäen <voiceText>ääntä</voiceText> tai <videoText>videota</videoText>.", "Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.": "Liity käyttäen <voiceText>ääntä</voiceText> tai <videoText>videota</videoText>.",
"%(targetName)s joined the room.": "%(targetName)s liittyi huoneeseen.", "%(targetName)s joined the room.": "%(targetName)s liittyi huoneeseen.",
"%(senderName)s kicked %(targetName)s.": "%(senderName)s poisti käyttäjän %(targetName)s huoneesta.", "%(senderName)s kicked %(targetName)s.": "%(senderName)s poisti käyttäjän %(targetName)s huoneesta.",
@ -458,7 +457,6 @@
"Publish this room to the public in %(domain)s's room directory?": "Julkaise tämä huone domainin %(domain)s huoneluettelossa?", "Publish this room to the public in %(domain)s's room directory?": "Julkaise tämä huone domainin %(domain)s huoneluettelossa?",
"Missing room_id in request": "room_id puuttuu kyselystä", "Missing room_id in request": "room_id puuttuu kyselystä",
"Missing user_id in request": "user_id puuttuu kyselystä", "Missing user_id in request": "user_id puuttuu kyselystä",
"Must be viewing a room": "Pakko olla huoneessa",
"Never send encrypted messages to unverified devices from this device": "Älä koskaa lähetä salattuja viestejä varmentamattomiin laitteisiin tältä laitteelta", "Never send encrypted messages to unverified devices from this device": "Älä koskaa lähetä salattuja viestejä varmentamattomiin laitteisiin tältä laitteelta",
"Never send encrypted messages to unverified devices in this room from this device": "Älä koskaa lähetä salattuja viestejä varmentamattomiin laitteisiin tässä huoneessa tältä laitteelta", "Never send encrypted messages to unverified devices in this room from this device": "Älä koskaa lähetä salattuja viestejä varmentamattomiin laitteisiin tässä huoneessa tältä laitteelta",
"New address (e.g. #foo:%(localDomain)s)": "Uusi osoite (esim. #foo:%(localDomain)s)", "New address (e.g. #foo:%(localDomain)s)": "Uusi osoite (esim. #foo:%(localDomain)s)",
@ -789,7 +787,6 @@
"You're not currently a member of any communities.": "Et ole minkään yhteisön jäsen tällä hetkellä.", "You're not currently a member of any communities.": "Et ole minkään yhteisön jäsen tällä hetkellä.",
"Error whilst fetching joined communities": "Virhe ladatessa listaa yhteistöistä joihin olet liittynyt", "Error whilst fetching joined communities": "Virhe ladatessa listaa yhteistöistä joihin olet liittynyt",
"Create a new community": "Luo uusi yhteisö", "Create a new community": "Luo uusi yhteisö",
"Join an existing community": "Liity olemassaolevaan yhteisöön",
"Light theme": "Vaalea ulkoasu", "Light theme": "Vaalea ulkoasu",
"Dark theme": "Tumma ulkoasu", "Dark theme": "Tumma ulkoasu",
"Status.im theme": "Status.im ulkoasu", "Status.im theme": "Status.im ulkoasu",
@ -823,7 +820,6 @@
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s pienoisohjelman lisännyt %(senderName)s", "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s pienoisohjelman lisännyt %(senderName)s",
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s pienoisohjelman poistanut %(senderName)s", "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s pienoisohjelman poistanut %(senderName)s",
"Send": "Lähetä", "Send": "Lähetä",
"Tag Panel": "Tagit",
"Delete %(count)s devices|other": "Poista %(count)s laitetta", "Delete %(count)s devices|other": "Poista %(count)s laitetta",
"Delete %(count)s devices|one": "Poista laite", "Delete %(count)s devices|one": "Poista laite",
"Select devices": "Valitse laitteet", "Select devices": "Valitse laitteet",

View file

@ -105,7 +105,6 @@
"Failed to kick": "Échec de l'exclusion", "Failed to kick": "Échec de l'exclusion",
"Failed to leave room": "Échec du départ du salon", "Failed to leave room": "Échec du départ du salon",
"Failed to load timeline position": "Échec du chargement de la position dans l'historique", "Failed to load timeline position": "Échec du chargement de la position dans l'historique",
"Failed to lookup current room": "Échec de la recherche du salon actuel",
"Failed to mute user": "Échec de la mise en sourdine de l'utilisateur", "Failed to mute user": "Échec de la mise en sourdine de l'utilisateur",
"Failed to reject invite": "Échec du rejet de l'invitation", "Failed to reject invite": "Échec du rejet de l'invitation",
"Failed to reject invitation": "Échec du rejet de l'invitation", "Failed to reject invitation": "Échec du rejet de l'invitation",
@ -182,7 +181,6 @@
"Missing user_id in request": "Absence du user_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", "Mobile phone number": "Numéro de téléphone mobile",
"Moderator": "Modérateur", "Moderator": "Modérateur",
"Must be viewing a room": "Doit être en train de visualiser un salon",
"%(serverName)s Matrix ID": "%(serverName)s identifiant Matrix", "%(serverName)s Matrix ID": "%(serverName)s identifiant Matrix",
"Name": "Nom", "Name": "Nom",
"Never send encrypted messages to unverified devices from this device": "Ne jamais envoyer de message chiffré aux appareils non vérifiés depuis cet appareil", "Never send encrypted messages to unverified devices from this device": "Ne jamais envoyer de message chiffré aux appareils non vérifiés depuis cet appareil",
@ -863,8 +861,6 @@
"Error whilst fetching joined communities": "Erreur lors de l'obtention des communautés rejointes", "Error whilst fetching joined communities": "Erreur lors de l'obtention des communautés rejointes",
"Create a new community": "Créer une nouvelle communauté", "Create a new community": "Créer une nouvelle communauté",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Créez une communauté pour grouper des utilisateurs et des salons ! Construisez une page d'accueil personnalisée pour distinguer votre espace dans l'univers Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Créez une communauté pour grouper des utilisateurs et des salons ! Construisez une page d'accueil personnalisée pour distinguer votre espace dans l'univers Matrix.",
"Join an existing community": "Rejoindre une communauté existante",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Pour rejoindre une communauté existante, vous devrez connaître son identifiant. Cela ressemblera à <i>+exemple:matrix.org</i>.",
"Disable Emoji suggestions while typing": "Désactiver les suggestions d'emojis lors de la saisie", "Disable Emoji suggestions while typing": "Désactiver les suggestions d'emojis lors de la saisie",
"Disable big emoji in chat": "Désactiver les gros emojis dans les discussions", "Disable big emoji in chat": "Désactiver les gros emojis dans les discussions",
"Mirror local video feed": "Refléter le flux vidéo local", "Mirror local video feed": "Refléter le flux vidéo local",
@ -918,7 +914,6 @@
"Flair will appear if enabled in room settings": "Les badges n'apparaîtront que s'ils sont activés dans les paramètres de chaque salon", "Flair will appear if enabled in room settings": "Les badges n'apparaîtront que s'ils sont activés dans les paramètres de chaque salon",
"Flair will not appear": "Les badges n'apparaîtront pas", "Flair will not appear": "Les badges n'apparaîtront pas",
"Display your community flair in rooms configured to show it.": "Sélectionnez les badges dans les paramètres de chaque salon pour les afficher.", "Display your community flair in rooms configured to show it.": "Sélectionnez les badges dans les paramètres de chaque salon pour les afficher.",
"Tag Panel": "Panneau des étiquettes",
"Addresses": "Adresses", "Addresses": "Adresses",
"expand": "développer", "expand": "développer",
"collapse": "réduire", "collapse": "réduire",
@ -937,7 +932,6 @@
"%(count)s of your messages have not been sent.|one": "Votre message n'a pas été envoyé.", "%(count)s of your messages have not been sent.|one": "Votre message n'a pas été envoyé.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Tout renvoyer</resendText> ou <cancelText>tout annuler</cancelText> maintenant. Vous pouvez aussi choisir des messages individuels à renvoyer ou annuler.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Tout renvoyer</resendText> ou <cancelText>tout annuler</cancelText> maintenant. Vous pouvez aussi choisir des messages individuels à renvoyer ou annuler.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Renvoyer le message</resendText> ou <cancelText>annuler le message</cancelText> maintenant.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Renvoyer le message</resendText> ou <cancelText>annuler le message</cancelText> maintenant.",
"Message Replies": "Réponses",
"Send an encrypted reply…": "Envoyer une réponse chiffrée…", "Send an encrypted reply…": "Envoyer une réponse chiffrée…",
"Send a reply (unencrypted)…": "Envoyer une réponse (non chiffrée)…", "Send a reply (unencrypted)…": "Envoyer une réponse (non chiffrée)…",
"Send an encrypted message…": "Envoyer un message chiffré…", "Send an encrypted message…": "Envoyer un message chiffré…",
@ -1186,5 +1180,20 @@
"Yes, I want to help!": "Oui, je veux aider !", "Yes, I want to help!": "Oui, je veux aider !",
"Can't leave Server Notices room": "Impossible de quitter le salon des Annonces du serveur", "Can't leave Server Notices room": "Impossible de quitter le salon des Annonces du serveur",
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Ce salon est utilisé pour les messages importants du serveur d'accueil, donc vous ne pouvez pas en partir.", "This room is used for important messages from the Homeserver, so you cannot leave it.": "Ce salon est utilisé pour les messages importants du serveur d'accueil, donc vous ne pouvez pas en partir.",
"To notify everyone in the room, you must be a": "Pour notifier tout le monde dans le salon, vous devez être un(e)" "To notify everyone in the room, you must be a": "Pour notifier tout le monde dans le salon, vous devez être un(e)",
"Try the app first": "Essayer d'abord l'application",
"Encrypting": "Chiffrement en cours",
"Encrypted, not sent": "Chiffré, pas envoyé",
"No Audio Outputs detected": "Aucune sortie audio détectée",
"Audio Output": "Sortie audio",
"Share Link to User": "Partager le lien vers l'utilisateur",
"Share room": "Partager le salon",
"Share Room": "Partager le salon",
"Link to most recent message": "Lien vers le message le plus récent",
"Share User": "Partager l'utilisateur",
"Share Community": "Partager la communauté",
"Share Room Message": "Partager le message du salon",
"Link to selected message": "Lien vers le message sélectionné",
"COPY": "COPIER",
"Share Message": "Partager le message"
} }

File diff suppressed because it is too large Load diff

View file

@ -168,7 +168,6 @@
"Failed to kick": "Kirúgás nem sikerült", "Failed to kick": "Kirúgás nem sikerült",
"Failed to leave room": "A szobát nem sikerült elhagyni", "Failed to leave room": "A szobát nem sikerült elhagyni",
"Failed to load timeline position": "Az idővonal pozíciót nem sikerült betölteni", "Failed to load timeline position": "Az idővonal pozíciót nem sikerült betölteni",
"Failed to lookup current room": "Az aktuális szoba felkeresése sikertelen",
"Failed to mute user": "A felhasználót nem sikerült hallgatásra bírni", "Failed to mute user": "A felhasználót nem sikerült hallgatásra bírni",
"Failed to reject invite": "A meghívót nem sikerült elutasítani", "Failed to reject invite": "A meghívót nem sikerült elutasítani",
"Failed to reject invitation": "A meghívót nem sikerült elutasítani", "Failed to reject invitation": "A meghívót nem sikerült elutasítani",
@ -255,7 +254,6 @@
"Mobile phone number": "Mobil telefonszám", "Mobile phone number": "Mobil telefonszám",
"Mobile phone number (optional)": "Mobill telefonszám (opcionális)", "Mobile phone number (optional)": "Mobill telefonszám (opcionális)",
"Moderator": "Moderátor", "Moderator": "Moderátor",
"Must be viewing a room": "Meg kell nézni a szobát",
"%(serverName)s Matrix ID": "%(serverName)s Matrix azonosítóm", "%(serverName)s Matrix ID": "%(serverName)s Matrix azonosítóm",
"Name": "Név", "Name": "Név",
"Never send encrypted messages to unverified devices from this device": "Soha ne küldj titkosított üzenetet ellenőrizetlen eszközre erről az eszközről", "Never send encrypted messages to unverified devices from this device": "Soha ne küldj titkosított üzenetet ellenőrizetlen eszközre erről az eszközről",
@ -782,8 +780,6 @@
"This Home server does not support communities": "Ez a saját szerver nem támogatja a közösségeket", "This Home server does not support communities": "Ez a saját szerver nem támogatja a közösségeket",
"Error whilst fetching joined communities": "Hiba a csatlakozott közösségek betöltésénél", "Error whilst fetching joined communities": "Hiba a csatlakozott közösségek betöltésénél",
"Create a new community": "Új közösség létrehozása", "Create a new community": "Új közösség létrehozása",
"Join an existing community": "Meglévő közösséghez csatlakozás",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Ahhoz hogy csatlakozni tudj egy meglévő közösséghez ismerned kell a közösségi azonosítót ami például így nézhet ki: <i>+pelda:matrix.org</i>.",
"example": "példa", "example": "példa",
"Failed to load %(groupId)s": "Nem sikerült betölteni: %(groupId)s", "Failed to load %(groupId)s": "Nem sikerült betölteni: %(groupId)s",
"Your Communities": "Közösségeid", "Your Communities": "Közösségeid",
@ -918,7 +914,6 @@
"Something went wrong when trying to get your communities.": "Valami nem sikerült a közösségeid elérésénél.", "Something went wrong when trying to get your communities.": "Valami nem sikerült a közösségeid elérésénél.",
"Display your community flair in rooms configured to show it.": "Közösségi jelvényeid megjelenítése azokban a szobákban ahol ez engedélyezett.", "Display your community flair in rooms configured to show it.": "Közösségi jelvényeid megjelenítése azokban a szobákban ahol ez engedélyezett.",
"This homeserver doesn't offer any login flows which are supported by this client.": "Ez a saját szerver egyetlen bejelentkezési metódust sem támogat amit ez a kliens ismer.", "This homeserver doesn't offer any login flows which are supported by this client.": "Ez a saját szerver egyetlen bejelentkezési metódust sem támogat amit ez a kliens ismer.",
"Tag Panel": "Címke panel",
"Addresses": "Címek", "Addresses": "Címek",
"collapse": "becsuk", "collapse": "becsuk",
"expand": "kinyit", "expand": "kinyit",
@ -937,7 +932,6 @@
"%(count)s of your messages have not been sent.|one": "Az üzeneted nem lett elküldve.", "%(count)s of your messages have not been sent.|one": "Az üzeneted nem lett elküldve.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Újraküldöd mind</resendText> vagy <cancelText>elveted mind</cancelText>. Az üzeneteket egyenként is elküldheted vagy elvetheted.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Újraküldöd mind</resendText> vagy <cancelText>elveted mind</cancelText>. Az üzeneteket egyenként is elküldheted vagy elvetheted.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Üzenet újraküldése</resendText> vagy <cancelText>üzenet elvetése</cancelText> most.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Üzenet újraküldése</resendText> vagy <cancelText>üzenet elvetése</cancelText> most.",
"Message Replies": "Üzenet válaszok",
"Send an encrypted reply…": "Titkosított válasz küldése…", "Send an encrypted reply…": "Titkosított válasz küldése…",
"Send a reply (unencrypted)…": "Válasz küldése (titkosítatlanul)…", "Send a reply (unencrypted)…": "Válasz küldése (titkosítatlanul)…",
"Send an encrypted message…": "Titkosított üzenet küldése…", "Send an encrypted message…": "Titkosított üzenet küldése…",
@ -1186,5 +1180,20 @@
"Yes, I want to help!": "Igen, segítek!", "Yes, I want to help!": "Igen, segítek!",
"Can't leave Server Notices room": "Nem lehet elhagyni a Szerver Üzenetek szobát", "Can't leave Server Notices room": "Nem lehet elhagyni a Szerver Üzenetek szobát",
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Ez a szoba fontos szerverüzenetek közlésére jött létre, nem tudsz kilépni belőle.", "This room is used for important messages from the Homeserver, so you cannot leave it.": "Ez a szoba fontos szerverüzenetek közlésére jött létre, nem tudsz kilépni belőle.",
"To notify everyone in the room, you must be a": "Hogy mindenkinek tudj üzenni ahhoz ilyen szinten kell lenned:" "To notify everyone in the room, you must be a": "Hogy mindenkinek tudj üzenni ahhoz ilyen szinten kell lenned:",
"Try the app first": "Először próbáld ki az alkalmazást",
"Encrypting": "Titkosít",
"Encrypted, not sent": "Titkosítva, de nincs elküldve",
"No Audio Outputs detected": "Nem található hang kimenet",
"Audio Output": "Hang kimenet",
"Share Link to User": "Hivatkozás megosztása felhasználóval",
"Share room": "Szoba megosztása",
"Share Room": "Szoba megosztása",
"Link to most recent message": "A legfrissebb üzenetre hivatkozás",
"Share User": "Felhasználó megosztás",
"Share Community": "Közösség megosztás",
"Share Room Message": "Szoba üzenet megosztás",
"Link to selected message": "Hivatkozás a kijelölt üzenetre",
"COPY": "Másol",
"Share Message": "Üzenet megosztása"
} }

View file

@ -160,10 +160,8 @@
"You are not in this room.": "Non sei in questa stanza.", "You are not in this room.": "Non sei in questa stanza.",
"You do not have permission to do that in this room.": "Non hai l'autorizzazione per farlo in questa stanza.", "You do not have permission to do that in this room.": "Non hai l'autorizzazione per farlo in questa stanza.",
"Missing room_id in request": "Manca l'id_stanza nella richiesta", "Missing room_id in request": "Manca l'id_stanza nella richiesta",
"Must be viewing a room": "Devi vedere una stanza",
"Room %(roomId)s not visible": "Stanza %(roomId)s non visibile", "Room %(roomId)s not visible": "Stanza %(roomId)s non visibile",
"Missing user_id in request": "Manca l'id_utente nella richiesta", "Missing user_id in request": "Manca l'id_utente nella richiesta",
"Failed to lookup current room": "Impossibile cercare la stanza attuale",
"Usage": "Utilizzo", "Usage": "Utilizzo",
"/ddg is not a command": "/ddg non è un comando", "/ddg is not a command": "/ddg non è un comando",
"To use it, just wait for autocomplete results to load and tab through them.": "Per usarlo, attendi l'autocompletamento dei risultati e selezionali con tab.", "To use it, just wait for autocomplete results to load and tab through them.": "Per usarlo, attendi l'autocompletamento dei risultati e selezionali con tab.",
@ -231,7 +229,6 @@
"Not a valid Riot keyfile": "Non è una chiave di Riot valida", "Not a valid Riot keyfile": "Non è una chiave di Riot valida",
"Authentication check failed: incorrect password?": "Controllo di autenticazione fallito: password sbagliata?", "Authentication check failed: incorrect password?": "Controllo di autenticazione fallito: password sbagliata?",
"Failed to join room": "Accesso alla stanza fallito", "Failed to join room": "Accesso alla stanza fallito",
"Tag Panel": "Pannello etichette",
"Disable Emoji suggestions while typing": "Disattiva i suggerimenti delle emoji durante la digitazione", "Disable Emoji suggestions while typing": "Disattiva i suggerimenti delle emoji durante la digitazione",
"Use compact timeline layout": "Usa impaginazione cronologia compatta", "Use compact timeline layout": "Usa impaginazione cronologia compatta",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Nascondi i messaggi di entrata/uscita (inviti/kick/ban esclusi)", "Hide join/leave messages (invites/kicks/bans unaffected)": "Nascondi i messaggi di entrata/uscita (inviti/kick/ban esclusi)",
@ -794,8 +791,6 @@
"Error whilst fetching joined communities": "Errore nella rilevazione delle comunità a cui ti sei unito", "Error whilst fetching joined communities": "Errore nella rilevazione delle comunità a cui ti sei unito",
"Create a new community": "Crea una nuova comunità", "Create a new community": "Crea una nuova comunità",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crea una comunità per raggruppare utenti e stanze! Crea una pagina iniziale personalizzata per stabilire il tuo spazio nell'universo di Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crea una comunità per raggruppare utenti e stanze! Crea una pagina iniziale personalizzata per stabilire il tuo spazio nell'universo di Matrix.",
"Join an existing community": "Unisciti ad una comunità esistente",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Per unirti ad una comunità esistente devi conoscere il suo identificativo; è qualcosa del tipo <i>+esempio:matrix.org</i>.",
"You have no visible notifications": "Non hai alcuna notifica visibile", "You have no visible notifications": "Non hai alcuna notifica visibile",
"Scroll to bottom of page": "Scorri in fondo alla pagina", "Scroll to bottom of page": "Scorri in fondo alla pagina",
"Message not sent due to unknown devices being present": "Messaggio non inviato data la presenza di dispositivi sconosciuti", "Message not sent due to unknown devices being present": "Messaggio non inviato data la presenza di dispositivi sconosciuti",
@ -1179,5 +1174,12 @@
"Terms and Conditions": "Termini e condizioni", "Terms and Conditions": "Termini e condizioni",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Per continuare a usare l'homeserver %(homeserverDomain)s devi leggere e accettare i nostri termini e condizioni.", "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Per continuare a usare l'homeserver %(homeserverDomain)s devi leggere e accettare i nostri termini e condizioni.",
"Review terms and conditions": "Leggi i termini e condizioni", "Review terms and conditions": "Leggi i termini e condizioni",
"Muted Users": "Utenti silenziati" "Muted Users": "Utenti silenziati",
"Message Replies": "Risposte",
"Message Pinning": "Messaggi appuntati",
"Mirror local video feed": "Feed video dai ripetitori locali",
"Replying": "Rispondere",
"Popout widget": "Oggetto a comparsa",
"Failed to indicate account erasure": "Impossibile indicare la cancellazione dell'account",
"Bulk Options": "Opzioni applicate in massa"
} }

View file

@ -169,7 +169,6 @@
"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 mute user": "사용자의 알림을 끄지 못했어요", "Failed to mute user": "사용자의 알림을 끄지 못했어요",
"Failed to reject invite": "초대를 거절하지 못했어요", "Failed to reject invite": "초대를 거절하지 못했어요",
"Failed to reject invitation": "초대를 거절하지 못했어요", "Failed to reject invitation": "초대를 거절하지 못했어요",
@ -257,7 +256,6 @@
"Mobile phone number": "휴대 전화번호", "Mobile phone number": "휴대 전화번호",
"Mobile phone number (optional)": "휴대 전화번호 (선택)", "Mobile phone number (optional)": "휴대 전화번호 (선택)",
"Moderator": "조정자", "Moderator": "조정자",
"Must be viewing a room": "방을 둘러봐야만 해요",
"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 from this device": "이 장치에서 이 방의 인증받지 않은 장치로 암호화한 메시지를 보내지 마세요", "Never send encrypted messages to unverified devices in this room from this device": "이 장치에서 이 방의 인증받지 않은 장치로 암호화한 메시지를 보내지 마세요",

View file

@ -153,7 +153,6 @@
"Failed to kick": "Neizdevās izspert/padzīt (kick)", "Failed to kick": "Neizdevās izspert/padzīt (kick)",
"Failed to leave room": "Neizdevās pamest istabu", "Failed to leave room": "Neizdevās pamest istabu",
"Failed to load timeline position": "Neizdevās ielādēt laikpaziņojumu pozīciju", "Failed to load timeline position": "Neizdevās ielādēt laikpaziņojumu pozīciju",
"Failed to lookup current room": "Neizdevās uziet pašreizējo istabu",
"Failed to mute user": "Neizdevās apklusināt lietotāju", "Failed to mute user": "Neizdevās apklusināt lietotāju",
"Failed to reject invite": "Neizdevās noraidīt uzaicinājumu", "Failed to reject invite": "Neizdevās noraidīt uzaicinājumu",
"Failed to reject invitation": "Neizdevās noraidīt uzaicinājumu", "Failed to reject invitation": "Neizdevās noraidīt uzaicinājumu",
@ -244,7 +243,6 @@
"Mobile phone number": "Mobilā telefona numurs", "Mobile phone number": "Mobilā telefona numurs",
"Mobile phone number (optional)": "Mobilā telefona numurs (nav obligāts)", "Mobile phone number (optional)": "Mobilā telefona numurs (nav obligāts)",
"Moderator": "Moderators", "Moderator": "Moderators",
"Must be viewing a room": "Jāapskata istaba",
"Mute": "Noklusināt (izslēgt skaņu)", "Mute": "Noklusināt (izslēgt skaņu)",
"%(serverName)s Matrix ID": "%(serverName)s Matrix Id", "%(serverName)s Matrix ID": "%(serverName)s Matrix Id",
"Name": "Vārds", "Name": "Vārds",
@ -719,9 +717,7 @@
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s vidžets, kuru mainīja %(senderName)s", "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s vidžets, kuru mainīja %(senderName)s",
"%(names)s and %(count)s others are typing|other": "%(names)s un %(count)s citi raksta", "%(names)s and %(count)s others are typing|other": "%(names)s un %(count)s citi raksta",
"%(names)s and %(count)s others are typing|one": "%(names)s un vēl kāds raksta", "%(names)s and %(count)s others are typing|one": "%(names)s un vēl kāds raksta",
"Message Replies": "Atbildes uz ziņām",
"Message Pinning": "Ziņu piekabināšana", "Message Pinning": "Ziņu piekabināšana",
"Tag Panel": "Birku panelis",
"Disable Emoji suggestions while typing": "Atspējot Emoji ieteikumus teksta rakstīšanas laikā", "Disable Emoji suggestions while typing": "Atspējot Emoji ieteikumus teksta rakstīšanas laikā",
"Hide avatar changes": "Slēpt avatara izmaiņas", "Hide avatar changes": "Slēpt avatara izmaiņas",
"Hide display name changes": "Slēpt attēlojamā/redzamā vārda izmaiņas", "Hide display name changes": "Slēpt attēlojamā/redzamā vārda izmaiņas",
@ -972,8 +968,6 @@
"Did you know: you can use communities to filter your Riot.im experience!": "Vai zināji: Tu vari izmantot kopienas, lai filtrētu (atlasītu) savu Riot.im pieredzi!", "Did you know: you can use communities to filter your Riot.im experience!": "Vai zināji: Tu vari izmantot kopienas, lai filtrētu (atlasītu) savu Riot.im pieredzi!",
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Lai uzstādītu filtru, uzvelc kopienas avataru uz filtru paneļa ekrāna kreisajā malā. Lai redzētu tikai istabas un cilvēkus, kas saistīti ar šo kopienu, Tu vari klikšķināt uz avatara filtru panelī jebkurā brīdī.", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Lai uzstādītu filtru, uzvelc kopienas avataru uz filtru paneļa ekrāna kreisajā malā. Lai redzētu tikai istabas un cilvēkus, kas saistīti ar šo kopienu, Tu vari klikšķināt uz avatara filtru panelī jebkurā brīdī.",
"Create a new community": "Izveidot jaunu kopienu", "Create a new community": "Izveidot jaunu kopienu",
"Join an existing community": "Pievienoties esošai kopienai",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Lai pievienotos esošai kopienai Tev jāzina tā ID; tas izskatīties piemēram šādi <i>+paraugs:matrix.org</i>.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "Tagad<resendText>visas atkārtoti sūtīt</resendText> vai <cancelText>visas atcelt</cancelText>. Tu vari atzīmēt arī individuālas ziņas, kuras atkārtoti sūtīt vai atcelt.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "Tagad<resendText>visas atkārtoti sūtīt</resendText> vai <cancelText>visas atcelt</cancelText>. Tu vari atzīmēt arī individuālas ziņas, kuras atkārtoti sūtīt vai atcelt.",
"Clear filter": "Attīrīt filtru", "Clear filter": "Attīrīt filtru",
"Debug Logs Submission": "Iesniegt atutošanas logfailus", "Debug Logs Submission": "Iesniegt atutošanas logfailus",

View file

@ -64,7 +64,7 @@
"Active call (%(roomName)s)": "Actief gesprek (%(roomName)s)", "Active call (%(roomName)s)": "Actief gesprek (%(roomName)s)",
"Add": "Toevoegen", "Add": "Toevoegen",
"Add a topic": "Een onderwerp toevoegen", "Add a topic": "Een onderwerp toevoegen",
"Admin Tools": "Beheerhulpmiddelen", "Admin Tools": "Beheerdershulpmiddelen",
"VoIP": "VoiP", "VoIP": "VoiP",
"Missing Media Permissions, click here to request.": "Ontbrekende mediatoestemmingen, klik hier om aan te vragen.", "Missing Media Permissions, click here to request.": "Ontbrekende mediatoestemmingen, klik hier om aan te vragen.",
"No Microphones detected": "Geen microfoons gevonden", "No Microphones detected": "Geen microfoons gevonden",
@ -101,7 +101,6 @@
"OK": "OK", "OK": "OK",
"Failed to change password. Is your password correct?": "Wachtwoord wijzigen mislukt. Is uw wachtwoord juist?", "Failed to change password. Is your password correct?": "Wachtwoord wijzigen mislukt. Is uw wachtwoord juist?",
"Moderator": "Moderator", "Moderator": "Moderator",
"Must be viewing a room": "Moet een ruimte weergeven",
"%(serverName)s Matrix ID": "%(serverName)s Matrix-ID", "%(serverName)s Matrix ID": "%(serverName)s Matrix-ID",
"Name": "Naam", "Name": "Naam",
"New password": "Nieuw wachtwoord", "New password": "Nieuw wachtwoord",
@ -238,7 +237,6 @@
"Failed to join room": "Niet gelukt om tot de ruimte toe te treden", "Failed to join room": "Niet gelukt om tot de ruimte toe te treden",
"Failed to leave room": "Niet gelukt om de ruimte te verlaten", "Failed to leave room": "Niet gelukt om de ruimte te verlaten",
"Failed to load timeline position": "Niet gelukt om de tijdlijnpositie te laden", "Failed to load timeline position": "Niet gelukt om de tijdlijnpositie te laden",
"Failed to lookup current room": "Niet gelukt om de huidige ruimte op te zoeken",
"Failed to mute user": "Niet gelukt om de gebruiker te dempen", "Failed to mute user": "Niet gelukt om de gebruiker te dempen",
"Failed to reject invite": "Niet gelukt om de uitnodiging te weigeren", "Failed to reject invite": "Niet gelukt om de uitnodiging te weigeren",
"Failed to reject invitation": "Niet gelukt om de uitnodiging te weigeren", "Failed to reject invitation": "Niet gelukt om de uitnodiging te weigeren",
@ -409,7 +407,7 @@
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Het is niet gelukt om een specifiek punt in de tijdlijn van deze ruimte te laden.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Het is niet gelukt om een specifiek punt in de tijdlijn van deze ruimte te laden.",
"Turn Markdown off": "Zet Markdown uit", "Turn Markdown off": "Zet Markdown uit",
"Turn Markdown on": "Zet Markdown aan", "Turn Markdown on": "Zet Markdown aan",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s heeft eind-tot-eind versleuteling aangezet (algoritme %(algorithm)s).", "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s heeft end-to-endbeveiliging aangezet (algoritme %(algorithm)s).",
"Unable to add email address": "Niet mogelijk om e-mailadres toe te voegen", "Unable to add email address": "Niet mogelijk om e-mailadres toe te voegen",
"Unable to remove contact information": "Niet mogelijk om contactinformatie te verwijderen", "Unable to remove contact information": "Niet mogelijk om contactinformatie te verwijderen",
"Unable to verify email address.": "Niet mogelijk om het e-mailadres te verifiëren.", "Unable to verify email address.": "Niet mogelijk om het e-mailadres te verifiëren.",
@ -580,7 +578,7 @@
"Add User": "Gebruiker Toevoegen", "Add User": "Gebruiker Toevoegen",
"This Home Server would like to make sure you are not a robot": "Deze thuisserver wil er zeker van zijn dat je geen robot bent", "This Home Server would like to make sure you are not a robot": "Deze thuisserver wil er zeker van zijn dat je geen robot bent",
"Sign in with CAS": "Inloggen met CAS", "Sign in with CAS": "Inloggen met CAS",
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Je kan de aangepaste server opties gebruiken om bij andere Matrix-servers in te loggen door een andere thuisserver-URL te specificeren.", "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Je kan de alternatieve-serverinstellingen gebruiken om bij andere Matrix-servers in te loggen door een andere thuisserver-URL te specificeren.",
"This allows you to use this app with an existing Matrix account on a different home server.": "Dit maakt het mogelijk om deze applicatie te gebruiken met een bestaand Matrix-account op een andere thuisserver.", "This allows you to use this app with an existing Matrix account on a different home server.": "Dit maakt het mogelijk om deze applicatie te gebruiken met een bestaand Matrix-account op een andere thuisserver.",
"You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Je kan ook een aangepaste identiteitsserver instellen maar dit zal waarschijnlijk interactie met gebruikers gebaseerd op een e-mailadres voorkomen.", "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Je kan ook een aangepaste identiteitsserver instellen maar dit zal waarschijnlijk interactie met gebruikers gebaseerd op een e-mailadres voorkomen.",
"Please check your email to continue registration.": "Bekijk je e-mail om door te gaan met de registratie.", "Please check your email to continue registration.": "Bekijk je e-mail om door te gaan met de registratie.",
@ -589,7 +587,7 @@
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Als je geen e-mailadres specificeert zal je niet je wachtwoord kunnen resetten. Weet je het zeker?", "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Als je geen e-mailadres specificeert zal je niet je wachtwoord kunnen resetten. Weet je het zeker?",
"You are registering with %(SelectedTeamName)s": "Je registreert je met %(SelectedTeamName)s", "You are registering with %(SelectedTeamName)s": "Je registreert je met %(SelectedTeamName)s",
"Default server": "Standaardserver", "Default server": "Standaardserver",
"Custom server": "Aangepaste server", "Custom server": "Alternatieve server",
"Home server URL": "Thuisserver-URL", "Home server URL": "Thuisserver-URL",
"Identity server URL": "Identiteitsserver-URL", "Identity server URL": "Identiteitsserver-URL",
"What does this mean?": "Wat betekent dit?", "What does this mean?": "Wat betekent dit?",
@ -602,7 +600,7 @@
"URL Previews": "URL-Voorvertoningen", "URL Previews": "URL-Voorvertoningen",
"Drop file here to upload": "Bestand hier laten vallen om te uploaden", "Drop file here to upload": "Bestand hier laten vallen om te uploaden",
" (unsupported)": " (niet ondersteund)", " (unsupported)": " (niet ondersteund)",
"Ongoing conference call%(supportedText)s.": "Lopend vergaderingsgesprek %(supportedText)s.", "Ongoing conference call%(supportedText)s.": "Lopend groepsgesprek%(supportedText)s.",
"Online": "Online", "Online": "Online",
"Idle": "Afwezig", "Idle": "Afwezig",
"Offline": "Offline", "Offline": "Offline",
@ -707,8 +705,6 @@
"%(names)s and %(count)s others are typing|one": "%(names)s en iemand anders is aan het typen", "%(names)s and %(count)s others are typing|one": "%(names)s en iemand anders is aan het typen",
"Send": "Verstuur", "Send": "Verstuur",
"Message Pinning": "Boodschap vastpinnen", "Message Pinning": "Boodschap vastpinnen",
"Message Replies": "Antwoorden op bericht",
"Tag Panel": "Label Paneel",
"Disable Emoji suggestions while typing": "Emoji suggesties tijdens het typen uitzetten", "Disable Emoji suggestions while typing": "Emoji suggesties tijdens het typen uitzetten",
"Hide avatar changes": "Avatar veranderingen verbergen", "Hide avatar changes": "Avatar veranderingen verbergen",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
@ -758,7 +754,7 @@
"World readable": "Leesbaar voor iedereen", "World readable": "Leesbaar voor iedereen",
"Guests can join": "Gasten kunnen toetreden", "Guests can join": "Gasten kunnen toetreden",
"Remove avatar": "Avatar verwijderen", "Remove avatar": "Avatar verwijderen",
"To change the room's avatar, you must be a": "Om de avatar van de ruimte te verwijderen, moet het volgende zijn:", "To change the room's avatar, you must be a": "Om de avatar van de ruimte te verwijderen, moet je het volgende zijn:",
"Drop here to favourite": "Hier laten vallen om aan favorieten toe te voegen", "Drop here to favourite": "Hier laten vallen om aan favorieten toe te voegen",
"Drop here to tag direct chat": "Hier laten vallen om als privégesprek te markeren", "Drop here to tag direct chat": "Hier laten vallen om als privégesprek te markeren",
"Drop here to restore": "Hier laten vallen om te herstellen", "Drop here to restore": "Hier laten vallen om te herstellen",
@ -922,13 +918,11 @@
"This Home server does not support communities": "Deze Thuisserver ondersteunt geen gemeenschappen", "This Home server does not support communities": "Deze Thuisserver ondersteunt geen gemeenschappen",
"Failed to load %(groupId)s": "Het is niet gelukt om %(groupId)s te laden", "Failed to load %(groupId)s": "Het is niet gelukt om %(groupId)s te laden",
"Old cryptography data detected": "Oude cryptografie gegevens gedetecteerd", "Old cryptography data detected": "Oude cryptografie gegevens gedetecteerd",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Er zijn gegevens van een oudere versie van Riot gedetecteerd. Dit zal eind-tot-eind versleuteling laten storen in de oudere versie. Eind-tot-eind berichten dat recent zijn uitgewisseld zal misschien niet ontsleutelbaar zijn in deze versie. Dit zou er misschien ook voor kunnen zorgen dat berichten die zijn uitgewisseld in deze versie falen. Indien je problemen ervaart, log opnieuw in. Om de berichtgeschiedenis te behouden, exporteer de sleutels en importeer ze achteraf weer.", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Er zijn gegevens van een oudere versie van Riot gedetecteerd. Dit verstoorde end-to-endbeveiliging in de oude versie. End-to-endbeveiligde berichten die recent uitgewisseld zijn met de oude versie zijn wellicht niet te ontsleutelen in deze versie. Dit zou er ook voor kunnen zorgen dat berichten die zijn uitgewisseld in deze versie falen. Log opnieuw in als je problemen ervaart. Exporteer de sleutels en importeer ze achteraf weer om de berichtgeschiedenis te behouden.",
"Your Communities": "Jouw Gemeenschappen", "Your Communities": "Jouw Gemeenschappen",
"Error whilst fetching joined communities": "Er is een fout opgetreden tijdens het ophalen van de gemeenschappen waar je lid van bent", "Error whilst fetching joined communities": "Er is een fout opgetreden tijdens het ophalen van de gemeenschappen waar je lid van bent",
"Create a new community": "Maak een nieuwe gemeenschap aan", "Create a new community": "Maak een nieuwe gemeenschap aan",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Maak een gemeenschap aan om gebruikers en ruimtes samen te groeperen! Bouw een aangepaste homepagina om je eigen plek in het Matrix universum te maken.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Maak een gemeenschap aan om gebruikers en ruimtes samen te groeperen! Bouw een aangepaste homepagina om je eigen plek in het Matrix universum te maken.",
"Join an existing community": "Treed tot een bestaande gemeenschap toe",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Je moet het gemeenschaps-ID weten om tot de gemeenschap toe te treden; dit zal er uitzien zoals <i>+voorbeeld:matrix.org</i>.",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Toon apparaten</showDevicesText>, <sendAnywayText>Toch versturen</sendAnywayText> of <cancelText>annuleren</cancelText>.", "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Toon apparaten</showDevicesText>, <sendAnywayText>Toch versturen</sendAnywayText> of <cancelText>annuleren</cancelText>.",
"%(count)s of your messages have not been sent.|one": "Je bericht was niet verstuurd.", "%(count)s of your messages have not been sent.|one": "Je bericht was niet verstuurd.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "Nu alles <resendText>opnieuw versturen</resendText> of <cancelText>annuleren</cancelText>. Je kan ook individuele berichten selecteren om opnieuw te versturen of te annuleren.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "Nu alles <resendText>opnieuw versturen</resendText> of <cancelText>annuleren</cancelText>. Je kan ook individuele berichten selecteren om opnieuw te versturen of te annuleren.",
@ -1164,7 +1158,7 @@
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Help Riot.im te verbeteren door het versturen van <UsageDataLink>anonieme gebruiksgegevens</UsageDataLink>. Dit zal een cookie gebruiken.", "Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Help Riot.im te verbeteren door het versturen van <UsageDataLink>anonieme gebruiksgegevens</UsageDataLink>. Dit zal een cookie gebruiken.",
"Yes, I want to help!": "Ja, ik wil helpen!", "Yes, I want to help!": "Ja, ik wil helpen!",
"Warning: This widget might use cookies.": "Waarschuwing: deze widget gebruikt misschien cookies.", "Warning: This widget might use cookies.": "Waarschuwing: deze widget gebruikt misschien cookies.",
"Popout widget": "Opspringende widget", "Popout widget": "Widget in nieuw venster openen",
"Picture": "Afbeelding", "Picture": "Afbeelding",
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Niet mogelijk om de gebeurtenis te laden waar op gereageerd was. Het kan zijn dat het niet bestaat of dat je niet toestemming hebt om het te bekijken.", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Niet mogelijk om de gebeurtenis te laden waar op gereageerd was. Het kan zijn dat het niet bestaat of dat je niet toestemming hebt om het te bekijken.",
"Riot bugs are tracked on GitHub: <a>create a GitHub issue</a>.": "Riot fouten worden bijgehouden op GitHub: <a>maak een GitHub melding</a>.", "Riot bugs are tracked on GitHub: <a>create a GitHub issue</a>.": "Riot fouten worden bijgehouden op GitHub: <a>maak een GitHub melding</a>.",

View file

@ -232,7 +232,6 @@
"Failed to kick": "Nie udało się wykopać użytkownika", "Failed to kick": "Nie udało się wykopać użytkownika",
"Failed to leave room": "Nie udało się opuścić pokoju", "Failed to leave room": "Nie udało się opuścić pokoju",
"Failed to load timeline position": "Nie udało się wczytać pozycji osi czasu", "Failed to load timeline position": "Nie udało się wczytać pozycji osi czasu",
"Failed to lookup current room": "Nie udało się wyszukać aktualnego pokoju",
"Failed to mute user": "Nie udało się wyciszyć użytkownika", "Failed to mute user": "Nie udało się wyciszyć użytkownika",
"Failed to reject invite": "Nie udało się odrzucić zaproszenia", "Failed to reject invite": "Nie udało się odrzucić zaproszenia",
"Failed to reject invitation": "Nie udało się odrzucić zaproszenia", "Failed to reject invitation": "Nie udało się odrzucić zaproszenia",
@ -434,7 +433,6 @@
"The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Podany klucz podpisu odpowiada kluczowi podpisania otrzymanemu z urządzenia %(userId)s %(deviceId)s. Urządzenie oznaczone jako zweryfikowane.", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Podany klucz podpisu odpowiada kluczowi podpisania otrzymanemu z urządzenia %(userId)s %(deviceId)s. Urządzenie oznaczone jako zweryfikowane.",
"This email address is already in use": "Podany adres e-mail jest już w użyciu", "This email address is already in use": "Podany adres e-mail jest już w użyciu",
"This email address was not found": "Podany adres e-mail nie został znaleziony", "This email address was not found": "Podany adres e-mail nie został znaleziony",
"Must be viewing a room": "Musi być w trakcie wyświetlania pokoju",
"The email address linked to your account must be entered.": "Musisz wpisać adres e-mail połączony z twoim kontem.", "The email address linked to your account must be entered.": "Musisz wpisać adres e-mail połączony z twoim kontem.",
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "Rozmiar pliku '%(fileName)s' przekracza możliwy limit do przesłania na serwer domowy", "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Rozmiar pliku '%(fileName)s' przekracza możliwy limit do przesłania na serwer domowy",
"The file '%(fileName)s' failed to upload": "Przesyłanie pliku '%(fileName)s' nie powiodło się", "The file '%(fileName)s' failed to upload": "Przesyłanie pliku '%(fileName)s' nie powiodło się",
@ -920,5 +918,42 @@
"Collapse panel": "Ukryj panel", "Collapse panel": "Ukryj panel",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Z Twoją obecną przeglądarką, wygląd oraz wrażenia z używania aplikacji mogą być niepoprawne, a niektóre funkcje wcale nie działać. Kontynuuj jeśli chcesz spróbować, jednak trudno będzie pomóc w przypadku błędów, które mogą nastąpić!", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Z Twoją obecną przeglądarką, wygląd oraz wrażenia z używania aplikacji mogą być niepoprawne, a niektóre funkcje wcale nie działać. Kontynuuj jeśli chcesz spróbować, jednak trudno będzie pomóc w przypadku błędów, które mogą nastąpić!",
"Checking for an update...": "Sprawdzanie aktualizacji...", "Checking for an update...": "Sprawdzanie aktualizacji...",
"There are advanced notifications which are not shown here": "Masz zaawansowane powiadomienia, nie pokazane tutaj" "There are advanced notifications which are not shown here": "Masz zaawansowane powiadomienia, nie pokazane tutaj",
"e.g. %(exampleValue)s": "np. %(exampleValue)s",
"Always show encryption icons": "Zawsze wyświetlaj ikony szyfrowania",
"Send analytics data": "Wysyłaj dane analityczne",
"%(duration)ss": "%(duration)ss",
"%(duration)sm": "%(duration)sm",
"%(duration)sh": "%(duration)sg",
"%(duration)sd": "%(duration)sd",
"%(user)s is a %(userRole)s": "%(user)s ma rolę %(userRole)s",
"Members only (since the point in time of selecting this option)": "Tylko członkowie (od momentu włączenia tej opcji)",
"Members only (since they were invited)": "Tylko członkowie (od kiedy zostali zaproszeni)",
"Members only (since they joined)": "Tylko członkowie (od kiedy dołączyli)",
"Copied!": "Skopiowano!",
"Failed to copy": "Kopiowanie nieudane",
"Message removed by %(userId)s": "Wiadomość usunięta przez %(userId)s",
"Message removed": "Wiadomość usunięta",
"An email has been sent to %(emailAddress)s": "Email został wysłany do %(emailAddress)s",
"A text message has been sent to %(msisdn)s": "Wysłano wiadomość tekstową do %(msisdn)s",
"Code": "Kod",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Pomóż nam ulepszyć Riot.im wysyłając <UsageDataLink>anonimowe dane analityczne</ UsageDataLink>. Spowoduje to użycie pliku cookie (zobacz naszą <PolicyLink>Politykę plików cookie</ PolicyLink>).",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Pomóż nam ulepszyć Riot.im wysyłając <UsageDataLink>anonimowe dane analityczne</ UsageDataLink>. Spowoduje to użycie pliku cookie.",
"Yes, I want to help!": "Tak, chcę pomóc!",
"Warning: This widget might use cookies.": "Uwaga: Ten widżet może używać ciasteczek.",
"Delete Widget": "Usuń widżet",
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Usunięcie widżetu usuwa go dla wszystkich użytkowników w tym pokoju. Czy na pewno chcesz usunąć ten widżet?",
"Communities": "Społeczności",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"collapse": "Zwiń",
"expand": "Rozwiń",
"Custom of %(powerLevel)s": "Poziom niestandardowy %(powerLevel)s",
"<a>In reply to</a> <pill>": "<a>W odpowiedzi do</a> <pill>",
"Matrix ID": "Matrix ID",
"email address": "adres e-mail",
"example": "przykład",
"Advanced options": "Opcje zaawansowane",
"To continue, please enter your password:": "Aby kontynuować, proszę wprowadzić swoje hasło:",
"password": "hasło",
"Refresh": "Odśwież"
} }

View file

@ -216,7 +216,6 @@
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s", "Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
"%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.", "%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
"Existing Call": "Chamada em andamento", "Existing Call": "Chamada em andamento",
"Failed to lookup current room": "Não foi possível buscar na sala atual",
"Failed to send email": "Falha ao enviar email", "Failed to send email": "Falha ao enviar email",
"Failed to send request.": "Não foi possível mandar requisição.", "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 set up conference call": "Não foi possível montar a chamada de conferência",
@ -235,7 +234,6 @@
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s deixou o histórico futuro da sala visível para desconhecido (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s deixou o histórico futuro da sala visível para desconhecido (%(visibility)s).",
"Missing room_id in request": "Faltou o id da sala na requisição", "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", "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",
"(not supported by this browser)": "(não é compatível com este navegador)", "(not supported by this browser)": "(não é compatível com este navegador)",
"%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.", "%(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.", "Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",

View file

@ -216,7 +216,6 @@
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s", "Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
"%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.", "%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
"Existing Call": "Chamada em andamento", "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 email": "Não foi possível enviar email",
"Failed to send request.": "Não foi possível mandar requisição.", "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 set up conference call": "Não foi possível montar a chamada de conferência",
@ -235,7 +234,6 @@
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s deixou o histórico futuro da sala visível para desconhecido (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s deixou o histórico futuro da sala visível para desconhecido (%(visibility)s).",
"Missing room_id in request": "Faltou o id da sala na requisição", "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", "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",
"(not supported by this browser)": "(não é compatível com este navegador)", "(not supported by this browser)": "(não é compatível com este navegador)",
"%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.", "%(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.", "Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",
@ -683,9 +681,7 @@
"%(names)s and %(count)s others are typing|other": "%(names)s e %(count)s outras pessoas estão escrevendo", "%(names)s and %(count)s others are typing|other": "%(names)s e %(count)s outras pessoas estão escrevendo",
"%(names)s and %(count)s others are typing|one": "%(names)s e uma outra pessoa estão escrevendo", "%(names)s and %(count)s others are typing|one": "%(names)s e uma outra pessoa estão escrevendo",
"Send": "Enviar", "Send": "Enviar",
"Message Replies": "Respostas",
"Message Pinning": "Fixar mensagem", "Message Pinning": "Fixar mensagem",
"Tag Panel": "Painel de tags",
"Disable Emoji suggestions while typing": "Desativar sugestões de emojis enquanto estiver escrevendo", "Disable Emoji suggestions while typing": "Desativar sugestões de emojis enquanto estiver escrevendo",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Ocultar mensagens de entrada e de saída (não afeta convites, expulsões e banimentos)", "Hide join/leave messages (invites/kicks/bans unaffected)": "Ocultar mensagens de entrada e de saída (não afeta convites, expulsões e banimentos)",
"Hide avatar changes": "Ocultar alterações da imagem de perfil", "Hide avatar changes": "Ocultar alterações da imagem de perfil",
@ -932,8 +928,6 @@
"Error whilst fetching joined communities": "Erro baixando comunidades das quais você faz parte", "Error whilst fetching joined communities": "Erro baixando comunidades das quais você faz parte",
"Create a new community": "Criar nova comunidade", "Create a new community": "Criar nova comunidade",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crie uma comunidade para agrupar em um mesmo local pessoas e salas! Monte uma página inicial personalizada para dar uma identidade ao seu espaço no universo Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Crie uma comunidade para agrupar em um mesmo local pessoas e salas! Monte uma página inicial personalizada para dar uma identidade ao seu espaço no universo Matrix.",
"Join an existing community": "Entrar numa comunidade existente",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Para entrar em uma comunidade, você terá que conhecer o seu ID; um ID de comunidade normalmente tem este formato: <i>+exemplo:matrix.org</i>.",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Exibir dispositivos</showDevicesText>, <sendAnywayText>enviar assim mesmo</sendAnywayText> ou <cancelText>cancelar</cancelText>.", "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Exibir dispositivos</showDevicesText>, <sendAnywayText>enviar assim mesmo</sendAnywayText> ou <cancelText>cancelar</cancelText>.",
"%(count)s of your messages have not been sent.|one": "Sua mensagem não foi enviada.", "%(count)s of your messages have not been sent.|one": "Sua mensagem não foi enviada.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Reenviar todas</resendText> ou <cancelText>cancelar todas</cancelText> agora. Você também pode selecionar mensagens individualmente a serem reenviadas ou canceladas.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Reenviar todas</resendText> ou <cancelText>cancelar todas</cancelText> agora. Você também pode selecionar mensagens individualmente a serem reenviadas ou canceladas.",

View file

@ -155,7 +155,6 @@
"Drop here to tag %(section)s": "Перетащите сюда, чтобы пометить как %(section)s", "Drop here to tag %(section)s": "Перетащите сюда, чтобы пометить как %(section)s",
"%(senderName)s ended the call.": "%(senderName)s завершил(а) звонок.", "%(senderName)s ended the call.": "%(senderName)s завершил(а) звонок.",
"Existing Call": "Текущий вызов", "Existing Call": "Текущий вызов",
"Failed to lookup current room": "Не удалось найти текущую комнату",
"Failed to send request.": "Не удалось отправить запрос.", "Failed to send request.": "Не удалось отправить запрос.",
"Failed to set up conference call": "Не удалось сделать конференц-звонок", "Failed to set up conference call": "Не удалось сделать конференц-звонок",
"Failed to verify email address: make sure you clicked the link in the email": "Не удалось проверить email: убедитесь, что вы перешли по ссылке в письме", "Failed to verify email address: make sure you clicked the link in the email": "Не удалось проверить email: убедитесь, что вы перешли по ссылке в письме",
@ -174,7 +173,6 @@
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s сделал(а) историю комнаты видимой в неизвестном режиме (%(visibility)s).", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s сделал(а) историю комнаты видимой в неизвестном режиме (%(visibility)s).",
"Missing room_id in request": "Отсутствует room_id в запросе", "Missing room_id in request": "Отсутствует room_id в запросе",
"Missing user_id in request": "Отсутствует user_id в запросе", "Missing user_id in request": "Отсутствует user_id в запросе",
"Must be viewing a room": "Вы должны просматривать комнату",
"(not supported by this browser)": "(не поддерживается этим браузером)", "(not supported by this browser)": "(не поддерживается этим браузером)",
"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.": "Отправленные сообщения будут сохранены, пока соединение не восстановится.",
@ -787,8 +785,6 @@
"Error whilst fetching joined communities": "Ошибка при загрузке сообществ", "Error whilst fetching joined communities": "Ошибка при загрузке сообществ",
"Create a new community": "Создать новое сообщество", "Create a new community": "Создать новое сообщество",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Создайте сообщество для объединения пользователей и комнат! Создайте собственную домашнюю страницу, чтобы выделить свое пространство во вселенной Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Создайте сообщество для объединения пользователей и комнат! Создайте собственную домашнюю страницу, чтобы выделить свое пространство во вселенной Matrix.",
"Join an existing community": "Присоединиться к существующему сообществу",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Чтобы присоединиться к существующему сообществу, вам нужно знать его ID; это будет выглядеть примерно так<i>+primer:matrix.org</i>.",
"Something went wrong whilst creating your community": "При создании сообщества что-то пошло не так", "Something went wrong whilst creating your community": "При создании сообщества что-то пошло не так",
"%(names)s and %(count)s others are typing|other": "%(names)s и еще %(count)s печатают", "%(names)s and %(count)s others are typing|other": "%(names)s и еще %(count)s печатают",
"And %(count)s more...|other": "Еще %(count)s…", "And %(count)s more...|other": "Еще %(count)s…",
@ -908,7 +904,6 @@
"Unknown for %(duration)s": "Неизвестно %(duration)s", "Unknown for %(duration)s": "Неизвестно %(duration)s",
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Здесь никого нет! Хотите <inviteText>пригласить кого-нибудь</inviteText> или <nowarnText>выключить предупреждение о пустой комнате</nowarnText>?", "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Здесь никого нет! Хотите <inviteText>пригласить кого-нибудь</inviteText> или <nowarnText>выключить предупреждение о пустой комнате</nowarnText>?",
"Something went wrong when trying to get your communities.": "Что-то пошло не так при попытке получить список ваших сообществ.", "Something went wrong when trying to get your communities.": "Что-то пошло не так при попытке получить список ваших сообществ.",
"Tag Panel": "Панель тегов",
"Delete %(count)s devices|other": "Удалить (%(count)s)", "Delete %(count)s devices|other": "Удалить (%(count)s)",
"Delete %(count)s devices|one": "Удалить", "Delete %(count)s devices|one": "Удалить",
"Select devices": "Выбрать", "Select devices": "Выбрать",
@ -937,7 +932,6 @@
"%(count)s of your messages have not been sent.|one": "Ваше сообщение не было отправлено.", "%(count)s of your messages have not been sent.|one": "Ваше сообщение не было отправлено.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Отправить все</resendText> или <cancelText>отменить все</cancelText> сейчас. Можно также выбрать отдельные сообщения для отправки или отмены.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Отправить все</resendText> или <cancelText>отменить все</cancelText> сейчас. Можно также выбрать отдельные сообщения для отправки или отмены.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Отправить</resendText> или <cancelText>отменить</cancelText> сообщение сейчас.", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Отправить</resendText> или <cancelText>отменить</cancelText> сообщение сейчас.",
"Message Replies": "Сообщения-ответы",
"Send an encrypted reply…": "Отправить зашифрованный ответ…", "Send an encrypted reply…": "Отправить зашифрованный ответ…",
"Send a reply (unencrypted)…": "Отправить ответ (нешифрованный)…", "Send a reply (unencrypted)…": "Отправить ответ (нешифрованный)…",
"Send an encrypted message…": "Отправить зашифрованное сообщение…", "Send an encrypted message…": "Отправить зашифрованное сообщение…",
@ -1183,6 +1177,19 @@
"Yes, I want to help!": "Да, я хочу помочь!", "Yes, I want to help!": "Да, я хочу помочь!",
"Reload widget": "Перезагрузить виджет", "Reload widget": "Перезагрузить виджет",
"To notify everyone in the room, you must be a": "Для уведомления всех в комнате, вы должны быть", "To notify everyone in the room, you must be a": "Для уведомления всех в комнате, вы должны быть",
"Can't leave Server Notices room": "Невозможно покинуть комнату для сервера по заметкам", "Can't leave Server Notices room": "Невозможно покинуть комнату сервера уведомлений",
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Эта комната для важных сообщений от сервера, потому ее не возможно покинуть." "This room is used for important messages from the Homeserver, so you cannot leave it.": "Эта комната используется для важных сообщений от сервера, поэтому вы не можете ее покинуть.",
"Try the app first": "Сначала попробуйте приложение",
"Encrypting": "Шифрование",
"Encrypted, not sent": "Зашифровано, не отправлено",
"No Audio Outputs detected": "Аудиовыход не обнаружен",
"Audio Output": "Аудиовыход",
"Share Link to User": "Поделиться ссылкой с пользователем",
"Share room": "Поделиться комнатой",
"Share Room": "Поделиться комнатой",
"Link to most recent message": "Ссылка на последнее сообщение",
"Share User": "Поделиться пользователем",
"Share Community": "Поделиться сообществом",
"Link to selected message": "Ссылка на выбранное сообщение",
"COPY": "КОПИРОВАТЬ"
} }

View file

@ -84,10 +84,8 @@
"You are not in this room.": "Nenachádzate sa v tejto miestnosti.", "You are not in this room.": "Nenachádzate sa v tejto miestnosti.",
"You do not have permission to do that in this room.": "V tejto miestnosti nemáte oprávnenie na vykonanie takejto akcie.", "You do not have permission to do that in this room.": "V tejto miestnosti nemáte oprávnenie na vykonanie takejto akcie.",
"Missing room_id in request": "V požiadavke chýba room_id", "Missing room_id in request": "V požiadavke chýba room_id",
"Must be viewing a room": "Musí byť zobrazená miestnosť",
"Room %(roomId)s not visible": "Miestnosť %(roomId)s nie je viditeľná", "Room %(roomId)s not visible": "Miestnosť %(roomId)s nie je viditeľná",
"Missing user_id in request": "V požiadavke chýba user_id", "Missing user_id in request": "V požiadavke chýba user_id",
"Failed to lookup current room": "Nepodarilo sa vyhľadať aktuálnu miestnosť",
"Usage": "Použitie", "Usage": "Použitie",
"/ddg is not a command": "/ddg nie je žiaden príkaz", "/ddg is not a command": "/ddg nie je žiaden príkaz",
"To use it, just wait for autocomplete results to load and tab through them.": "Ak to chcete použiť, len počkajte na načítanie výsledkov automatického dopĺňania a cyklicky prechádzajte stláčaním klávesu tab..", "To use it, just wait for autocomplete results to load and tab through them.": "Ak to chcete použiť, len počkajte na načítanie výsledkov automatického dopĺňania a cyklicky prechádzajte stláčaním klávesu tab..",
@ -691,8 +689,6 @@
"Error whilst fetching joined communities": "Pri získavaní vašich komunít sa vyskytla chyba", "Error whilst fetching joined communities": "Pri získavaní vašich komunít sa vyskytla chyba",
"Create a new community": "Vytvoriť novú komunitu", "Create a new community": "Vytvoriť novú komunitu",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Vytvorte si komunitu s cieľom zoskupiť miestnosti a používateľov! Zostavte si vlastnú domovskú stránku a vymedzte tak svoj priestor vo svete Matrix.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Vytvorte si komunitu s cieľom zoskupiť miestnosti a používateľov! Zostavte si vlastnú domovskú stránku a vymedzte tak svoj priestor vo svete Matrix.",
"Join an existing community": "Vstúpiť do existujúcej komunity",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Aby ste mohli vstúpiť do existujúcej komunity, musíte poznať jej identifikátor; Mal by vizerať nejako takto <i>+priklad:matrix.org</i>.",
"You have no visible notifications": "Nie sú k dispozícii žiadne oznámenia", "You have no visible notifications": "Nie sú k dispozícii žiadne oznámenia",
"Scroll to bottom of page": "Posunúť na spodok stránky", "Scroll to bottom of page": "Posunúť na spodok stránky",
"Connectivity to the server has been lost.": "Spojenie so serverom bolo prerušené.", "Connectivity to the server has been lost.": "Spojenie so serverom bolo prerušené.",
@ -907,7 +903,6 @@
"Call": "Hovor", "Call": "Hovor",
"Answer": "Prijať", "Answer": "Prijať",
"Send": "Odoslať", "Send": "Odoslať",
"Tag Panel": "Panel so značkami",
"Delete %(count)s devices|other": "Vymazať %(count)s zariadení", "Delete %(count)s devices|other": "Vymazať %(count)s zariadení",
"Delete %(count)s devices|one": "Vymazať zariadenie", "Delete %(count)s devices|one": "Vymazať zariadenie",
"Select devices": "Vybrať zariadenia", "Select devices": "Vybrať zariadenia",
@ -934,7 +929,6 @@
"Flair will not appear": "Príslušnosť ku komunite nebude zobrazená", "Flair will not appear": "Príslušnosť ku komunite nebude zobrazená",
"Display your community flair in rooms configured to show it.": "Zobrazovať vašu príslušnosť ku komunite v miestnostiach, ktoré sú nastavené na zobrazovanie tejto príslušnosti.", "Display your community flair in rooms configured to show it.": "Zobrazovať vašu príslušnosť ku komunite v miestnostiach, ktoré sú nastavené na zobrazovanie tejto príslušnosti.",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
"Message Replies": "Odpovede na správy",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Túto zmenu nebudete môcť vrátiť späť pretože znižujete vašu vlastnú úroveň moci. Ak ste jediný poverený používateľ v miestnosti, nebudete môcť znovu získať úroveň, akú máte teraz.", "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Túto zmenu nebudete môcť vrátiť späť pretože znižujete vašu vlastnú úroveň moci. Ak ste jediný poverený používateľ v miestnosti, nebudete môcť znovu získať úroveň, akú máte teraz.",
"Send an encrypted reply…": "Odoslať šifrovanú odpoveď…", "Send an encrypted reply…": "Odoslať šifrovanú odpoveď…",
"Send a reply (unencrypted)…": "Odoslať odpoveď (nešifrovanú)…", "Send a reply (unencrypted)…": "Odoslať odpoveď (nešifrovanú)…",

View file

@ -106,9 +106,7 @@
"Power level must be positive integer.": "Niveli fuqie duhet të jetë numër i plotë pozitiv.", "Power level must be positive integer.": "Niveli fuqie duhet të jetë numër i plotë pozitiv.",
"You are not in this room.": "Ti nuk je në këtë dhomë.", "You are not in this room.": "Ti nuk je në këtë dhomë.",
"You do not have permission to do that in this room.": "Nuk ke leje të bësh këtë në këtë dhomë.", "You do not have permission to do that in this room.": "Nuk ke leje të bësh këtë në këtë dhomë.",
"Must be viewing a room": "Duhet të shikohet një dhomë",
"Room %(roomId)s not visible": "Dhoma %(roomId)s e padukshme", "Room %(roomId)s not visible": "Dhoma %(roomId)s e padukshme",
"Failed to lookup current room": "Dhoma aktuale nuk mundi të kërkohej",
"Usage": "Përdorimi", "Usage": "Përdorimi",
"/ddg is not a command": "/ddg s'është komandë", "/ddg is not a command": "/ddg s'është komandë",
"To use it, just wait for autocomplete results to load and tab through them.": "Për të përdorur, thjesht prit derisa të mbushën rezultatat vetëplotësuese dhe pastaj shfletoji.", "To use it, just wait for autocomplete results to load and tab through them.": "Për të përdorur, thjesht prit derisa të mbushën rezultatat vetëplotësuese dhe pastaj shfletoji.",

View file

@ -85,7 +85,6 @@
"You are not in this room.": "Нисте у овој соби.", "You are not in this room.": "Нисте у овој соби.",
"You do not have permission to do that in this room.": "Немате овлашћење да урадите то у овој соби.", "You do not have permission to do that in this room.": "Немате овлашћење да урадите то у овој соби.",
"Missing room_id in request": "Недостаје room_id у захтеву", "Missing room_id in request": "Недостаје room_id у захтеву",
"Must be viewing a room": "Морате гледати собу",
"Room %(roomId)s not visible": "Соба %(roomId)s није видљива", "Room %(roomId)s not visible": "Соба %(roomId)s није видљива",
"Missing user_id in request": "Недостаје user_id у захтеву", "Missing user_id in request": "Недостаје user_id у захтеву",
"Call Failed": "Позивање неуспешно", "Call Failed": "Позивање неуспешно",
@ -97,7 +96,6 @@
"Answer": "Одговори", "Answer": "Одговори",
"Call Timeout": "Прекорачено време позивања", "Call Timeout": "Прекорачено време позивања",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
"Failed to lookup current room": "Неуспех при потраживању тренутне собе",
"Usage": "Коришћење", "Usage": "Коришћење",
"/ddg is not a command": "/ddg није наредба", "/ddg is not a command": "/ddg није наредба",
"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.": "Да бисте је користили, само сачекајте да се исходи самодовршавања учитају и табом прођите кроз њих.",
@ -170,9 +168,7 @@
"Not a valid Riot keyfile": "Није исправана Riot кључ-датотека", "Not a valid Riot keyfile": "Није исправана Riot кључ-датотека",
"Authentication check failed: incorrect password?": "Провера идентитета није успела: нетачна лозинка?", "Authentication check failed: incorrect password?": "Провера идентитета није успела: нетачна лозинка?",
"Failed to join room": "Нисам успео да уђем у собу", "Failed to join room": "Нисам успео да уђем у собу",
"Message Replies": "Одговори",
"Message Pinning": "Закачене поруке", "Message Pinning": "Закачене поруке",
"Tag Panel": "Означи површ",
"Disable Emoji suggestions while typing": "Онемогући предлагање емоџија приликом куцања", "Disable Emoji suggestions while typing": "Онемогући предлагање емоџија приликом куцања",
"Use compact timeline layout": "Користи збијени распоред временске линије", "Use compact timeline layout": "Користи збијени распоред временске линије",
"Hide removed messages": "Сакриј уклоњене поруке", "Hide removed messages": "Сакриј уклоњене поруке",
@ -778,8 +774,6 @@
"Error whilst fetching joined communities": "Грешка приликом добављања списка са приступљеним заједницама", "Error whilst fetching joined communities": "Грешка приликом добављања списка са приступљеним заједницама",
"Create a new community": "Направи нову заједницу", "Create a new community": "Направи нову заједницу",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Направите заједницу да бисте спојили кориснике и собе! Направите прилагођену почетну страницу да бисте означили ваш кутак у Матрикс универзуму.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Направите заједницу да бисте спојили кориснике и собе! Направите прилагођену почетну страницу да бисте означили ваш кутак у Матрикс универзуму.",
"Join an existing community": "Приступи већ постојећој заједници",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "Да бисте приступили већ постојећој заједници, морате знати њен идентификатор заједнице. Ово изгледа нешто као <i>+primer:matrix.org</i>.",
"You have no visible notifications": "Немате видљивих обавештења", "You have no visible notifications": "Немате видљивих обавештења",
"Scroll to bottom of page": "Превуци на дно странице", "Scroll to bottom of page": "Превуци на дно странице",
"Message not sent due to unknown devices being present": "Порука се неће послати због присутности непознатих уређаја", "Message not sent due to unknown devices being present": "Порука се неће послати због присутности непознатих уређаја",
@ -1159,5 +1153,29 @@
"Clear Storage and Sign Out": "Очисти складиште и одјави ме", "Clear Storage and Sign Out": "Очисти складиште и одјави ме",
"Refresh": "Освежи", "Refresh": "Освежи",
"We encountered an error trying to restore your previous session.": "Наишли смо на грешку приликом повраћаја ваше претходне сесије.", "We encountered an error trying to restore your previous session.": "Наишли смо на грешку приликом повраћаја ваше претходне сесије.",
"Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Чишћење складишта вашег прегледача може решити проблем али ће вас то одјавити и учинити шифровани историјат ћаскања нечитљивим." "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Чишћење складишта вашег прегледача може решити проблем али ће вас то одјавити и учинити шифровани историјат ћаскања нечитљивим.",
"e.g. %(exampleValue)s": "нпр.: %(exampleValue)s",
"Reload widget": "Поново учитај виџет",
"Send analytics data": "Пошаљи аналитичке податке",
"Enable widget screenshots on supported widgets": "Омогући снимке екрана виџета у подржаним виџетима",
"At this time it is not possible to reply with a file so this will be sent without being a reply.": "У овом тренутку није могуће одговорити са датотеком тако да ово неће бити послато у облику одговора.",
"Unable to reply": "Не могу да одговорим",
"At this time it is not possible to reply with an emote.": "У овом тренутку није могуће одговорити са емотиконом.",
"To notify everyone in the room, you must be a": "Да бисте обавестили све у соби, морате бити",
"Muted Users": "Утишани корисници",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Помозите побољшавање Riot.im програма тако што ћете послати <UsageDataLink>анонимне податке о коришћењу</UsageDataLink>. Ово ће захтевати коришћење колачића (погледајте нашу <PolicyLink>политику о колачићима</PolicyLink>).",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Помозите побољшавање Riot.im програма тако што ћете послати <UsageDataLink>анонимне податке о коришћењу</UsageDataLink>. Ово ће захтевати коришћење колачића.",
"Yes, I want to help!": "Да, желим помоћи!",
"Warning: This widget might use cookies.": "Упозорење: овај виџет ће можда користити колачиће.",
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Не могу да учитам догађај на који је послат одговор, или не постоји или немате овлашћење да га погледате.",
"Failed to indicate account erasure": "Неуспех при наговештавању да је налог обрисан",
"To continue, please enter your password:": "Да бисте наставили, унесите вашу лозинку:",
"password": "лозинка",
"Collapse Reply Thread": "Скупи нит са одговорима",
"Can't leave Server Notices room": "Не могу да напустим собу са напоменама сервера",
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Ова соба се користи за важне поруке са Кућног сервера, не можете изаћи из ове собе.",
"Terms and Conditions": "Услови коришћења",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Да бисте наставили са коришћењем Кућног сервера %(homeserverDomain)s морате погледати и пристати на наше услове коришћења.",
"Review terms and conditions": "Погледај услове коришћења",
"Try the app first": "Пробајте прво апликацију"
} }

View file

@ -128,7 +128,6 @@
"Failed to kick": "Det gick inte att kicka", "Failed to kick": "Det gick inte att kicka",
"Failed to leave room": "Det gick inte att lämna rummet", "Failed to leave room": "Det gick inte att lämna rummet",
"Failed to load timeline position": "Det gick inte att hämta positionen på tidslinjen", "Failed to load timeline position": "Det gick inte att hämta positionen på tidslinjen",
"Failed to lookup current room": "Det gick inte att hämta det nuvarande rummet",
"Failed to mute user": "Det gick inte att dämpa användaren", "Failed to mute user": "Det gick inte att dämpa användaren",
"Failed to reject invite": "Det gick inte att avböja inbjudan", "Failed to reject invite": "Det gick inte att avböja inbjudan",
"Failed to reject invitation": "Det gick inte att avböja inbjudan", "Failed to reject invitation": "Det gick inte att avböja inbjudan",
@ -149,7 +148,7 @@
"Add": "Lägg till", "Add": "Lägg till",
"Admin Tools": "Admin-verktyg", "Admin Tools": "Admin-verktyg",
"Alias (optional)": "Alias (valfri)", "Alias (optional)": "Alias (valfri)",
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Det gick inte att ansluta till servern - kontrollera anslutningen, försäkra att din <a>hemservers TLS-certifikat</a> är betrott, och att inget webbläsartillägg blockerar förfrågningar.", "Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Det gick inte att ansluta till hemservern - kontrollera anslutningen, se till att <a>hemserverns SSL-certifikat</a> är betrott, och att inget webbläsartillägg blockerar förfrågningar.",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ändrade behörighetsnivå för %(powerLevelDiffText)s.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ändrade behörighetsnivå för %(powerLevelDiffText)s.",
"<a>Click here</a> to join the discussion!": "<a>Klicka här</a> för att gå med i diskussionen!", "<a>Click here</a> to join the discussion!": "<a>Klicka här</a> för att gå med i diskussionen!",
"Close": "Stäng", "Close": "Stäng",
@ -240,7 +239,6 @@
"Mobile phone number": "Telefonnummer", "Mobile phone number": "Telefonnummer",
"Mobile phone number (optional)": "Telefonnummer (valfri)", "Mobile phone number (optional)": "Telefonnummer (valfri)",
"Moderator": "Moderator", "Moderator": "Moderator",
"Must be viewing a room": "Du måste ha ett öppet rum",
"Mute": "Dämpa", "Mute": "Dämpa",
"%(serverName)s Matrix ID": "%(serverName)s Matrix-ID", "%(serverName)s Matrix ID": "%(serverName)s Matrix-ID",
"Name": "Namn", "Name": "Namn",
@ -289,7 +287,7 @@
"Register": "Registrera", "Register": "Registrera",
"%(targetName)s rejected the invitation.": "%(targetName)s avvisade inbjudan.", "%(targetName)s rejected the invitation.": "%(targetName)s avvisade inbjudan.",
"Reject invitation": "Avböj inbjudan", "Reject invitation": "Avböj inbjudan",
"Rejoin": "Gå med tillbaka", "Rejoin": "Gå med igen",
"Remote addresses for this room:": "Fjärradresser för det här rummet:", "Remote addresses for this room:": "Fjärradresser för det här rummet:",
"Remove Contact Information?": "Ta bort kontaktuppgifter?", "Remove Contact Information?": "Ta bort kontaktuppgifter?",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s tog bort sitt visningsnamn (%(oldDisplayName)s).", "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s tog bort sitt visningsnamn (%(oldDisplayName)s).",
@ -325,7 +323,7 @@
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s bjöd in %(targetDisplayName)s med i rummet.", "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s bjöd in %(targetDisplayName)s med i rummet.",
"Server error": "Serverfel", "Server error": "Serverfel",
"Server may be unavailable or overloaded": "Servern kan vara otillgänglig eller överbelastad", "Server may be unavailable or overloaded": "Servern kan vara otillgänglig eller överbelastad",
"Server may be unavailable, overloaded, or search timed out :(": "Servern kan vara otillgänglig, överbelastad, eller så timade sökningen ut :(", "Server may be unavailable, overloaded, or search timed out :(": "Servern kan vara otillgänglig, överbelastad, eller så tog sökningen för lång tid :(",
"Server may be unavailable, overloaded, or the file too big": "Servern kan vara otillgänglig, överbelastad, eller så är filen för stor", "Server may be unavailable, overloaded, or the file too big": "Servern kan vara otillgänglig, överbelastad, eller så är filen för stor",
"Server may be unavailable, overloaded, or you hit a bug.": "Servern kan vara otillgänglig, överbelastad, eller så stötte du på en bugg.", "Server may be unavailable, overloaded, or you hit a bug.": "Servern kan vara otillgänglig, överbelastad, eller så stötte du på en bugg.",
"Server unavailable, overloaded, or something else went wrong.": "Servern är otillgänglig, överbelastad, eller så gick något annat fel.", "Server unavailable, overloaded, or something else went wrong.": "Servern är otillgänglig, överbelastad, eller så gick något annat fel.",
@ -346,7 +344,7 @@
"Start Chat": "Starta en chatt", "Start Chat": "Starta en chatt",
"Cancel": "Avbryt", "Cancel": "Avbryt",
"Create new room": "Skapa nytt rum", "Create new room": "Skapa nytt rum",
"Custom Server Options": "Egna serverinställningar", "Custom Server Options": "Anpassade serverinställningar",
"Dismiss": "Avvisa", "Dismiss": "Avvisa",
"powered by Matrix": "drivs av Matrix", "powered by Matrix": "drivs av Matrix",
"Room directory": "Rumskatalog", "Room directory": "Rumskatalog",
@ -379,7 +377,7 @@
"The email address linked to your account must be entered.": "Epostadressen som är kopplad till ditt konto måste anges.", "The email address linked to your account must be entered.": "Epostadressen som är kopplad till ditt konto måste anges.",
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "Filen '%(fileName)s' överskrider serverns största tillåtna filstorlek", "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Filen '%(fileName)s' överskrider serverns största tillåtna filstorlek",
"The file '%(fileName)s' failed to upload": "Filen '%(fileName)s' kunde inte laddas upp", "The file '%(fileName)s' failed to upload": "Filen '%(fileName)s' kunde inte laddas upp",
"Online": "Aktiv", "Online": "Online",
"Unnamed room": "Namnlöst rum", "Unnamed room": "Namnlöst rum",
"World readable": "Alla kan läsa", "World readable": "Alla kan läsa",
"Guests can join": "Gäster kan bli medlem i rummet", "Guests can join": "Gäster kan bli medlem i rummet",
@ -466,7 +464,7 @@
"Room not found": "Rummet hittades inte", "Room not found": "Rummet hittades inte",
"Messages containing my display name": "Meddelanden som innehåller mitt namn", "Messages containing my display name": "Meddelanden som innehåller mitt namn",
"Messages in one-to-one chats": "Meddelanden i privata chattar", "Messages in one-to-one chats": "Meddelanden i privata chattar",
"Unavailable": "Inte tillgänglig", "Unavailable": "Otillgänglig",
"View Decrypted Source": "Visa dekrypterad källa", "View Decrypted Source": "Visa dekrypterad källa",
"Failed to update keywords": "Det gick inte att uppdatera nyckelorden", "Failed to update keywords": "Det gick inte att uppdatera nyckelorden",
"remove %(name)s from the directory.": "ta bort %(name)s från katalogen.", "remove %(name)s from the directory.": "ta bort %(name)s från katalogen.",
@ -502,7 +500,7 @@
"Saturday": "lördag", "Saturday": "lördag",
"I understand the risks and wish to continue": "Jag förstår riskerna och vill fortsätta", "I understand the risks and wish to continue": "Jag förstår riskerna och vill fortsätta",
"Direct Chat": "Direkt-chatt", "Direct Chat": "Direkt-chatt",
"The server may be unavailable or overloaded": "Servern kan vara överbelastad eller inte tillgänglig", "The server may be unavailable or overloaded": "Servern kan vara otillgänglig eller överbelastad",
"Reject": "Avböj", "Reject": "Avböj",
"Failed to set Direct Message status of room": "Det gick inte att ställa in direktmeddelandestatus för rummet", "Failed to set Direct Message status of room": "Det gick inte att ställa in direktmeddelandestatus för rummet",
"Monday": "måndag", "Monday": "måndag",
@ -559,7 +557,7 @@
"View Source": "Visa källa", "View Source": "Visa källa",
"Thank you!": "Tack!", "Thank you!": "Tack!",
"Quote": "Citera", "Quote": "Citera",
"Collapse panel": "Kollapsa panel", "Collapse panel": "Dölj panel",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Med din nuvarande webbläsare kan appens utseende vara helt fel, och vissa eller alla egenskaper kommer nödvändigtvis inte att fungera. Om du ändå vill försöka så kan du fortsätta, men gör det på egen risk!", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Med din nuvarande webbläsare kan appens utseende vara helt fel, och vissa eller alla egenskaper kommer nödvändigtvis inte att fungera. Om du ändå vill försöka så kan du fortsätta, men gör det på egen risk!",
"Checking for an update...": "Letar efter uppdateringar...", "Checking for an update...": "Letar efter uppdateringar...",
"There are advanced notifications which are not shown here": "Det finns avancerade aviseringar som inte visas här", "There are advanced notifications which are not shown here": "Det finns avancerade aviseringar som inte visas här",
@ -636,7 +634,7 @@
"%(duration)sm": "%(duration)sm", "%(duration)sm": "%(duration)sm",
"%(duration)sh": "%(duration)sh", "%(duration)sh": "%(duration)sh",
"%(duration)sd": "%(duration)sd", "%(duration)sd": "%(duration)sd",
"Online for %(duration)s": "Aktiv i %(duration)s", "Online for %(duration)s": "Online i %(duration)s",
"Idle for %(duration)s": "Inaktiv i %(duration)s", "Idle for %(duration)s": "Inaktiv i %(duration)s",
"Offline for %(duration)s": "Offline i %(duration)s", "Offline for %(duration)s": "Offline i %(duration)s",
"Idle": "Inaktiv", "Idle": "Inaktiv",
@ -852,7 +850,7 @@
"Drop here to demote": "Släpp här för att göra till låg prioritet", "Drop here to demote": "Släpp här för att göra till låg prioritet",
"You're not in any rooms yet! Press <CreateRoomButton> to make a room or <RoomDirectoryButton> to browse the directory": "Du är inte i något rum ännu! Tryck <CreateRoomButton> för att skapa ett rum eller <RoomDirectoryButton> för att bläddra i katalogen", "You're not in any rooms yet! Press <CreateRoomButton> to make a room or <RoomDirectoryButton> to browse the directory": "Du är inte i något rum ännu! Tryck <CreateRoomButton> för att skapa ett rum eller <RoomDirectoryButton> för att bläddra i katalogen",
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Vill du <acceptText>acceptera</acceptText> eller <declineText>avböja</declineText> denna inbjudan?", "Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Vill du <acceptText>acceptera</acceptText> eller <declineText>avböja</declineText> denna inbjudan?",
"You have been invited to join this room by %(inviterName)s": "Du har blivit inbjuden att gå med i rummet av %(inviterName)s", "You have been invited to join this room by %(inviterName)s": "Du har blivit inbjuden till rummet av %(inviterName)s",
"Kick this user?": "Kicka användaren?", "Kick this user?": "Kicka användaren?",
"To send messages, you must be a": "För att skicka meddelanden, måste du vara", "To send messages, you must be a": "För att skicka meddelanden, måste du vara",
"To invite users into the room, you must be a": "För att bjuda in användare i rummet, måste du vara", "To invite users into the room, you must be a": "För att bjuda in användare i rummet, måste du vara",
@ -1021,7 +1019,6 @@
"Add rooms to the community": "Lägg till rum i communityn", "Add rooms to the community": "Lägg till rum i communityn",
"Add to community": "Lägg till i community", "Add to community": "Lägg till i community",
"Failed to invite users to community": "Det gick inte att bjuda in användare till communityn", "Failed to invite users to community": "Det gick inte att bjuda in användare till communityn",
"Message Replies": "Meddelandesvar",
"Mirror local video feed": "Spegelvänd lokal video", "Mirror local video feed": "Spegelvänd lokal video",
"Disable Community Filter Panel": "Inaktivera community-filterpanel", "Disable Community Filter Panel": "Inaktivera community-filterpanel",
"Community Invites": "Community-inbjudningar", "Community Invites": "Community-inbjudningar",
@ -1052,7 +1049,7 @@
"Leave Community": "Lämna community", "Leave Community": "Lämna community",
"Unable to leave community": "Det gick inte att lämna community", "Unable to leave community": "Det gick inte att lämna community",
"Community Settings": "Community-inställningar", "Community Settings": "Community-inställningar",
"Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Ändringar på <bold1>namn</bold1> och <bold2>avatar</bold2> som gjorts i din community kommer eventuellt inte synas för andra användare i upp till 30 minuter.", "Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Det kan dröja upp till 30 minuter innan ändringar på communityns <bold1>namn</bold1> och <bold2>avatar</bold2> blir synliga för andra användare.",
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Dessa rum visas för community-medlemmar på community-sidan. Community-medlemmar kan gå med i rummen genom att klicka på dem.", "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Dessa rum visas för community-medlemmar på community-sidan. Community-medlemmar kan gå med i rummen genom att klicka på dem.",
"Add rooms to this community": "Lägg till rum i denna community", "Add rooms to this community": "Lägg till rum i denna community",
"%(inviter)s has invited you to join this community": "%(inviter)s har bjudit in dig till denna community", "%(inviter)s has invited you to join this community": "%(inviter)s har bjudit in dig till denna community",
@ -1066,8 +1063,6 @@
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "För att skapa ett filter, dra en community-avatar till filterpanelen längst till vänster på skärmen. Du kan när som helst klicka på en avatar i filterpanelen för att bara se rum och personer som är associerade med den communityn.", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "För att skapa ett filter, dra en community-avatar till filterpanelen längst till vänster på skärmen. Du kan när som helst klicka på en avatar i filterpanelen för att bara se rum och personer som är associerade med den communityn.",
"Create a new community": "Skapa en ny community", "Create a new community": "Skapa en ny community",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Skapa en community för att gruppera användare och rum! Bygg en anpassad hemsida för att markera er plats i Matrix-universumet.", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Skapa en community för att gruppera användare och rum! Bygg en anpassad hemsida för att markera er plats i Matrix-universumet.",
"Join an existing community": "Gå med i en befintlig community",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "För att gå med i en befintlig gemenskap behöver du ha community-ID; det ser ut som något i stil med <i>+exempel:matrix.org</i>.",
"Invite to this community": "Bjud in till denna community", "Invite to this community": "Bjud in till denna community",
"Something went wrong when trying to get your communities.": "Något gick fel vid hämtning av dina communityn.", "Something went wrong when trying to get your communities.": "Något gick fel vid hämtning av dina communityn.",
"You're not currently a member of any communities.": "Du är för närvarande inte medlem i någon community.", "You're not currently a member of any communities.": "Du är för närvarande inte medlem i någon community.",
@ -1180,7 +1175,6 @@
"The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Den exporterade filen kommer att låta någon som kan läsa den att dekryptera alla krypterade meddelanden som du kan se, så du bör vara noga med att hålla den säker. För att hjälpa till med detta, bör du ange en lösenfras nedan, som kommer att användas för att kryptera exporterad data. Det kommer bara vara möjligt att importera data genom att använda samma lösenfras.", "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Den exporterade filen kommer att låta någon som kan läsa den att dekryptera alla krypterade meddelanden som du kan se, så du bör vara noga med att hålla den säker. För att hjälpa till med detta, bör du ange en lösenfras nedan, som kommer att användas för att kryptera exporterad data. Det kommer bara vara möjligt att importera data genom att använda samma lösenfras.",
"This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Denna process möjliggör import av krypteringsnycklar som tidigare exporterats från en annan Matrix-klient. Du kommer då kunna dekryptera alla meddelanden som den andra klienten kunde dekryptera.", "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Denna process möjliggör import av krypteringsnycklar som tidigare exporterats från en annan Matrix-klient. Du kommer då kunna dekryptera alla meddelanden som den andra klienten kunde dekryptera.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Den exporterade filen kommer vara skyddad med en lösenfras. Du måste ange lösenfrasen här, för att dekryptera filen.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Den exporterade filen kommer vara skyddad med en lösenfras. Du måste ange lösenfrasen här, för att dekryptera filen.",
"Tag Panel": "Tagg-panel",
"Flair": "Emblem", "Flair": "Emblem",
"Showing flair for these communities:": "Visar emblem för dessa communityn:", "Showing flair for these communities:": "Visar emblem för dessa communityn:",
"This room is not showing flair for any communities": "Detta rum visar inte emblem för några communityn", "This room is not showing flair for any communities": "Detta rum visar inte emblem för några communityn",

View file

@ -132,7 +132,6 @@
"Failed to join room": "การเข้าร่วมห้องล้มเหลว", "Failed to join room": "การเข้าร่วมห้องล้มเหลว",
"Failed to kick": "การเตะล้มเหลว", "Failed to kick": "การเตะล้มเหลว",
"Failed to leave room": "การออกจากห้องล้มเหลว", "Failed to leave room": "การออกจากห้องล้มเหลว",
"Failed to lookup current room": "การหาห้องปัจจุบันล้มเหลว",
"Failed to reject invite": "การปฏิเสธคำเชิญล้มเหลว", "Failed to reject invite": "การปฏิเสธคำเชิญล้มเหลว",
"Failed to reject invitation": "การปฏิเสธคำเชิญล้มเหลว", "Failed to reject invitation": "การปฏิเสธคำเชิญล้มเหลว",
"Failed to save settings": "การบันทึกการตั้งค่าล้มเหลว", "Failed to save settings": "การบันทึกการตั้งค่าล้มเหลว",

View file

@ -153,7 +153,6 @@
"Failed to kick": "Atma(Kick) işlemi başarısız oldu", "Failed to kick": "Atma(Kick) işlemi başarısız oldu",
"Failed to leave room": "Odadan ayrılma başarısız oldu", "Failed to leave room": "Odadan ayrılma başarısız oldu",
"Failed to load timeline position": "Zaman çizelgesi konumu yüklenemedi", "Failed to load timeline position": "Zaman çizelgesi konumu yüklenemedi",
"Failed to lookup current room": "Geçerli odayı aramak başarısız oldu",
"Failed to mute user": "Kullanıcıyı sessize almak başarısız oldu", "Failed to mute user": "Kullanıcıyı sessize almak başarısız oldu",
"Failed to reject invite": "Daveti reddetme başarısız oldu", "Failed to reject invite": "Daveti reddetme başarısız oldu",
"Failed to reject invitation": "Davetiyeyi reddetme başarısız oldu", "Failed to reject invitation": "Davetiyeyi reddetme başarısız oldu",
@ -241,7 +240,6 @@
"Mobile phone number": "Cep telefonu numarası", "Mobile phone number": "Cep telefonu numarası",
"Mobile phone number (optional)": "Cep telefonu numarası (isteğe bağlı)", "Mobile phone number (optional)": "Cep telefonu numarası (isteğe bağlı)",
"Moderator": "Moderatör", "Moderator": "Moderatör",
"Must be viewing a room": "Bir oda görüntülemeli olmalı",
"Mute": "Sessiz", "Mute": "Sessiz",
"Name": "İsim", "Name": "İsim",
"Never send encrypted messages to unverified devices from this device": "Bu cihazdan doğrulanmamış cihazlara asla şifrelenmiş mesajlar göndermeyin", "Never send encrypted messages to unverified devices from this device": "Bu cihazdan doğrulanmamış cihazlara asla şifrelenmiş mesajlar göndermeyin",

View file

@ -94,7 +94,7 @@
"Register": "Зарегіструватись", "Register": "Зарегіструватись",
"Rooms": "Кімнати", "Rooms": "Кімнати",
"Add rooms to this community": "Добавити кімнати в це суспільство", "Add rooms to this community": "Добавити кімнати в це суспільство",
"This email address is already in use": "Ця адреса елект. почти вже використовується", "This email address is already in use": "Ця е-пошта вже використовується",
"This phone number is already in use": "Цей телефонний номер вже використовується", "This phone number is already in use": "Цей телефонний номер вже використовується",
"Fetching third party location failed": "Не вдалось отримати стороннє місцеперебування", "Fetching third party location failed": "Не вдалось отримати стороннє місцеперебування",
"Messages in one-to-one chats": "Повідомлення у чатах \"сам на сам\"", "Messages in one-to-one chats": "Повідомлення у чатах \"сам на сам\"",
@ -269,5 +269,15 @@
"Your language of choice": "Обрана мова", "Your language of choice": "Обрана мова",
"Which officially provided instance you are using, if any": "Яким офіційно наданим примірником ви користуєтесь (якщо користуєтесь)", "Which officially provided instance you are using, if any": "Яким офіційно наданим примірником ви користуєтесь (якщо користуєтесь)",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим Richtext у редакторі Rich Text Editor", "Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим Richtext у редакторі Rich Text Editor",
"Your homeserver's URL": "URL адреса вашого домашнього серверу" "Your homeserver's URL": "URL адреса вашого домашнього серверу",
"Failed to verify email address: make sure you clicked the link in the email": "Не вдалось перевірити адресу е-пошти: переконайтесь, що ви перейшли за посиланням у листі",
"The platform you're on": "Використовувана платформа",
"Your identity server's URL": "URL адреса серверу ідентифікації",
"e.g. %(exampleValue)s": "напр. %(exampleValue)s",
"Every page you use in the app": "Кожна використовувана у застосунку сторінка",
"e.g. <CurrentPageURL>": "напр. <CurrentPageURL>",
"Your User Agent": "Ваш користувацький агент",
"Your device resolution": "Роздільність вашого пристрою",
"Analytics": "Аналітика",
"The information being sent to us to help make Riot.im better includes:": "Надсилана інформація, що допомагає нам покращити Riot.im, вміщує:"
} }

View file

@ -42,7 +42,6 @@
"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 mute user": "禁言用户失败", "Failed to mute user": "禁言用户失败",
"Failed to reject invite": "拒绝邀请失败", "Failed to reject invite": "拒绝邀请失败",
"Failed to reject invitation": "拒绝邀请失败", "Failed to reject invitation": "拒绝邀请失败",
@ -742,7 +741,6 @@
"Add rooms to the community": "添加聊天室到社区", "Add rooms to the community": "添加聊天室到社区",
"Add to community": "添加到社区", "Add to community": "添加到社区",
"Failed to invite users to community": "邀请用户到社区失败", "Failed to invite users to community": "邀请用户到社区失败",
"Message Replies": "消息回复",
"Disable Peer-to-Peer for 1:1 calls": "在一对一通话中禁用 P2P 对等网络", "Disable Peer-to-Peer for 1:1 calls": "在一对一通话中禁用 P2P 对等网络",
"Enable inline URL previews by default": "默认启用网址预览", "Enable inline URL previews by default": "默认启用网址预览",
"Disinvite this user?": "取消邀请此用户?", "Disinvite this user?": "取消邀请此用户?",
@ -934,7 +932,6 @@
"Create a new community": "创建新社区", "Create a new community": "创建新社区",
"Error whilst fetching joined communities": "获取已加入社区列表时出现错误", "Error whilst fetching joined communities": "获取已加入社区列表时出现错误",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "创建社区,将用户与聊天室整合在一起!搭建自定义社区主页以在 Matrix 宇宙之中标记出您的私人空间。", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "创建社区,将用户与聊天室整合在一起!搭建自定义社区主页以在 Matrix 宇宙之中标记出您的私人空间。",
"Join an existing community": "加入已有的社区",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>显示未信任的设备</showDevicesText> 、 <sendAnywayText>不经信任直接发送</sendAnywayText> 或 <cancelText>取消发送</cancelText>。", "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>显示未信任的设备</showDevicesText> 、 <sendAnywayText>不经信任直接发送</sendAnywayText> 或 <cancelText>取消发送</cancelText>。",
"%(count)s of your messages have not been sent.|one": "您的消息尚未发送。", "%(count)s of your messages have not been sent.|one": "您的消息尚未发送。",
"Uploading %(filename)s and %(count)s others|other": "正在上传 %(filename)s 与其他 %(count)s 个文件", "Uploading %(filename)s and %(count)s others|other": "正在上传 %(filename)s 与其他 %(count)s 个文件",
@ -961,7 +958,6 @@
"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.": "尝试加载此房间的时间线的特定时间点,但是无法找到。",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "现在 <resendText>重新发送消息</resendText> 或 <cancelText>取消发送</cancelText> 。", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "现在 <resendText>重新发送消息</resendText> 或 <cancelText>取消发送</cancelText> 。",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "現在 <resendText>重新发送消息</resendText> 或 <cancelText>取消发送</cancelText> 。你也可以单独选择消息以重新发送或取消。", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "現在 <resendText>重新发送消息</resendText> 或 <cancelText>取消发送</cancelText> 。你也可以单独选择消息以重新发送或取消。",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "要加入已有的社区,你需要知道它的社区链接,比如 <i>+example:matrix.org</i>。",
"Visibility in Room List": "是否在聊天室目录中可见", "Visibility in Room List": "是否在聊天室目录中可见",
"Something went wrong when trying to get your communities.": "获取你加入的社区时发生错误。", "Something went wrong when trying to get your communities.": "获取你加入的社区时发生错误。",
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "删除小部件后,此聊天室中的所有用户的这个小部件都会被删除。你确定要删除这个小部件吗?", "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "删除小部件后,此聊天室中的所有用户的这个小部件都会被删除。你确定要删除这个小部件吗?",
@ -1120,14 +1116,12 @@
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "这里没有其他人了!你是想 <inviteText>邀请用户</inviteText> 还是 <nowarnText>不再提示</nowarnText>", "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "这里没有其他人了!你是想 <inviteText>邀请用户</inviteText> 还是 <nowarnText>不再提示</nowarnText>",
"You need to be able to invite users to do that.": "你需要有邀请用户的权限才能进行此操作。", "You need to be able to invite users to do that.": "你需要有邀请用户的权限才能进行此操作。",
"Missing roomId.": "找不到此聊天室 ID 所对应的聊天室。", "Missing roomId.": "找不到此聊天室 ID 所对应的聊天室。",
"Tag Panel": "标签面板",
"You have been banned from %(roomName)s by %(userName)s.": "您已被 %(userName)s 从聊天室 %(roomName)s 中封禁。", "You have been banned from %(roomName)s by %(userName)s.": "您已被 %(userName)s 从聊天室 %(roomName)s 中封禁。",
"You have been banned from this room by %(userName)s.": "您已被 %(userName)s 从此聊天室中封禁。", "You have been banned from this room by %(userName)s.": "您已被 %(userName)s 从此聊天室中封禁。",
"Every page you use in the app": "您在 Riot 中使用的每一个页面", "Every page you use in the app": "您在 Riot 中使用的每一个页面",
"e.g. <CurrentPageURL>": "例如:<CurrentPageURL>", "e.g. <CurrentPageURL>": "例如:<CurrentPageURL>",
"Your User Agent": "您的 User Agent", "Your User Agent": "您的 User Agent",
"Your device resolution": "您设备的分辨率", "Your device resolution": "您设备的分辨率",
"Must be viewing a room": "必须是在查看一个聊天室时",
"Always show encryption icons": "总是显示加密标志", "Always show encryption icons": "总是显示加密标志",
"At this time it is not possible to reply with a file so this will be sent without being a reply.": "目前无法以文件作为回复的内容,所以此文件将不作为回复,独立发送。", "At this time it is not possible to reply with a file so this will be sent without being a reply.": "目前无法以文件作为回复的内容,所以此文件将不作为回复,独立发送。",
"Unable to reply": "无法回复", "Unable to reply": "无法回复",

View file

@ -78,7 +78,6 @@
"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 mute user": "禁言用戶失敗", "Failed to mute user": "禁言用戶失敗",
"Failed to reject invite": "拒絕邀請失敗", "Failed to reject invite": "拒絕邀請失敗",
"Failed to reject invitation": "拒絕邀請失敗", "Failed to reject invitation": "拒絕邀請失敗",
@ -151,7 +150,7 @@
"Server unavailable, overloaded, or something else went wrong.": "伺服器可能不可用、超載,或者其他東西出錯了.", "Server unavailable, overloaded, or something else went wrong.": "伺服器可能不可用、超載,或者其他東西出錯了.",
"Session ID": "會話 ID", "Session ID": "會話 ID",
"%(senderName)s set a profile picture.": "%(senderName)s 設置了頭像。.", "%(senderName)s set a profile picture.": "%(senderName)s 設置了頭像。.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s 將暱稱改為了 %(displayName)s。.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s 將他的暱稱改成 %(displayName)s。.",
"Settings": "設定", "Settings": "設定",
"Show panel": "顯示側邊欄", "Show panel": "顯示側邊欄",
"Show timestamps in 12 hour format (e.g. 2:30pm)": "用12小時制顯示時間戳 (如:下午 2:30", "Show timestamps in 12 hour format (e.g. 2:30pm)": "用12小時制顯示時間戳 (如:下午 2:30",
@ -192,11 +191,11 @@
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s 更改了聊天室 %(roomName)s 圖像", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s 更改了聊天室 %(roomName)s 圖像",
"Cancel": "取消", "Cancel": "取消",
"Custom Server Options": "自訂伺服器選項", "Custom Server Options": "自訂伺服器選項",
"Dismiss": "無視", "Dismiss": "關閉",
"Mute": "靜音", "Mute": "靜音",
"Notifications": "通知", "Notifications": "通知",
"Operation failed": "操作失敗", "Operation failed": "操作失敗",
"powered by Matrix": "由 Matrix 架設", "powered by Matrix": "由 Matrix 提供",
"Remove": "移除", "Remove": "移除",
"unknown error code": "未知的錯誤代碼", "unknown error code": "未知的錯誤代碼",
"OK": "確定", "OK": "確定",
@ -249,7 +248,7 @@
"Are you sure you want to leave the room '%(roomName)s'?": "您確定您要想要離開房間 '%(roomName)s' 嗎?", "Are you sure you want to leave the room '%(roomName)s'?": "您確定您要想要離開房間 '%(roomName)s' 嗎?",
"Bans user with given id": "禁止有指定 ID 的使用者", "Bans user with given id": "禁止有指定 ID 的使用者",
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "無法連線到家伺服器 - 請檢查您的連線,確保您的<a>家伺服器的 SSL 憑證</a>可被信任,而瀏覽器擴充套件也沒有阻擋請求。", "Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "無法連線到家伺服器 - 請檢查您的連線,確保您的<a>家伺服器的 SSL 憑證</a>可被信任,而瀏覽器擴充套件也沒有阻擋請求。",
"%(senderName)s changed their profile picture.": "%(senderName)s 已經變更了他的基本資料圖片。", "%(senderName)s changed their profile picture.": "%(senderName)s 已經變更了他的基本資料圖片。",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s 變更了 %(powerLevelDiffText)s 權限等級。", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s 變更了 %(powerLevelDiffText)s 權限等級。",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s 將房間名稱變更為 %(roomName)s。", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s 將房間名稱變更為 %(roomName)s。",
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s 已經移除了房間名稱。", "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s 已經移除了房間名稱。",
@ -336,7 +335,6 @@
"Mobile phone number": "行動電話號碼", "Mobile phone number": "行動電話號碼",
"Mobile phone number (optional)": "行動電話號碼(選擇性)", "Mobile phone number (optional)": "行動電話號碼(選擇性)",
"Moderator": "仲裁者", "Moderator": "仲裁者",
"Must be viewing a room": "必須檢視房間",
"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 from this device": "從不在此房間中從此裝置上傳送未加密的訊息到未驗證的裝置", "Never send encrypted messages to unverified devices in this room from this device": "從不在此房間中從此裝置上傳送未加密的訊息到未驗證的裝置",
@ -380,8 +378,8 @@
"Rejoin": "重新加入", "Rejoin": "重新加入",
"Remote addresses for this room:": "此房間的遠端地址:", "Remote addresses for this room:": "此房間的遠端地址:",
"Remove Contact Information?": "移除聯絡人資訊?", "Remove Contact Information?": "移除聯絡人資訊?",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s 移除了他的顯示名稱 (%(oldDisplayName)s)。", "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s 移除了他的顯示名稱 (%(oldDisplayName)s)。",
"%(senderName)s removed their profile picture.": "%(senderName)s 移除了他們的基本資寮圖片。", "%(senderName)s removed their profile picture.": "%(senderName)s 移除了他的基本資料圖片。",
"Remove %(threePid)s?": "移除 %(threePid)s", "Remove %(threePid)s?": "移除 %(threePid)s",
"%(senderName)s requested a VoIP conference.": "%(senderName)s 請求了一次 VoIP 會議。", "%(senderName)s requested a VoIP conference.": "%(senderName)s 請求了一次 VoIP 會議。",
"Results from DuckDuckGo": "DuckDuckGo 的結果", "Results from DuckDuckGo": "DuckDuckGo 的結果",
@ -702,9 +700,7 @@
"%(names)s and %(count)s others are typing|other": "%(names)s 與其他 %(count)s 個人正在輸入", "%(names)s and %(count)s others are typing|other": "%(names)s 與其他 %(count)s 個人正在輸入",
"%(names)s and %(count)s others are typing|one": "%(names)s 與另一個人正在輸入", "%(names)s and %(count)s others are typing|one": "%(names)s 與另一個人正在輸入",
"Send": "傳送", "Send": "傳送",
"Message Replies": "訊息回覆",
"Message Pinning": "訊息釘選", "Message Pinning": "訊息釘選",
"Tag Panel": "標籤面板",
"Disable Emoji suggestions while typing": "在輸入時停用繪文字建議", "Disable Emoji suggestions while typing": "在輸入時停用繪文字建議",
"Hide avatar changes": "隱藏大頭貼變更", "Hide avatar changes": "隱藏大頭貼變更",
"Hide display name changes": "隱藏顯示名稱變更", "Hide display name changes": "隱藏顯示名稱變更",
@ -925,8 +921,6 @@
"Error whilst fetching joined communities": "擷取已加入的社群時發生錯誤", "Error whilst fetching joined communities": "擷取已加入的社群時發生錯誤",
"Create a new community": "建立新社群", "Create a new community": "建立新社群",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "建立社群以將使用者與聊天室湊成一組!建立自訂的首頁以在 Matrix 宇宙中標出您的空間。", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "建立社群以將使用者與聊天室湊成一組!建立自訂的首頁以在 Matrix 宇宙中標出您的空間。",
"Join an existing community": "加入既有的社群",
"To join an existing community you'll have to know its community identifier; this will look something like <i>+example:matrix.org</i>.": "要加入既有的社群,您必須知道它的社群標記符號;其看起來像是 <i>+example:matrix.org</i>.",
"%(count)s of your messages have not been sent.|one": "您的訊息尚未傳送。", "%(count)s of your messages have not been sent.|one": "您的訊息尚未傳送。",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "現在<resendText>重新傳送全部</resendText>或<cancelText>取消全部</cancelText>。您也可以選取單一訊息以重新傳送或取消。", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "現在<resendText>重新傳送全部</resendText>或<cancelText>取消全部</cancelText>。您也可以選取單一訊息以重新傳送或取消。",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "現在<resendText>重新傳送訊息</resendText>或<cancelText>取消訊息</cancelText>。", "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "現在<resendText>重新傳送訊息</resendText>或<cancelText>取消訊息</cancelText>。",
@ -961,7 +955,7 @@
"Community IDs cannot not be empty.": "社群 ID 不能為空。", "Community IDs cannot not be empty.": "社群 ID 不能為空。",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>顯示裝置</showDevicesText>、<sendAnywayText>無論如何都要傳送</sendAnywayText>或<cancelText>取消</cancelText>。", "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>顯示裝置</showDevicesText>、<sendAnywayText>無論如何都要傳送</sendAnywayText>或<cancelText>取消</cancelText>。",
"<a>In reply to</a> <pill>": "<a>回覆給</a> <pill>", "<a>In reply to</a> <pill>": "<a>回覆給</a> <pill>",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s 變更了他的顯示名稱為 %(displayName)s 。", "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s 變更了他的顯示名稱為 %(displayName)s 。",
"Failed to set direct chat tag": "設定直接聊天標籤失敗", "Failed to set direct chat tag": "設定直接聊天標籤失敗",
"Failed to remove tag %(tagName)s from room": "從聊天室移除標籤 %(tagName)s 失敗", "Failed to remove tag %(tagName)s from room": "從聊天室移除標籤 %(tagName)s 失敗",
"Failed to add tag %(tagName)s to room": "新增標籤 %(tagName)s 到聊天室失敗", "Failed to add tag %(tagName)s to room": "新增標籤 %(tagName)s 到聊天室失敗",
@ -1186,5 +1180,20 @@
"Terms and Conditions": "條款與細則", "Terms and Conditions": "條款與細則",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "要繼續使用 %(homeserverDomain)s 家伺服器,您必須審閱並同意我們的條款與細則。", "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "要繼續使用 %(homeserverDomain)s 家伺服器,您必須審閱並同意我們的條款與細則。",
"Review terms and conditions": "審閱條款與細則", "Review terms and conditions": "審閱條款與細則",
"To notify everyone in the room, you must be a": "為了通知每個在聊天室裡的人,您必須為" "To notify everyone in the room, you must be a": "為了通知每個在聊天室裡的人,您必須為",
"Encrypting": "正在加密",
"Encrypted, not sent": "已加密,未傳送",
"No Audio Outputs detected": "未偵測到音訊輸出",
"Audio Output": "音訊輸出",
"Try the app first": "先試試看應用程式",
"Share Link to User": "分享連結給使用者",
"Share room": "分享聊天室",
"Share Room": "分享聊天室",
"Link to most recent message": "連結到最近的訊息",
"Share User": "分享使用者",
"Share Community": "分享社群",
"Share Room Message": "分享聊天室訊息",
"Link to selected message": "連結到選定的訊息",
"COPY": "複製",
"Share Message": "分享訊息"
} }

View file

@ -169,11 +169,18 @@ matrixLinkify.VECTOR_URL_PATTERN = "^(?:https?:\/\/)?(?:"
+ "(?:www\\.)?(?:riot|vector)\\.im/(?:app|beta|staging|develop)/" + "(?:www\\.)?(?:riot|vector)\\.im/(?:app|beta|staging|develop)/"
+ ")(#.*)"; + ")(#.*)";
matrixLinkify.MATRIXTO_URL_PATTERN = "^(?:https?:\/\/)?(?:www\\.)?matrix\\.to/#/((#|@|!).*)"; matrixLinkify.MATRIXTO_URL_PATTERN = "^(?:https?:\/\/)?(?:www\\.)?matrix\\.to/#/(([#@!+]).*)";
matrixLinkify.MATRIXTO_MD_LINK_PATTERN = matrixLinkify.MATRIXTO_MD_LINK_PATTERN =
'\\[([^\\]]*)\\]\\((?:https?:\/\/)?(?:www\\.)?matrix\\.to/#/((#|@|!)[^\\)]*)\\)'; '\\[([^\\]]*)\\]\\((?:https?:\/\/)?(?:www\\.)?matrix\\.to/#/([#@!+][^\\)]*)\\)';
matrixLinkify.MATRIXTO_BASE_URL= baseUrl; matrixLinkify.MATRIXTO_BASE_URL= baseUrl;
const matrixToEntityMap = {
'@': '#/user/',
'#': '#/room/',
'!': '#/room/',
'+': '#/group/',
};
matrixLinkify.options = { matrixLinkify.options = {
events: function(href, type) { events: function(href, type) {
switch (type) { switch (type) {
@ -204,24 +211,20 @@ matrixLinkify.options = {
case 'userid': case 'userid':
case 'groupid': case 'groupid':
return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href; return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href;
default: default: {
var m;
// FIXME: horrible duplication with HtmlUtils' transform tags // FIXME: horrible duplication with HtmlUtils' transform tags
m = href.match(matrixLinkify.VECTOR_URL_PATTERN); let m = href.match(matrixLinkify.VECTOR_URL_PATTERN);
if (m) { if (m) {
return m[1]; return m[1];
} }
m = href.match(matrixLinkify.MATRIXTO_URL_PATTERN); m = href.match(matrixLinkify.MATRIXTO_URL_PATTERN);
if (m) { if (m) {
const entity = m[1]; const entity = m[1];
if (entity[0] === '@') { if (matrixToEntityMap[entity[0]]) return matrixToEntityMap[entity[0]] + entity;
return '#/user/' + entity;
} else if (entity[0] === '#' || entity[0] === '!') {
return '#/room/' + entity;
}
} }
return href; return href;
}
} }
}, },

View file

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export const baseUrl = "https://matrix.to"; export const host = "matrix.to";
export const baseUrl = `https://${host}`;
export function makeEventPermalink(roomId, eventId) { export function makeEventPermalink(roomId, eventId) {
return `${baseUrl}/#/${roomId}/${eventId}`; return `${baseUrl}/#/${roomId}/${eventId}`;

View file

@ -77,12 +77,6 @@ export const SETTINGS = {
// // level is always appended to the end. // // level is always appended to the end.
// supportedLevelsAreOrdered: false, // supportedLevelsAreOrdered: false,
// }, // },
"feature_rich_quoting": {
isFeature: true,
displayName: _td("Message Replies"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_pinning": { "feature_pinning": {
isFeature: true, isFeature: true,
displayName: _td("Message Pinning"), displayName: _td("Message Pinning"),

View file

@ -0,0 +1,185 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import expect from 'expect';
import DecryptionFailureTracker from '../src/DecryptionFailureTracker';
import { MatrixEvent } from 'matrix-js-sdk';
function createFailedDecryptionEvent() {
const event = new MatrixEvent({
event_id: "event-id-" + Math.random().toString(16).slice(2),
});
event._setClearData(
event._badEncryptedMessage(":("),
);
return event;
}
describe('DecryptionFailureTracker', function() {
it('tracks a failed decryption', function(done) {
const failedDecryptionEvent = createFailedDecryptionEvent();
let trackedFailure = null;
const tracker = new DecryptionFailureTracker((failure) => {
trackedFailure = failure;
});
tracker.eventDecrypted(failedDecryptionEvent);
// Pretend "now" is Infinity
tracker.checkFailures(Infinity);
// Immediately track the newest failure, if there is one
tracker.trackFailure();
expect(trackedFailure).toNotBe(null, 'should track a failure for an event that failed decryption');
done();
});
it('does not track a failed decryption where the event is subsequently successfully decrypted', (done) => {
const decryptedEvent = createFailedDecryptionEvent();
const tracker = new DecryptionFailureTracker((failure) => {
expect(true).toBe(false, 'should not track an event that has since been decrypted correctly');
});
tracker.eventDecrypted(decryptedEvent);
// Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted
decryptedEvent._setClearData({});
tracker.eventDecrypted(decryptedEvent);
// Pretend "now" is Infinity
tracker.checkFailures(Infinity);
// Immediately track the newest failure, if there is one
tracker.trackFailure();
done();
});
it('only tracks a single failure per event, despite multiple failed decryptions for multiple events', (done) => {
const decryptedEvent = createFailedDecryptionEvent();
const decryptedEvent2 = createFailedDecryptionEvent();
let count = 0;
const tracker = new DecryptionFailureTracker((failure) => count++);
// Arbitrary number of failed decryptions for both events
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent2);
tracker.eventDecrypted(decryptedEvent2);
tracker.eventDecrypted(decryptedEvent2);
// Pretend "now" is Infinity
tracker.checkFailures(Infinity);
// Simulated polling of `trackFailure`, an arbitrary number ( > 2 ) times
tracker.trackFailure();
tracker.trackFailure();
tracker.trackFailure();
tracker.trackFailure();
expect(count).toBe(2, count + ' failures tracked, should only track a single failure per event');
done();
});
it('track failures in the order they occured', (done) => {
const decryptedEvent = createFailedDecryptionEvent();
const decryptedEvent2 = createFailedDecryptionEvent();
const failures = [];
const tracker = new DecryptionFailureTracker((failure) => failures.push(failure));
// Indicate decryption
tracker.eventDecrypted(decryptedEvent);
tracker.eventDecrypted(decryptedEvent2);
// Pretend "now" is Infinity
tracker.checkFailures(Infinity);
// Simulated polling of `trackFailure`, an arbitrary number ( > 2 ) times
tracker.trackFailure();
tracker.trackFailure();
expect(failures.length).toBe(2, 'expected 2 failures to be tracked, got ' + failures.length);
expect(failures[0].failedEventId).toBe(decryptedEvent.getId(), 'the first failure should be tracked first');
expect(failures[1].failedEventId).toBe(decryptedEvent2.getId(), 'the second failure should be tracked second');
done();
});
it('should not track a failure for an event that was tracked previously', (done) => {
const decryptedEvent = createFailedDecryptionEvent();
const failures = [];
const tracker = new DecryptionFailureTracker((failure) => failures.push(failure));
// Indicate decryption
tracker.eventDecrypted(decryptedEvent);
// Pretend "now" is Infinity
tracker.checkFailures(Infinity);
tracker.trackFailure();
// Indicate a second decryption, after having tracked the failure
tracker.eventDecrypted(decryptedEvent);
tracker.trackFailure();
expect(failures.length).toBe(1, 'should only track a single failure per event');
done();
});
xit('should not track a failure for an event that was tracked in a previous session', (done) => {
// This test uses localStorage, clear it beforehand
localStorage.clear();
const decryptedEvent = createFailedDecryptionEvent();
const failures = [];
const tracker = new DecryptionFailureTracker((failure) => failures.push(failure));
// Indicate decryption
tracker.eventDecrypted(decryptedEvent);
// Pretend "now" is Infinity
// NB: This saves to localStorage specific to DFT
tracker.checkFailures(Infinity);
tracker.trackFailure();
// Simulate the browser refreshing by destroying tracker and creating a new tracker
const secondTracker = new DecryptionFailureTracker((failure) => failures.push(failure));
//secondTracker.loadTrackedEventHashMap();
secondTracker.eventDecrypted(decryptedEvent);
secondTracker.checkFailures(Infinity);
secondTracker.trackFailure();
expect(failures.length).toBe(1, 'should track a single failure per event per session, got ' + failures.length);
done();
});
});