mirror of
https://github.com/element-hq/element-web
synced 2024-11-23 09:46:09 +03:00
Merge remote-tracking branch 'origin/develop' into jryans/room-view-crypto-crash
This commit is contained in:
commit
209b386e23
62 changed files with 3025 additions and 865 deletions
|
@ -319,7 +319,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dialog_titleImage {
|
.mx_Dialog_titleImage {
|
||||||
vertical-align: middle;
|
vertical-align: sub;
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
|
@ -588,27 +588,16 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||||
|
|
||||||
// A context menu that largely fits the | [icon] [label] | format.
|
// A context menu that largely fits the | [icon] [label] | format.
|
||||||
.mx_IconizedContextMenu {
|
.mx_IconizedContextMenu {
|
||||||
// Put 20px of padding around the whole menu. We do this instead of a
|
min-width: 146px;
|
||||||
// simple `padding: 20px` rule so the horizontal rules added by the
|
|
||||||
// optionLists is rendered correctly (full width).
|
|
||||||
> * {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 20px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_IconizedContextMenu_optionList {
|
.mx_IconizedContextMenu_optionList {
|
||||||
|
& > * {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
// the notFirst class is for cases where the optionList might be under a header of sorts.
|
// the notFirst class is for cases where the optionList might be under a header of sorts.
|
||||||
&:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst {
|
&:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst {
|
||||||
margin-top: 12px;
|
|
||||||
|
|
||||||
// This is a bit of a hack when we could just use a simple border-top property,
|
// This is a bit of a hack when we could just use a simple border-top property,
|
||||||
// however we have a (kinda) good reason for doing it this way: we need opacity.
|
// however we have a (kinda) good reason for doing it this way: we need opacity.
|
||||||
// To get the right color, we need an opacity modifier which means we have to work
|
// To get the right color, we need an opacity modifier which means we have to work
|
||||||
|
@ -631,72 +620,76 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
// round the top corners of the top button for the hover effect to be bounded
|
||||||
list-style: none;
|
&:first-child .mx_AccessibleButton:first-child {
|
||||||
margin: 0;
|
border-radius: 4px 4px 0 0; // radius matches .mx_ContextualMenu
|
||||||
padding: 0;
|
}
|
||||||
|
|
||||||
li {
|
// round the bottom corners of the bottom button for the hover effect to be bounded
|
||||||
margin: 0;
|
&:last-child .mx_AccessibleButton:last-child {
|
||||||
padding: 12px 0 0;
|
border-radius: 0 0 4px 4px; // radius matches .mx_ContextualMenu
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
text-decoration: none;
|
// pad the inside of the button so that the hover background is padded too
|
||||||
color: $primary-fg-color;
|
padding-top: 12px;
|
||||||
font-size: $font-15px;
|
padding-bottom: 12px;
|
||||||
line-height: $font-24px;
|
text-decoration: none;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
|
||||||
// Create a flexbox to more easily define the list items
|
// Create a flexbox to more easily define the list items
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
img, .mx_IconizedContextMenu_icon { // icons
|
&:hover {
|
||||||
width: 16px;
|
background-color: $menu-selected-color;
|
||||||
min-width: 16px;
|
}
|
||||||
max-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span:last-child { // labels
|
img, .mx_IconizedContextMenu_icon { // icons
|
||||||
padding-left: 14px;
|
width: 16px;
|
||||||
width: 100%;
|
min-width: 16px;
|
||||||
flex: 1;
|
max-width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
// Ellipsize any text overflow
|
span.mx_IconizedContextMenu_label { // labels
|
||||||
text-overflow: ellipsis;
|
padding-left: 14px;
|
||||||
overflow: hidden;
|
width: 100%;
|
||||||
white-space: nowrap;
|
flex: 1;
|
||||||
}
|
|
||||||
}
|
// Ellipsize any text overflow
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_IconizedContextMenu_compact {
|
&.mx_IconizedContextMenu_compact {
|
||||||
> * {
|
.mx_IconizedContextMenu_optionList > * {
|
||||||
padding-left: 11px;
|
padding: 8px 16px 8px 11px;
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-top: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-bottom: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_IconizedContextMenu_optionList {
|
|
||||||
&:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
li:first-child {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@define-mixin ProgressBarColour $colour {
|
||||||
|
color: $colour;
|
||||||
|
&::-moz-progress-bar {
|
||||||
|
background-color: $colour;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
background-color: $colour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@define-mixin ProgressBarBorderRadius $radius {
|
||||||
|
border-radius: $radius;
|
||||||
|
&::-moz-progress-bar {
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-bar,
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_UserMenu_contextMenu_redRow {
|
.mx_UserMenu_contextMenu_redRow {
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
color: $warning-color !important; // !important to override styles from context menu
|
color: $warning-color !important; // !important to override styles from context menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +97,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_UserMenu_contextMenu_header {
|
.mx_UserMenu_contextMenu_header {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
// Create a flexbox to organize the header a bit easier
|
// Create a flexbox to organize the header a bit easier
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -18,16 +18,6 @@ $PassphraseStrengthHigh: $accent-color;
|
||||||
$PassphraseStrengthMedium: $username-variant5-color;
|
$PassphraseStrengthMedium: $username-variant5-color;
|
||||||
$PassphraseStrengthLow: $notice-primary-color;
|
$PassphraseStrengthLow: $notice-primary-color;
|
||||||
|
|
||||||
@define-mixin ProgressBarColour $colour {
|
|
||||||
color: $colour;
|
|
||||||
&::-moz-progress-bar {
|
|
||||||
background-color: $colour;
|
|
||||||
}
|
|
||||||
&::-webkit-progress-value {
|
|
||||||
background-color: $colour;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.mx_PassphraseField_progress {
|
progress.mx_PassphraseField_progress {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -36,15 +26,7 @@ progress.mx_PassphraseField_progress {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -12px;
|
top: -12px;
|
||||||
|
|
||||||
border-radius: 2px;
|
@mixin ProgressBarBorderRadius "2px";
|
||||||
&::-moz-progress-bar {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
&::-webkit-progress-bar,
|
|
||||||
&::-webkit-progress-value {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin ProgressBarColour $PassphraseStrengthLow;
|
@mixin ProgressBarColour $PassphraseStrengthLow;
|
||||||
&[value="2"], &[value="3"] {
|
&[value="2"], &[value="3"] {
|
||||||
@mixin ProgressBarColour $PassphraseStrengthMedium;
|
@mixin ProgressBarColour $PassphraseStrengthMedium;
|
||||||
|
|
|
@ -15,20 +15,79 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_titleWithIcon::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
background-color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_secureBackupTitle::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-backup.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_securePhraseTitle::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-phrase.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AccessSecretStorageDialog_keyStatus {
|
.mx_AccessSecretStorageDialog_keyStatus {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessSecretStorageDialog_primaryContainer {
|
.mx_AccessSecretStorageDialog_passPhraseInput {
|
||||||
/* FIXME: plinth colour in new theme(s). background-color: $accent-color; */
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AccessSecretStorageDialog_passPhraseInput,
|
|
||||||
.mx_AccessSecretStorageDialog_recoveryKeyInput {
|
|
||||||
width: 300px;
|
width: 300px;
|
||||||
border: 1px solid $accent-color;
|
border: 1px solid $accent-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyEntry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyEntry_textInput {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText {
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyFeedback {
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: 20px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyFeedback_valid {
|
||||||
|
color: $input-valid-border-color;
|
||||||
|
&::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/check.svg');
|
||||||
|
background-color: $input-valid-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyFeedback_invalid {
|
||||||
|
color: $input-invalid-border-color;
|
||||||
|
&::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/x.svg');
|
||||||
|
background-color: $input-invalid-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,29 @@ limitations under the License.
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_titleWithIcon::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
background-color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_secureBackupTitle::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-backup.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_securePhraseTitle::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-phrase.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_centeredTitle, .mx_CreateSecretStorageDialog_centeredBody {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_primaryContainer {
|
.mx_CreateSecretStorageDialog_primaryContainer {
|
||||||
/* FIXME: plinth colour in new theme(s). background-color: $accent-color; */
|
/* FIXME: plinth colour in new theme(s). background-color: $accent-color; */
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
@ -59,6 +82,36 @@ limitations under the License.
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_primaryContainer .mx_RadioButton {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_optionTitle {
|
||||||
|
color: $dialog-title-fg-color;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: $font-18px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_optionIcon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
background-color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_optionIcon_securePhrase {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-phrase.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_optionIcon_secureBackup {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/secure-backup.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_passPhraseContainer {
|
.mx_CreateSecretStorageDialog_passPhraseContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@ -73,33 +126,42 @@ limitations under the License.
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_recoveryKeyHeader {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_recoveryKeyContainer {
|
.mx_CreateSecretStorageDialog_recoveryKeyContainer {
|
||||||
display: flex;
|
width: 380px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_recoveryKey {
|
.mx_CreateSecretStorageDialog_recoveryKey {
|
||||||
width: 262px;
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
color: $info-plinth-fg-color;
|
color: $info-plinth-fg-color;
|
||||||
background-color: $info-plinth-bg-color;
|
background-color: $info-plinth-bg-color;
|
||||||
margin-right: 12px;
|
border-radius: 6px;
|
||||||
|
word-spacing: 1em;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_recoveryKeyButtons {
|
.mx_CreateSecretStorageDialog_recoveryKeyButtons {
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_recoveryKeyButtons .mx_AccessibleButton {
|
.mx_CreateSecretStorageDialog_recoveryKeyButtons .mx_AccessibleButton {
|
||||||
margin-right: 10px;
|
width: 160px;
|
||||||
}
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
.mx_CreateSecretStorageDialog_recoveryKeyButtons button {
|
|
||||||
flex: 1;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_continueSpinner {
|
||||||
|
margin-top: 33px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSecretStorageDialog_continueSpinner img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
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,12 +14,26 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_ProgressBar {
|
progress.mx_ProgressBar {
|
||||||
height: 5px;
|
height: 4px;
|
||||||
border: 1px solid $progressbar-color;
|
width: 60px;
|
||||||
}
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
appearance: none;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
.mx_ProgressBar_fill {
|
@mixin ProgressBarBorderRadius "10px";
|
||||||
height: 100%;
|
@mixin ProgressBarColour $accent-color;
|
||||||
background-color: $progressbar-color;
|
::-webkit-progress-value {
|
||||||
|
transition: width 1s;
|
||||||
|
}
|
||||||
|
::-moz-progress-bar {
|
||||||
|
transition: padding-bottom 1s;
|
||||||
|
padding-bottom: var(--value);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: rotate(-90deg) translateX(-15px);
|
||||||
|
padding-left: 15px;
|
||||||
|
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,17 @@ limitations under the License.
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
> span {
|
border: 1px solid $input-darker-bg-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
> .mx_RadioButton_content {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
@ -105,3 +109,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RadioButton_checked {
|
||||||
|
border-color: $accent-color;
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ limitations under the License.
|
||||||
|
|
||||||
// The tile is also a flexbox row itself
|
// The tile is also a flexbox row itself
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
&.mx_RoomTile2_selected, &:hover, &.mx_RoomTile2_hasMenuOpen {
|
&.mx_RoomTile2_selected, &:hover, &.mx_RoomTile2_hasMenuOpen {
|
||||||
background-color: $roomtile2-selected-bg-color;
|
background-color: $roomtile2-selected-bg-color;
|
||||||
|
@ -43,7 +42,8 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomTile2_nameContainer {
|
.mx_RoomTile2_nameContainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
max-width: calc(100% - 58px); // 32px avatar, 18px badge area, 8px margin on avatar
|
min-width: 0; // allow flex to shrink it
|
||||||
|
margin-right: 8px; // spacing to buttons/badges
|
||||||
|
|
||||||
// Create a new column layout flexbox for the name parts
|
// Create a new column layout flexbox for the name parts
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -81,31 +81,39 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile2_badgeContainer {
|
.mx_RoomTile2_menuButton {
|
||||||
width: 18px;
|
margin-left: 4px; // spacing between buttons
|
||||||
height: 32px;
|
|
||||||
|
|
||||||
// Create another flexbox row because it's super easy to position the badge at
|
|
||||||
// the end this way.
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The menu button is hidden by default
|
.mx_RoomTile2_badgeContainer {
|
||||||
// TODO: [Notifications] Use mx_RoomTile2_notificationsButton, similar to the following approach:
|
height: 16px;
|
||||||
// https://github.com/matrix-org/matrix-react-sdk/blob/2180a56074f3698fc0241c309a72ba6cad802d1c/res/css/views/rooms/_RoomSublist2.scss#L48-L76
|
// don't set width so that it takes no space when there is no badge to show
|
||||||
// You'll need to do the same down below on the &:hover selector for the tile.
|
margin: auto 0; // vertically align
|
||||||
// See https://github.com/vector-im/riot-web/issues/13961.
|
|
||||||
// ... also remove this 5 line TODO comment.
|
.mx_NotificationBadge {
|
||||||
|
margin-right: 2px; // centering
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_NotificationBadge_dot {
|
||||||
|
// make the smaller dot occupy the same width for centering
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The context menu buttons are hidden by default
|
||||||
.mx_RoomTile2_menuButton,
|
.mx_RoomTile2_menuButton,
|
||||||
.mx_RoomTile2_notificationsButton {
|
.mx_RoomTile2_notificationsButton {
|
||||||
width: 0;
|
width: 20px;
|
||||||
height: 0;
|
min-width: 20px; // yay flex
|
||||||
visibility: hidden;
|
height: 20px;
|
||||||
|
margin: auto 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
content: '';
|
content: '';
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@ -117,9 +125,12 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the room has an overriden notification setting then we always show the notifications menu button
|
||||||
|
.mx_RoomTile2_notificationsButton.mx_RoomTile2_notificationsButton_show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomTile2_menuButton::before {
|
.mx_RoomTile2_menuButton::before {
|
||||||
top: 8px;
|
|
||||||
left: -1px; // this is off-center to align it with the badges
|
|
||||||
mask-image: url('$(res)/img/feather-customised/more-horizontal.svg');
|
mask-image: url('$(res)/img/feather-customised/more-horizontal.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +140,12 @@ limitations under the License.
|
||||||
.mx_RoomTile2_badgeContainer {
|
.mx_RoomTile2_badgeContainer {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
visibility: hidden;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile2_notificationsButton,
|
||||||
.mx_RoomTile2_menuButton {
|
.mx_RoomTile2_menuButton {
|
||||||
width: 18px;
|
display: block;
|
||||||
height: 32px;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +168,23 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We use these both in context menus and the room tiles
|
||||||
|
.mx_RoomTile2_iconBell::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/bell.svg');
|
||||||
|
}
|
||||||
|
.mx_RoomTile2_iconBellDot::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/bell-notification.custom.svg');
|
||||||
|
}
|
||||||
|
.mx_RoomTile2_iconBellCrossed::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/bell-crossed.svg');
|
||||||
|
}
|
||||||
|
.mx_RoomTile2_iconBellMentions::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/bell-mentions.custom.svg');
|
||||||
|
}
|
||||||
|
.mx_RoomTile2_iconCheck::before {
|
||||||
|
mask-image: url('$(res)/img/feather-customised/check.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomTile2_contextMenu {
|
.mx_RoomTile2_contextMenu {
|
||||||
.mx_RoomTile2_contextMenu_redRow {
|
.mx_RoomTile2_contextMenu_redRow {
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
|
@ -169,6 +196,16 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile2_contextMenu_activeRow {
|
||||||
|
&.mx_AccessibleButton, .mx_AccessibleButton {
|
||||||
|
color: $accent-color !important; // !important to override styles from context menu
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_IconizedContextMenu_icon::before {
|
||||||
|
background-color: $accent-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_IconizedContextMenu_icon {
|
.mx_IconizedContextMenu_icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
4
res/img/feather-customised/bell-crossed.svg
Normal file
4
res/img/feather-customised/bell-crossed.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.31422 2.4647C8.07372 2.6004 7.98877 2.90537 8.12448 3.14587C8.26018 3.38637 8.56515 3.47132 8.80565 3.33562L8.31422 2.4647ZM18.9999 9.00016L18.4999 8.9999V9.00016H18.9999ZM18.4999 13.0002C18.4999 13.2763 18.7238 13.5002 18.9999 13.5002C19.2761 13.5002 19.4999 13.2763 19.4999 13.0002H18.4999ZM17 17.5004C17.2761 17.5004 17.5 17.2765 17.5 17.0004C17.5 16.7242 17.2761 16.5004 17 16.5004V17.5004ZM2 16.5004C1.72386 16.5004 1.5 16.7242 1.5 17.0004C1.5 17.2765 1.72386 17.5004 2 17.5004V16.5004ZM5 9.00036H5.5L5.5 8.99973L5 9.00036ZM6.22429 6.00974C6.35096 5.76436 6.25474 5.46276 6.00937 5.33608C5.764 5.2094 5.46239 5.30562 5.33571 5.551L6.22429 6.00974ZM14.1625 21.2509C14.301 21.012 14.2197 20.7061 13.9808 20.5675C13.742 20.4289 13.436 20.5103 13.2975 20.7491L14.1625 21.2509ZM10.7025 20.7491C10.5639 20.5103 10.2579 20.4289 10.0191 20.5675C9.78021 20.7061 9.6989 21.012 9.83746 21.2509L10.7025 20.7491ZM8.80565 3.33562C10.8187 2.19975 13.2834 2.21831 15.2791 3.38436L15.7836 2.52094C13.4809 1.17549 10.6369 1.15408 8.31422 2.4647L8.80565 3.33562ZM15.2791 3.38436C17.2748 4.55042 18.5011 6.68854 18.4999 8.9999L19.4999 9.00041C19.5013 6.33346 18.0863 3.86639 15.7836 2.52094L15.2791 3.38436ZM18.4999 9.00016V13.0002H19.4999V9.00016H18.4999ZM17 16.5004H2V17.5004H17V16.5004ZM2 17.5004C3.933 17.5004 5.5 15.9334 5.5 14.0004H4.5C4.5 15.3811 3.38071 16.5004 2 16.5004V17.5004ZM5.5 14.0004V9.00036H4.5V14.0004H5.5ZM5.5 8.99973C5.49869 7.95947 5.74707 6.93408 6.22429 6.00974L5.33571 5.551C4.78509 6.61755 4.49849 7.80069 4.5 9.00099L5.5 8.99973ZM13.2975 20.7491C13.0291 21.2117 12.5348 21.4965 12 21.4965V22.4965C12.8913 22.4965 13.7152 22.0219 14.1625 21.2509L13.2975 20.7491ZM12 21.4965C11.4652 21.4965 10.9708 21.2117 10.7025 20.7491L9.83746 21.2509C10.2847 22.0219 11.1086 22.4965 12 22.4965V21.4965Z" fill="#2E2F32"/>
|
||||||
|
<path d="M1 1L23 23" stroke="#2E2F32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
3
res/img/feather-customised/bell-mentions.custom.svg
Normal file
3
res/img/feather-customised/bell-mentions.custom.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.62998 3.57476C7.55241 1.6636 9.6476 0.644608 11.692 1.13263C13.7342 1.62012 15.1666 3.47754 15.1667 5.60305V5.60308V6.01222C15.1667 6.95553 14.4163 7.73965 13.4668 7.73965C12.9349 7.73965 12.4655 7.49363 12.1555 7.11141C11.7768 7.49925 11.2519 7.74098 10.6668 7.74098C9.49647 7.74098 8.56689 6.77368 8.56689 5.60441C8.56689 4.43514 9.49647 3.46784 10.6668 3.46784C11.8348 3.46784 12.7629 4.43111 12.7668 5.59709L12.7668 5.60308V6.01222C12.7668 6.4247 13.0908 6.73965 13.4668 6.73965C13.8428 6.73965 14.1667 6.4247 14.1667 6.01222V5.60311V5.60308C14.1666 3.92595 13.0379 2.48201 11.4598 2.1053C9.8839 1.72911 8.25387 2.51086 7.53057 4.00944C6.80579 5.5111 7.19017 7.3233 8.44894 8.38151C9.70415 9.43672 11.5011 9.46808 12.7905 8.45807C13.0079 8.28778 13.3221 8.32596 13.4924 8.54335C13.6627 8.76074 13.6245 9.07501 13.4071 9.24529C11.745 10.5473 9.42229 10.5062 7.80545 9.14696C6.19216 7.79072 5.70903 5.48285 6.62998 3.57476ZM10.6668 4.46784C10.07 4.46784 9.56689 4.96597 9.56689 5.60441C9.56689 6.24285 10.07 6.74098 10.6668 6.74098C11.2637 6.74098 11.7668 6.24285 11.7668 5.60441C11.7668 4.96597 11.2637 4.46784 10.6668 4.46784ZM5.48951 2.14C5.61741 2.38474 5.5227 2.68682 5.27796 2.81472C3.92878 3.51981 3 4.95881 3 6.62506V10.0347C3 10.6137 2.8091 11.1505 2.48631 11.5805H13.8333C14.1095 11.5805 14.3333 11.8043 14.3333 12.0805C14.3333 12.3566 14.1095 12.5805 13.8333 12.5805H0.5C0.223858 12.5805 0 12.3566 0 12.0805C0 11.8043 0.223858 11.5805 0.5 11.5805C1.31782 11.5805 2 10.8991 2 10.0347V6.62506C2 4.58053 3.14094 2.80322 4.81479 1.92845C5.05953 1.80055 5.36161 1.89527 5.48951 2.14ZM5.76678 14.3741C6.00698 14.2379 6.31214 14.3222 6.44836 14.5624C6.59999 14.8298 6.8752 14.9886 7.16676 14.9886C7.45832 14.9886 7.73354 14.8298 7.88516 14.5624C8.02139 14.3222 8.32654 14.2379 8.56674 14.3741C8.80695 14.5104 8.89124 14.8155 8.75502 15.0557C8.42959 15.6296 7.82596 15.9886 7.16676 15.9886C6.50756 15.9886 5.90393 15.6296 5.5785 15.0557C5.44228 14.8155 5.52657 14.5104 5.76678 14.3741Z" fill="#2E2F32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
5
res/img/feather-customised/bell-notification.custom.svg
Normal file
5
res/img/feather-customised/bell-notification.custom.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M13.73 21C13.3722 21.6168 12.7131 21.9965 12 21.9965C11.287 21.9965 10.6278 21.6168 10.27 21" stroke="#2E2F32" stroke-linecap="round"/>
|
||||||
|
<path d="M11.9999 2.00024C8.13388 2.00024 4.99988 5.13425 4.99988 9.00024V14.0002C4.99988 15.6571 3.65673 17.0002 1.99988 17.0002H21.9999C20.343 17.0002 18.9999 15.6571 18.9999 14.0002V12.75" stroke="#2E2F32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="18.75" cy="5.25" r="4.75" stroke="#2E2F32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 563 B |
3
res/img/feather-customised/bell.svg
Normal file
3
res/img/feather-customised/bell.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M22 17.5C22.2761 17.5 22.5 17.2761 22.5 17C22.5 16.7239 22.2761 16.5 22 16.5V17.5ZM2 16.5C1.72386 16.5 1.5 16.7239 1.5 17C1.5 17.2761 1.72386 17.5 2 17.5V16.5ZM5 9H4.5H5ZM19 9H19.5H19ZM14.1625 21.2509C14.3011 21.012 14.2197 20.7061 13.9809 20.5675C13.742 20.4289 13.4361 20.5103 13.2975 20.7491L14.1625 21.2509ZM10.7025 20.7491C10.5639 20.5103 10.258 20.4289 10.0191 20.5675C9.78025 20.7061 9.69894 21.012 9.8375 21.2509L10.7025 20.7491ZM22 16.5H2V17.5H22V16.5ZM2 17.5C3.933 17.5 5.5 15.933 5.5 14H4.5C4.5 15.3807 3.38071 16.5 2 16.5V17.5ZM5.5 14V9H4.5V14H5.5ZM5.5 9C5.5 5.41015 8.41015 2.5 12 2.5V1.5C7.85786 1.5 4.5 4.85786 4.5 9H5.5ZM12 2.5C15.5899 2.5 18.5 5.41015 18.5 9H19.5C19.5 4.85786 16.1421 1.5 12 1.5V2.5ZM18.5 9V14H19.5V9H18.5ZM18.5 14C18.5 15.933 20.067 17.5 22 17.5V16.5C20.6193 16.5 19.5 15.3807 19.5 14H18.5ZM13.2975 20.7491C13.0292 21.2117 12.5348 21.4965 12 21.4965V22.4965C12.8913 22.4965 13.7153 22.0219 14.1625 21.2509L13.2975 20.7491ZM12 21.4965C11.4652 21.4965 10.9708 21.2117 10.7025 20.7491L9.8375 21.2509C10.2847 22.0219 11.1087 22.4965 12 22.4965V21.4965Z" fill="#2E2F32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
11
res/img/feather-customised/secure-backup.svg
Normal file
11
res/img/feather-customised/secure-backup.svg
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="1" y="2" width="22" height="21">
|
||||||
|
<rect x="1" y="2" width="21.5" height="5" fill="white"/>
|
||||||
|
<rect x="1" y="17.7" width="21.5" height="5" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0)">
|
||||||
|
<path d="M3.16626 12.4014C3.16626 7.64675 6.99526 3.81775 11.75 3.81775C13.0964 3.81775 14.4429 4.15437 15.6631 4.74345H14.9899C14.5691 4.74345 14.2325 5.08006 14.2325 5.50083C14.2325 5.9216 14.5691 6.25822 14.9899 6.25822H17.3041C17.809 6.25822 18.1877 5.83745 18.1877 5.3746V3.06037C18.1877 2.6396 17.8511 2.30298 17.4303 2.30298C17.0096 2.30298 16.673 2.6396 16.673 3.06037V3.60737C16.6309 3.56529 16.5888 3.5653 16.5467 3.52322C15.074 2.72376 13.433 2.30298 11.75 2.30298C6.1958 2.30298 1.65149 6.84729 1.65149 12.4014C1.65149 14.0845 2.07226 15.7676 2.87172 17.2403C2.99795 17.4928 3.25041 17.619 3.54495 17.619C3.67118 17.619 3.79741 17.5769 3.92364 17.5348C4.30233 17.3245 4.42857 16.8616 4.21819 16.525C3.50288 15.2627 3.16626 13.8321 3.16626 12.4014Z" fill="#2E2F32"/>
|
||||||
|
<path d="M20.6281 7.56263C20.4177 7.18394 19.9548 7.05771 19.6182 7.2681C19.2395 7.47848 19.1133 7.94133 19.3237 8.27794C19.9969 9.54025 20.3756 10.9288 20.3756 12.4015C20.3756 17.1562 16.5045 20.9852 11.7919 20.9852C10.4454 20.9852 9.09897 20.6486 7.87874 20.0595H8.55198C8.97275 20.0595 9.30937 19.7229 9.30937 19.3021C9.30937 18.8813 8.97275 18.5447 8.55198 18.5447H6.23774C5.73282 18.5447 5.35413 18.9655 5.35413 19.4283V21.7426C5.35413 22.1633 5.69075 22.4999 6.11152 22.4999C6.53229 22.4999 6.8689 22.1633 6.8689 21.7426V21.1956C6.91098 21.2376 6.95306 21.2376 6.99514 21.2797C8.42575 22.0792 10.0667 22.4999 11.7498 22.4999C17.304 22.4999 21.8483 17.9556 21.8483 12.4015C21.8483 10.7184 21.4275 9.03532 20.6281 7.56263Z" fill="#2E2F32"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 9C1.89543 9 1 9.89543 1 11V14C1 15.1046 1.89543 16 3 16H21C22.1046 16 23 15.1046 23 14V11C23 9.89543 22.1046 9 21 9H3ZM5.25 10.5C4.83579 10.5 4.5 10.8358 4.5 11.25C4.5 11.6642 4.83579 12 5.25 12H7.75C8.16421 12 8.5 11.6642 8.5 11.25C8.5 10.8358 8.16421 10.5 7.75 10.5H5.25ZM9.5 11.25C9.5 10.8358 9.83579 10.5 10.25 10.5H10.75C11.1642 10.5 11.5 10.8358 11.5 11.25C11.5 11.6642 11.1642 12 10.75 12H10.25C9.83579 12 9.5 11.6642 9.5 11.25ZM13.25 10.5C12.8358 10.5 12.5 10.8358 12.5 11.25C12.5 11.6642 12.8358 12 13.25 12H15.75C16.1642 12 16.5 11.6642 16.5 11.25C16.5 10.8358 16.1642 10.5 15.75 10.5H13.25ZM17.5 11.25C17.5 10.8358 17.8358 10.5 18.25 10.5H18.75C19.1642 10.5 19.5 10.8358 19.5 11.25C19.5 11.6642 19.1642 12 18.75 12H18.25C17.8358 12 17.5 11.6642 17.5 11.25ZM5.25 13C4.83579 13 4.5 13.3358 4.5 13.75C4.5 14.1642 4.83579 14.5 5.25 14.5H5.75C6.16421 14.5 6.5 14.1642 6.5 13.75C6.5 13.3358 6.16421 13 5.75 13H5.25ZM7.5 13.75C7.5 13.3358 7.83579 13 8.25 13H10.75C11.1642 13 11.5 13.3358 11.5 13.75C11.5 14.1642 11.1642 14.5 10.75 14.5H8.25C7.83579 14.5 7.5 14.1642 7.5 13.75ZM13.25 13C12.8358 13 12.5 13.3358 12.5 13.75C12.5 14.1642 12.8358 14.5 13.25 14.5H13.75C14.1642 14.5 14.5 14.1642 14.5 13.75C14.5 13.3358 14.1642 13 13.75 13H13.25Z" fill="#2E2F32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
11
res/img/feather-customised/secure-phrase.svg
Normal file
11
res/img/feather-customised/secure-phrase.svg
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="1" y="2" width="22" height="21">
|
||||||
|
<rect x="1" y="2" width="21.5" height="5" fill="white"/>
|
||||||
|
<rect x="1" y="17.7" width="21.5" height="5" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0)">
|
||||||
|
<path d="M3.16626 12.4014C3.16626 7.64675 6.99526 3.81775 11.75 3.81775C13.0964 3.81775 14.4429 4.15437 15.6631 4.74345H14.9899C14.5691 4.74345 14.2325 5.08006 14.2325 5.50083C14.2325 5.9216 14.5691 6.25822 14.9899 6.25822H17.3041C17.809 6.25822 18.1877 5.83745 18.1877 5.3746V3.06037C18.1877 2.6396 17.8511 2.30298 17.4303 2.30298C17.0096 2.30298 16.673 2.6396 16.673 3.06037V3.60737C16.6309 3.56529 16.5888 3.5653 16.5467 3.52322C15.074 2.72376 13.433 2.30298 11.75 2.30298C6.1958 2.30298 1.65149 6.84729 1.65149 12.4014C1.65149 14.0845 2.07226 15.7676 2.87172 17.2403C2.99795 17.4928 3.25041 17.619 3.54495 17.619C3.67118 17.619 3.79741 17.5769 3.92364 17.5348C4.30233 17.3245 4.42857 16.8616 4.21819 16.525C3.50288 15.2627 3.16626 13.8321 3.16626 12.4014Z" fill="#2E2F32"/>
|
||||||
|
<path d="M20.6281 7.56263C20.4177 7.18394 19.9548 7.05771 19.6182 7.2681C19.2395 7.47848 19.1133 7.94133 19.3237 8.27794C19.9969 9.54025 20.3756 10.9288 20.3756 12.4015C20.3756 17.1562 16.5045 20.9852 11.7919 20.9852C10.4454 20.9852 9.09897 20.6486 7.87874 20.0595H8.55198C8.97275 20.0595 9.30937 19.7229 9.30937 19.3021C9.30937 18.8813 8.97275 18.5447 8.55198 18.5447H6.23774C5.73282 18.5447 5.35413 18.9655 5.35413 19.4283V21.7426C5.35413 22.1633 5.69075 22.4999 6.11152 22.4999C6.53229 22.4999 6.8689 22.1633 6.8689 21.7426V21.1956C6.91098 21.2376 6.95306 21.2376 6.99514 21.2797C8.42575 22.0792 10.0667 22.4999 11.7498 22.4999C17.304 22.4999 21.8483 17.9556 21.8483 12.4015C21.8483 10.7184 21.4275 9.03532 20.6281 7.56263Z" fill="#2E2F32"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 11C1 9.89543 1.89543 9 3 9H21C22.1046 9 23 9.89543 23 11V14C23 15.1046 22.1046 16 21 16H3C1.89543 16 1 15.1046 1 14V11ZM6 12.5C6 13.3284 5.32843 14 4.5 14C3.67157 14 3 13.3284 3 12.5C3 11.6716 3.67157 11 4.5 11C5.32843 11 6 11.6716 6 12.5ZM9.5 14C10.3284 14 11 13.3284 11 12.5C11 11.6716 10.3284 11 9.5 11C8.67157 11 8 11.6716 8 12.5C8 13.3284 8.67157 14 9.5 14ZM16 12.5C16 13.3284 15.3284 14 14.5 14C13.6716 14 13 13.3284 13 12.5C13 11.6716 13.6716 11 14.5 11C15.3284 11 16 11.6716 16 12.5ZM19.5 14C20.3284 14 21 13.3284 21 12.5C21 11.6716 20.3284 11 19.5 11C18.6716 11 18 11.6716 18 12.5C18 13.3284 18.6716 14 19.5 14Z" fill="#2E2F32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -495,8 +495,7 @@ export const Commands = [
|
||||||
});
|
});
|
||||||
return success();
|
return success();
|
||||||
} else if (params[0][0] === '!') {
|
} else if (params[0][0] === '!') {
|
||||||
const roomId = params[0];
|
const [roomId, ...viaServers] = params;
|
||||||
const viaServers = params.splice(0);
|
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
|
|
|
@ -26,20 +26,27 @@ import { promptForBackupPassphrase } from '../../../../CrossSigningManager';
|
||||||
import {copyNode} from "../../../../utils/strings";
|
import {copyNode} from "../../../../utils/strings";
|
||||||
import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents";
|
import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents";
|
||||||
import PassphraseField from "../../../../components/views/auth/PassphraseField";
|
import PassphraseField from "../../../../components/views/auth/PassphraseField";
|
||||||
|
import StyledRadioButton from '../../../../components/views/elements/StyledRadioButton';
|
||||||
|
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
||||||
|
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||||
|
import InlineSpinner from "../../../../components/views/elements/InlineSpinner";
|
||||||
|
|
||||||
const PHASE_LOADING = 0;
|
const PHASE_LOADING = 0;
|
||||||
const PHASE_LOADERROR = 1;
|
const PHASE_LOADERROR = 1;
|
||||||
const PHASE_MIGRATE = 2;
|
const PHASE_CHOOSE_KEY_PASSPHRASE = 2;
|
||||||
const PHASE_PASSPHRASE = 3;
|
const PHASE_MIGRATE = 3;
|
||||||
const PHASE_PASSPHRASE_CONFIRM = 4;
|
const PHASE_PASSPHRASE = 4;
|
||||||
const PHASE_SHOWKEY = 5;
|
const PHASE_PASSPHRASE_CONFIRM = 5;
|
||||||
const PHASE_KEEPITSAFE = 6;
|
const PHASE_SHOWKEY = 6;
|
||||||
const PHASE_STORING = 7;
|
const PHASE_STORING = 8;
|
||||||
const PHASE_DONE = 8;
|
const PHASE_CONFIRM_SKIP = 10;
|
||||||
const PHASE_CONFIRM_SKIP = 9;
|
|
||||||
|
|
||||||
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
|
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
|
||||||
|
|
||||||
|
// these end up as strings from being values in the radio buttons, so just use strings
|
||||||
|
const CREATE_STORAGE_OPTION_KEY = 'key';
|
||||||
|
const CREATE_STORAGE_OPTION_PASSPHRASE = 'passphrase';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Walks the user through the process of creating a passphrase to guard Secure
|
* Walks the user through the process of creating a passphrase to guard Secure
|
||||||
* Secret Storage in account data.
|
* Secret Storage in account data.
|
||||||
|
@ -70,6 +77,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
passPhraseConfirm: '',
|
passPhraseConfirm: '',
|
||||||
copied: false,
|
copied: false,
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
|
setPassphrase: false,
|
||||||
backupInfo: null,
|
backupInfo: null,
|
||||||
backupSigStatus: null,
|
backupSigStatus: null,
|
||||||
// does the server offer a UI auth flow with just m.login.password
|
// does the server offer a UI auth flow with just m.login.password
|
||||||
|
@ -77,8 +85,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
canUploadKeysWithPasswordOnly: null,
|
canUploadKeysWithPasswordOnly: null,
|
||||||
accountPassword: props.accountPassword || "",
|
accountPassword: props.accountPassword || "",
|
||||||
accountPasswordCorrect: null,
|
accountPasswordCorrect: null,
|
||||||
// status of the key backup toggle switch
|
|
||||||
useKeyBackup: true,
|
passPhraseKeySelected: CREATE_STORAGE_OPTION_KEY,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._passphraseField = createRef();
|
this._passphraseField = createRef();
|
||||||
|
@ -110,7 +118,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
);
|
);
|
||||||
|
|
||||||
const { force } = this.props;
|
const { force } = this.props;
|
||||||
const phase = (backupInfo && !force) ? PHASE_MIGRATE : PHASE_PASSPHRASE;
|
const phase = (backupInfo && !force) ? PHASE_MIGRATE : PHASE_CHOOSE_KEY_PASSPHRASE;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
phase,
|
phase,
|
||||||
|
@ -152,14 +160,33 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
if (this.state.phase === PHASE_MIGRATE) this._fetchBackupInfo();
|
if (this.state.phase === PHASE_MIGRATE) this._fetchBackupInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onKeyPassphraseChange = e => {
|
||||||
|
this.setState({
|
||||||
|
passPhraseKeySelected: e.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_collectRecoveryKeyNode = (n) => {
|
_collectRecoveryKeyNode = (n) => {
|
||||||
this._recoveryKeyNode = n;
|
this._recoveryKeyNode = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUseKeyBackupChange = (enabled) => {
|
_onChooseKeyPassphraseFormSubmit = async () => {
|
||||||
this.setState({
|
if (this.state.passPhraseKeySelected === CREATE_STORAGE_OPTION_KEY) {
|
||||||
useKeyBackup: enabled,
|
this._recoveryKey =
|
||||||
});
|
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
||||||
|
this.setState({
|
||||||
|
copied: false,
|
||||||
|
downloaded: false,
|
||||||
|
setPassphrase: false,
|
||||||
|
phase: PHASE_SHOWKEY,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
copied: false,
|
||||||
|
downloaded: false,
|
||||||
|
phase: PHASE_PASSPHRASE,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMigrateFormSubmit = (e) => {
|
_onMigrateFormSubmit = (e) => {
|
||||||
|
@ -176,7 +203,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
if (successful) {
|
if (successful) {
|
||||||
this.setState({
|
this.setState({
|
||||||
copied: true,
|
copied: true,
|
||||||
phase: PHASE_KEEPITSAFE,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,7 +215,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
downloaded: true,
|
downloaded: true,
|
||||||
phase: PHASE_KEEPITSAFE,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,22 +284,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
await cli.bootstrapSecretStorage({
|
await cli.bootstrapSecretStorage({
|
||||||
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
||||||
createSecretStorageKey: async () => this._recoveryKey,
|
createSecretStorageKey: async () => this._recoveryKey,
|
||||||
setupNewKeyBackup: this.state.useKeyBackup,
|
setupNewKeyBackup: true,
|
||||||
setupNewSecretStorage: true,
|
setupNewSecretStorage: true,
|
||||||
});
|
});
|
||||||
if (!this.state.useKeyBackup && this.state.backupInfo) {
|
|
||||||
// If the user is resetting their cross-signing keys and doesn't want
|
|
||||||
// key backup (but had it enabled before), delete the key backup as it's
|
|
||||||
// no longer valid.
|
|
||||||
console.log("Deleting invalid key backup (secrets have been reset; key backup not requested)");
|
|
||||||
await cli.deleteKeyBackupVersion(this.state.backupInfo.version);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await cli.bootstrapSecretStorage({
|
await cli.bootstrapSecretStorage({
|
||||||
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
||||||
createSecretStorageKey: async () => this._recoveryKey,
|
createSecretStorageKey: async () => this._recoveryKey,
|
||||||
keyBackupInfo: this.state.backupInfo,
|
keyBackupInfo: this.state.backupInfo,
|
||||||
setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup,
|
setupNewKeyBackup: !this.state.backupInfo,
|
||||||
getKeyBackupPassphrase: () => {
|
getKeyBackupPassphrase: () => {
|
||||||
// We may already have the backup key if we earlier went
|
// We may already have the backup key if we earlier went
|
||||||
// through the restore backup path, so pass it along
|
// through the restore backup path, so pass it along
|
||||||
|
@ -286,9 +304,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setState({
|
this.props.onFinished(true);
|
||||||
phase: PHASE_DONE,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) {
|
if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -342,22 +358,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
this._fetchBackupInfo();
|
this._fetchBackupInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSkipSetupClick = () => {
|
_onShowKeyContinueClick = () => {
|
||||||
|
this._bootstrapSecretStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onCancelClick = () => {
|
||||||
this.setState({phase: PHASE_CONFIRM_SKIP});
|
this.setState({phase: PHASE_CONFIRM_SKIP});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSetUpClick = () => {
|
_onGoBackClick = () => {
|
||||||
this.setState({phase: PHASE_PASSPHRASE});
|
this.setState({phase: PHASE_CHOOSE_KEY_PASSPHRASE});
|
||||||
}
|
|
||||||
|
|
||||||
_onSkipPassPhraseClick = async () => {
|
|
||||||
this._recoveryKey =
|
|
||||||
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
|
||||||
this.setState({
|
|
||||||
copied: false,
|
|
||||||
downloaded: false,
|
|
||||||
phase: PHASE_SHOWKEY,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPassPhraseNextClick = async (e) => {
|
_onPassPhraseNextClick = async (e) => {
|
||||||
|
@ -384,6 +394,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
this.setState({
|
this.setState({
|
||||||
copied: false,
|
copied: false,
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
|
setPassphrase: true,
|
||||||
phase: PHASE_SHOWKEY,
|
phase: PHASE_SHOWKEY,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -397,12 +408,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onKeepItSafeBackClick = () => {
|
|
||||||
this.setState({
|
|
||||||
phase: PHASE_SHOWKEY,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPassPhraseValidate = (result) => {
|
_onPassPhraseValidate = (result) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
passPhraseValid: result.valid,
|
passPhraseValid: result.valid,
|
||||||
|
@ -427,13 +432,53 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderPhaseChooseKeyPassphrase() {
|
||||||
|
return <form onSubmit={this._onChooseKeyPassphraseFormSubmit}>
|
||||||
|
<p className="mx_CreateSecretStorageDialog_centeredBody">{_t(
|
||||||
|
"Safeguard against losing access to encrypted messages & data by " +
|
||||||
|
"backing up encryption keys on your server.",
|
||||||
|
)}</p>
|
||||||
|
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup" onChange={this._onKeyPassphraseChange}>
|
||||||
|
<StyledRadioButton
|
||||||
|
key={CREATE_STORAGE_OPTION_KEY}
|
||||||
|
value={CREATE_STORAGE_OPTION_KEY}
|
||||||
|
name="keyPassphrase"
|
||||||
|
checked={this.state.passPhraseKeySelected === CREATE_STORAGE_OPTION_KEY}
|
||||||
|
>
|
||||||
|
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||||
|
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup"></span>
|
||||||
|
{_t("Generate a Security Key")}
|
||||||
|
</div>
|
||||||
|
<div>{_t("We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.")}</div>
|
||||||
|
</StyledRadioButton>
|
||||||
|
<StyledRadioButton
|
||||||
|
key={CREATE_STORAGE_OPTION_PASSPHRASE}
|
||||||
|
value={CREATE_STORAGE_OPTION_PASSPHRASE}
|
||||||
|
name="keyPassphrase"
|
||||||
|
checked={this.state.passPhraseKeySelected === CREATE_STORAGE_OPTION_PASSPHRASE}
|
||||||
|
>
|
||||||
|
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||||
|
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase"></span>
|
||||||
|
{_t("Enter a Security Phrase")}
|
||||||
|
</div>
|
||||||
|
<div>{_t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.")}</div>
|
||||||
|
</StyledRadioButton>
|
||||||
|
</div>
|
||||||
|
<DialogButtons
|
||||||
|
primaryButton={_t("Continue")}
|
||||||
|
onPrimaryButtonClick={this._onChooseKeyPassphraseFormSubmit}
|
||||||
|
onCancel={this._onCancelClick}
|
||||||
|
hasCancel={true}
|
||||||
|
/>
|
||||||
|
</form>;
|
||||||
|
}
|
||||||
|
|
||||||
_renderPhaseMigrate() {
|
_renderPhaseMigrate() {
|
||||||
// TODO: This is a temporary screen so people who have the labs flag turned on and
|
// TODO: This is a temporary screen so people who have the labs flag turned on and
|
||||||
// click the button are aware they're making a change to their account.
|
// click the button are aware they're making a change to their account.
|
||||||
// Once we're confident enough in this (and it's supported enough) we can do
|
// Once we're confident enough in this (and it's supported enough) we can do
|
||||||
// it automatically.
|
// it automatically.
|
||||||
// https://github.com/vector-im/riot-web/issues/11696
|
// https://github.com/vector-im/riot-web/issues/11696
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
const Field = sdk.getComponent('views.elements.Field');
|
const Field = sdk.getComponent('views.elements.Field');
|
||||||
|
|
||||||
let authPrompt;
|
let authPrompt;
|
||||||
|
@ -446,7 +491,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
label={_t("Password")}
|
label={_t("Password")}
|
||||||
value={this.state.accountPassword}
|
value={this.state.accountPassword}
|
||||||
onChange={this._onAccountPasswordChange}
|
onChange={this._onAccountPasswordChange}
|
||||||
flagInvalid={this.state.accountPasswordCorrect === false}
|
forceValidity={this.state.accountPasswordCorrect === false ? false : null}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
/></div>
|
/></div>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -474,7 +519,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
hasCancel={false}
|
hasCancel={false}
|
||||||
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
|
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
|
||||||
>
|
>
|
||||||
<button type="button" className="danger" onClick={this._onSkipSetupClick}>
|
<button type="button" className="danger" onClick={this._onCancelClick}>
|
||||||
{_t('Skip')}
|
{_t('Skip')}
|
||||||
</button>
|
</button>
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
|
@ -482,14 +527,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPhasePassPhrase() {
|
_renderPhasePassPhrase() {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
||||||
const LabelledToggleSwitch = sdk.getComponent('views.elements.LabelledToggleSwitch');
|
|
||||||
|
|
||||||
return <form onSubmit={this._onPassPhraseNextClick}>
|
return <form onSubmit={this._onPassPhraseNextClick}>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Set a recovery passphrase to secure encrypted information and recover it if you log out. " +
|
"Enter a security phrase only you know, as it’s used to safeguard your data. " +
|
||||||
"This should be different to your account password:",
|
"To be secure, you shouldn’t re-use your account password.",
|
||||||
)}</p>
|
)}</p>
|
||||||
|
|
||||||
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
||||||
|
@ -508,11 +549,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LabelledToggleSwitch
|
|
||||||
label={ _t("Back up encrypted message keys")}
|
|
||||||
onChange={this._onUseKeyBackupChange} value={this.state.useKeyBackup}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DialogButtons
|
<DialogButtons
|
||||||
primaryButton={_t('Continue')}
|
primaryButton={_t('Continue')}
|
||||||
onPrimaryButtonClick={this._onPassPhraseNextClick}
|
onPrimaryButtonClick={this._onPassPhraseNextClick}
|
||||||
|
@ -520,22 +556,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
disabled={!this.state.passPhraseValid}
|
disabled={!this.state.passPhraseValid}
|
||||||
>
|
>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
onClick={this._onSkipSetupClick}
|
onClick={this._onCancelClick}
|
||||||
className="danger"
|
className="danger"
|
||||||
>{_t("Skip")}</button>
|
>{_t("Cancel")}</button>
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>{_t("Advanced")}</summary>
|
|
||||||
<AccessibleButton kind='primary' onClick={this._onSkipPassPhraseClick} >
|
|
||||||
{_t("Set up with a recovery key")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</details>
|
|
||||||
</form>;
|
</form>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPhasePassPhraseConfirm() {
|
_renderPhasePassPhraseConfirm() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
||||||
const Field = sdk.getComponent('views.elements.Field');
|
const Field = sdk.getComponent('views.elements.Field');
|
||||||
|
|
||||||
let matchText;
|
let matchText;
|
||||||
|
@ -566,7 +594,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
|
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Enter your recovery passphrase a second time to confirm it.",
|
"Enter your recovery passphrase a second time to confirm it.",
|
||||||
|
@ -592,7 +619,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||||
>
|
>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
onClick={this._onSkipSetupClick}
|
onClick={this._onCancelClick}
|
||||||
className="danger"
|
className="danger"
|
||||||
>{_t("Skip")}</button>
|
>{_t("Skip")}</button>
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
|
@ -600,66 +627,48 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPhaseShowKey() {
|
_renderPhaseShowKey() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
let continueButton;
|
||||||
|
if (this.state.phase === PHASE_SHOWKEY) {
|
||||||
|
continueButton = <DialogButtons primaryButton={_t("Continue")}
|
||||||
|
disabled={!this.state.downloaded && !this.state.copied && !this.state.setPassphrase}
|
||||||
|
onPrimaryButtonClick={this._onShowKeyContinueClick}
|
||||||
|
hasCancel={false}
|
||||||
|
/>;
|
||||||
|
} else {
|
||||||
|
continueButton = <div className="mx_CreateSecretStorageDialog_continueSpinner">
|
||||||
|
<InlineSpinner />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
return <div>
|
return <div>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Your recovery key is a safety net - you can use it to restore " +
|
"Store your Security Key somewhere safe, like a password manager or a safe, " +
|
||||||
"access to your encrypted messages if you forget your recovery passphrase.",
|
"as it’s used to safeguard your encrypted data.",
|
||||||
)}</p>
|
|
||||||
<p>{_t(
|
|
||||||
"Keep a copy of it somewhere secure, like a password manager or even a safe.",
|
|
||||||
)}</p>
|
)}</p>
|
||||||
<div className="mx_CreateSecretStorageDialog_primaryContainer">
|
<div className="mx_CreateSecretStorageDialog_primaryContainer">
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyHeader">
|
|
||||||
{_t("Your recovery key")}
|
|
||||||
</div>
|
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
||||||
<code ref={this._collectRecoveryKeyNode}>{this._recoveryKey.encodedPrivateKey}</code>
|
<code ref={this._collectRecoveryKeyNode}>{this._recoveryKey.encodedPrivateKey}</code>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
||||||
|
<AccessibleButton kind='primary' className="mx_Dialog_primary"
|
||||||
|
onClick={this._onDownloadClick}
|
||||||
|
disabled={this.state.phase === PHASE_STORING}
|
||||||
|
>
|
||||||
|
{_t("Download")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<span>{_t("or")}</span>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
kind='primary'
|
kind='primary'
|
||||||
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
|
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
|
||||||
onClick={this._onCopyClick}
|
onClick={this._onCopyClick}
|
||||||
|
disabled={this.state.phase === PHASE_STORING}
|
||||||
>
|
>
|
||||||
{_t("Copy")}
|
{this.state.copied ? _t("Copied!") : _t("Copy")}
|
||||||
</AccessibleButton>
|
|
||||||
<AccessibleButton kind='primary' className="mx_Dialog_primary" onClick={this._onDownloadClick}>
|
|
||||||
{_t("Download")}
|
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
{continueButton}
|
||||||
}
|
|
||||||
|
|
||||||
_renderPhaseKeepItSafe() {
|
|
||||||
let introText;
|
|
||||||
if (this.state.copied) {
|
|
||||||
introText = _t(
|
|
||||||
"Your recovery key has been <b>copied to your clipboard</b>, paste it to:",
|
|
||||||
{}, {b: s => <b>{s}</b>},
|
|
||||||
);
|
|
||||||
} else if (this.state.downloaded) {
|
|
||||||
introText = _t(
|
|
||||||
"Your recovery key is in your <b>Downloads</b> folder.",
|
|
||||||
{}, {b: s => <b>{s}</b>},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <div>
|
|
||||||
{introText}
|
|
||||||
<ul>
|
|
||||||
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, {b: s => <b>{s}</b>})}</li>
|
|
||||||
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, {b: s => <b>{s}</b>})}</li>
|
|
||||||
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, {b: s => <b>{s}</b>})}</li>
|
|
||||||
</ul>
|
|
||||||
<DialogButtons primaryButton={_t("Continue")}
|
|
||||||
onPrimaryButtonClick={this._bootstrapSecretStorage}
|
|
||||||
hasCancel={false}>
|
|
||||||
<button onClick={this._onKeepItSafeBackClick}>{_t("Back")}</button>
|
|
||||||
</DialogButtons>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,7 +680,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPhaseLoadError() {
|
_renderPhaseLoadError() {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <div>
|
return <div>
|
||||||
<p>{_t("Unable to query secret storage status")}</p>
|
<p>{_t("Unable to query secret storage status")}</p>
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
|
@ -684,53 +692,39 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPhaseDone() {
|
_renderPhaseSkipConfirm() {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <div>
|
return <div>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"You can now verify your other devices, " +
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.",
|
||||||
"and other users to keep your chats safe.",
|
)}</p>
|
||||||
|
<p>{_t(
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.",
|
||||||
)}</p>
|
)}</p>
|
||||||
<DialogButtons primaryButton={_t('OK')}
|
|
||||||
onPrimaryButtonClick={this._onDone}
|
|
||||||
hasCancel={false}
|
|
||||||
/>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderPhaseSkipConfirm() {
|
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <div>
|
|
||||||
{_t(
|
|
||||||
"Without completing security on this session, it won’t have " +
|
|
||||||
"access to encrypted messages.",
|
|
||||||
)}
|
|
||||||
<DialogButtons primaryButton={_t('Go back')}
|
<DialogButtons primaryButton={_t('Go back')}
|
||||||
onPrimaryButtonClick={this._onSetUpClick}
|
onPrimaryButtonClick={this._onGoBackClick}
|
||||||
hasCancel={false}
|
hasCancel={false}
|
||||||
>
|
>
|
||||||
<button type="button" className="danger" onClick={this._onCancel}>{_t('Skip')}</button>
|
<button type="button" className="danger" onClick={this._onCancel}>{_t('Cancel')}</button>
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_titleForPhase(phase) {
|
_titleForPhase(phase) {
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
|
case PHASE_CHOOSE_KEY_PASSPHRASE:
|
||||||
|
return _t('Set up Secure backup');
|
||||||
case PHASE_MIGRATE:
|
case PHASE_MIGRATE:
|
||||||
return _t('Upgrade your encryption');
|
return _t('Upgrade your encryption');
|
||||||
case PHASE_PASSPHRASE:
|
case PHASE_PASSPHRASE:
|
||||||
return _t('Set up encryption');
|
return _t('Set a Security Phrase');
|
||||||
case PHASE_PASSPHRASE_CONFIRM:
|
case PHASE_PASSPHRASE_CONFIRM:
|
||||||
return _t('Confirm recovery passphrase');
|
return _t('Confirm Security Phrase');
|
||||||
case PHASE_CONFIRM_SKIP:
|
case PHASE_CONFIRM_SKIP:
|
||||||
return _t('Are you sure?');
|
return _t('Are you sure?');
|
||||||
case PHASE_SHOWKEY:
|
case PHASE_SHOWKEY:
|
||||||
case PHASE_KEEPITSAFE:
|
return _t('Save your Security Key');
|
||||||
return _t('Make a copy of your recovery key');
|
|
||||||
case PHASE_STORING:
|
case PHASE_STORING:
|
||||||
return _t('Setting up keys');
|
return _t('Setting up keys');
|
||||||
case PHASE_DONE:
|
|
||||||
return _t("You're done!");
|
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -741,7 +735,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
content = <div>
|
content = <div>
|
||||||
<p>{_t("Unable to set up secret storage")}</p>
|
<p>{_t("Unable to set up secret storage")}</p>
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
|
@ -760,6 +753,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
case PHASE_LOADERROR:
|
case PHASE_LOADERROR:
|
||||||
content = this._renderPhaseLoadError();
|
content = this._renderPhaseLoadError();
|
||||||
break;
|
break;
|
||||||
|
case PHASE_CHOOSE_KEY_PASSPHRASE:
|
||||||
|
content = this._renderPhaseChooseKeyPassphrase();
|
||||||
|
break;
|
||||||
case PHASE_MIGRATE:
|
case PHASE_MIGRATE:
|
||||||
content = this._renderPhaseMigrate();
|
content = this._renderPhaseMigrate();
|
||||||
break;
|
break;
|
||||||
|
@ -772,31 +768,40 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
case PHASE_SHOWKEY:
|
case PHASE_SHOWKEY:
|
||||||
content = this._renderPhaseShowKey();
|
content = this._renderPhaseShowKey();
|
||||||
break;
|
break;
|
||||||
case PHASE_KEEPITSAFE:
|
|
||||||
content = this._renderPhaseKeepItSafe();
|
|
||||||
break;
|
|
||||||
case PHASE_STORING:
|
case PHASE_STORING:
|
||||||
content = this._renderBusyPhase();
|
content = this._renderBusyPhase();
|
||||||
break;
|
break;
|
||||||
case PHASE_DONE:
|
|
||||||
content = this._renderPhaseDone();
|
|
||||||
break;
|
|
||||||
case PHASE_CONFIRM_SKIP:
|
case PHASE_CONFIRM_SKIP:
|
||||||
content = this._renderPhaseSkipConfirm();
|
content = this._renderPhaseSkipConfirm();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let headerImage;
|
let titleClass = null;
|
||||||
if (this._titleForPhase(this.state.phase)) {
|
switch (this.state.phase) {
|
||||||
headerImage = require("../../../../../res/img/e2e/normal.svg");
|
case PHASE_PASSPHRASE:
|
||||||
|
case PHASE_PASSPHRASE_CONFIRM:
|
||||||
|
titleClass = [
|
||||||
|
'mx_CreateSecretStorageDialog_titleWithIcon',
|
||||||
|
'mx_CreateSecretStorageDialog_securePhraseTitle',
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case PHASE_SHOWKEY:
|
||||||
|
titleClass = [
|
||||||
|
'mx_CreateSecretStorageDialog_titleWithIcon',
|
||||||
|
'mx_CreateSecretStorageDialog_secureBackupTitle',
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case PHASE_CHOOSE_KEY_PASSPHRASE:
|
||||||
|
titleClass = 'mx_CreateSecretStorageDialog_centeredTitle';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className='mx_CreateSecretStorageDialog'
|
<BaseDialog className='mx_CreateSecretStorageDialog'
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={this._titleForPhase(this.state.phase)}
|
title={this._titleForPhase(this.state.phase)}
|
||||||
headerImage={headerImage}
|
titleClass={titleClass}
|
||||||
hasCancel={this.props.hasCancel && [PHASE_PASSPHRASE].includes(this.state.phase)}
|
hasCancel={this.props.hasCancel && [PHASE_PASSPHRASE].includes(this.state.phase)}
|
||||||
fixedWidth={false}
|
fixedWidth={false}
|
||||||
>
|
>
|
||||||
|
|
|
@ -25,9 +25,9 @@ import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
import QueryMatcher from './QueryMatcher';
|
import QueryMatcher from './QueryMatcher';
|
||||||
import {PillCompletion} from './Components';
|
import {PillCompletion} from './Components';
|
||||||
import * as sdk from '../index';
|
import * as sdk from '../index';
|
||||||
import _sortBy from 'lodash/sortBy';
|
|
||||||
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
|
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
|
||||||
import {ICompletion, ISelectionRange} from "./Autocompleter";
|
import {ICompletion, ISelectionRange} from "./Autocompleter";
|
||||||
|
import { uniqBy, sortBy } from 'lodash';
|
||||||
|
|
||||||
const ROOM_REGEX = /\B#\S*/g;
|
const ROOM_REGEX = /\B#\S*/g;
|
||||||
|
|
||||||
|
@ -91,10 +91,11 @@ export default class RoomProvider extends AutocompleteProvider {
|
||||||
this.matcher.setObjects(matcherObjects);
|
this.matcher.setObjects(matcherObjects);
|
||||||
const matchedString = command[0];
|
const matchedString = command[0];
|
||||||
completions = this.matcher.match(matchedString);
|
completions = this.matcher.match(matchedString);
|
||||||
completions = _sortBy(completions, [
|
completions = sortBy(completions, [
|
||||||
(c) => score(matchedString, c.displayedAlias),
|
(c) => score(matchedString, c.displayedAlias),
|
||||||
(c) => c.displayedAlias.length,
|
(c) => c.displayedAlias.length,
|
||||||
]);
|
]);
|
||||||
|
completions = uniqBy(completions, (match) => match.room);
|
||||||
completions = completions.map((room) => {
|
completions = completions.map((room) => {
|
||||||
return {
|
return {
|
||||||
completion: room.displayedAlias,
|
completion: room.displayedAlias,
|
||||||
|
|
|
@ -205,6 +205,11 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
"mx_LeftPanel2_minimized": this.props.isMinimized,
|
"mx_LeftPanel2_minimized": this.props.isMinimized,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const roomListClasses = classNames(
|
||||||
|
"mx_LeftPanel2_actualRoomListContainer",
|
||||||
|
"mx_AutoHideScrollbar",
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
{tagPanel}
|
{tagPanel}
|
||||||
|
@ -212,7 +217,7 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
{this.renderSearchExplore()}
|
{this.renderSearchExplore()}
|
||||||
<div
|
<div
|
||||||
className="mx_LeftPanel2_actualRoomListContainer"
|
className={roomListClasses}
|
||||||
onScroll={this.onScroll}
|
onScroll={this.onScroll}
|
||||||
ref={this.listContainerRef}
|
ref={this.listContainerRef}
|
||||||
>{roomList}</div>
|
>{roomList}</div>
|
||||||
|
|
|
@ -1931,11 +1931,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
getFragmentAfterLogin() {
|
getFragmentAfterLogin() {
|
||||||
let fragmentAfterLogin = "";
|
let fragmentAfterLogin = "";
|
||||||
if (this.props.initialScreenAfterLogin &&
|
const initialScreenAfterLogin = this.props.initialScreenAfterLogin;
|
||||||
|
if (initialScreenAfterLogin &&
|
||||||
// XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop
|
// XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop
|
||||||
!["welcome", "login", "register"].includes(this.props.initialScreenAfterLogin.screen)
|
!["welcome", "login", "register", "start_sso", "start_cas"].includes(initialScreenAfterLogin.screen)
|
||||||
) {
|
) {
|
||||||
fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`;
|
fragmentAfterLogin = `/${initialScreenAfterLogin.screen}`;
|
||||||
}
|
}
|
||||||
return fragmentAfterLogin;
|
return fragmentAfterLogin;
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,8 +388,11 @@ export default class MessagePanel extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={"readMarker_"+eventId} ref={this._readMarkerNode}
|
<li key={"readMarker_"+eventId}
|
||||||
className="mx_RoomView_myReadMarker_container">
|
ref={this._readMarkerNode}
|
||||||
|
className="mx_RoomView_myReadMarker_container"
|
||||||
|
data-scroll-tokens={eventId}
|
||||||
|
>
|
||||||
{ hr }
|
{ hr }
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
|
@ -191,12 +191,10 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
let homeButton = null;
|
let homeButton = null;
|
||||||
if (this.hasHomePage) {
|
if (this.hasHomePage) {
|
||||||
homeButton = (
|
homeButton = (
|
||||||
<li>
|
<AccessibleButton onClick={this.onHomeClick}>
|
||||||
<AccessibleButton onClick={this.onHomeClick}>
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconHome" />
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconHome" />
|
<span>{_t("Home")}</span>
|
||||||
<span>{_t("Home")}</span>
|
</AccessibleButton>
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +202,8 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
return (
|
return (
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
chevronFace="none"
|
chevronFace="none"
|
||||||
left={elementRect.width + elementRect.left}
|
// -20 to overlap the context menu by just over the width of the `...` icon and make it look connected
|
||||||
|
left={elementRect.width + elementRect.left - 20}
|
||||||
top={elementRect.top + elementRect.height}
|
top={elementRect.top + elementRect.height}
|
||||||
onFinished={this.onCloseMenu}
|
onFinished={this.onCloseMenu}
|
||||||
>
|
>
|
||||||
|
@ -232,49 +231,33 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
</div>
|
</div>
|
||||||
{hostingLink}
|
{hostingLink}
|
||||||
<div className="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst">
|
<div className="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst">
|
||||||
<ul>
|
{homeButton}
|
||||||
{homeButton}
|
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}>
|
||||||
<li>
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconBell" />
|
||||||
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}>
|
<span className="mx_IconizedContextMenu_label">{_t("Notification settings")}</span>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconBell" />
|
</AccessibleButton>
|
||||||
<span>{_t("Notification settings")}</span>
|
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}>
|
||||||
</AccessibleButton>
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconLock" />
|
||||||
</li>
|
<span className="mx_IconizedContextMenu_label">{_t("Security & privacy")}</span>
|
||||||
<li>
|
</AccessibleButton>
|
||||||
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}>
|
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, null)}>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconLock" />
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSettings" />
|
||||||
<span>{_t("Security & privacy")}</span>
|
<span className="mx_IconizedContextMenu_label">{_t("All settings")}</span>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</li>
|
<AccessibleButton onClick={this.onShowArchived}>
|
||||||
<li>
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconArchive" />
|
||||||
<AccessibleButton onClick={(e) => this.onSettingsOpen(e, null)}>
|
<span className="mx_IconizedContextMenu_label">{_t("Archived rooms")}</span>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSettings" />
|
</AccessibleButton>
|
||||||
<span>{_t("All settings")}</span>
|
<AccessibleButton onClick={this.onProvideFeedback}>
|
||||||
</AccessibleButton>
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconMessage" />
|
||||||
</li>
|
<span className="mx_IconizedContextMenu_label">{_t("Feedback")}</span>
|
||||||
<li>
|
</AccessibleButton>
|
||||||
<AccessibleButton onClick={this.onShowArchived}>
|
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconArchive" />
|
|
||||||
<span>{_t("Archived rooms")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<AccessibleButton onClick={this.onProvideFeedback}>
|
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconMessage" />
|
|
||||||
<span>{_t("Feedback")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_IconizedContextMenu_optionList">
|
<div className="mx_IconizedContextMenu_optionList mx_UserMenu_contextMenu_redRow">
|
||||||
<ul>
|
<AccessibleButton onClick={this.onSignOutClick}>
|
||||||
<li className="mx_UserMenu_contextMenu_redRow">
|
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSignOut" />
|
||||||
<AccessibleButton onClick={this.onSignOutClick}>
|
<span className="mx_IconizedContextMenu_label">{_t("Sign out")}</span>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSignOut" />
|
</AccessibleButton>
|
||||||
<span>{_t("Sign out")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
|
|
|
@ -75,8 +75,12 @@ export default createReactClass({
|
||||||
// If provided, this is used to add a aria-describedby attribute
|
// If provided, this is used to add a aria-describedby attribute
|
||||||
contentId: PropTypes.string,
|
contentId: PropTypes.string,
|
||||||
|
|
||||||
// optional additional class for the title element
|
// optional additional class for the title element (basically anything that can be passed to classnames)
|
||||||
titleClass: PropTypes.string,
|
titleClass: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.object,
|
||||||
|
PropTypes.arrayOf(PropTypes.string),
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -15,13 +15,24 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import * as sdk from '../../../../index';
|
import * as sdk from '../../../../index';
|
||||||
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||||
|
import Field from '../../elements/Field';
|
||||||
|
import AccessibleButton from '../../elements/AccessibleButton';
|
||||||
|
|
||||||
import { _t } from '../../../../languageHandler';
|
import { _t } from '../../../../languageHandler';
|
||||||
import { accessSecretStorage } from '../../../../CrossSigningManager';
|
|
||||||
|
// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
|
||||||
|
// so this should be plenty and allow for people putting extra whitespace in the file because
|
||||||
|
// maybe that's a thing people would do?
|
||||||
|
const KEY_FILE_MAX_SIZE = 128;
|
||||||
|
|
||||||
|
// Don't shout at the user that their key is invalid every time they type a key: wait a short time
|
||||||
|
const VALIDATION_THROTTLE_MS = 200;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Access Secure Secret Storage by requesting the user's passphrase.
|
* Access Secure Secret Storage by requesting the user's passphrase.
|
||||||
|
@ -36,9 +47,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this._fileUpload = React.createRef();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
recoveryKey: "",
|
recoveryKey: "",
|
||||||
recoveryKeyValid: false,
|
recoveryKeyValid: null,
|
||||||
|
recoveryKeyCorrect: null,
|
||||||
|
recoveryKeyFileError: null,
|
||||||
forceRecoveryKey: false,
|
forceRecoveryKey: false,
|
||||||
passPhrase: '',
|
passPhrase: '',
|
||||||
keyMatches: null,
|
keyMatches: null,
|
||||||
|
@ -55,18 +71,89 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onResetRecoveryClick = () => {
|
_validateRecoveryKeyOnChange = debounce(() => {
|
||||||
// Re-enter the access flow, but resetting storage this time around.
|
this._validateRecoveryKey();
|
||||||
this.props.onFinished(false);
|
}, VALIDATION_THROTTLE_MS);
|
||||||
accessSecretStorage(() => {}, /* forceReset = */ true);
|
|
||||||
|
async _validateRecoveryKey() {
|
||||||
|
if (this.state.recoveryKey === '') {
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyValid: null,
|
||||||
|
recoveryKeyCorrect: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
const decodedKey = cli.keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||||
|
const correct = await cli.checkSecretStorageKey(
|
||||||
|
decodedKey, this.props.keyInfo,
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyValid: true,
|
||||||
|
recoveryKeyCorrect: correct,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyValid: false,
|
||||||
|
recoveryKeyCorrect: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRecoveryKeyChange = (e) => {
|
_onRecoveryKeyChange = (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
recoveryKey: e.target.value,
|
recoveryKey: e.target.value,
|
||||||
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
|
recoveryKeyFileError: null,
|
||||||
keyMatches: null,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// also clear the file upload control so that the user can upload the same file
|
||||||
|
// the did before (otherwise the onchange wouldn't fire)
|
||||||
|
if (this._fileUpload.current) this._fileUpload.current.value = null;
|
||||||
|
|
||||||
|
// We don't use Field's validation here because a) we want it in a separate place rather
|
||||||
|
// than in a tooltip and b) we want it to display feedback based on the uploaded file
|
||||||
|
// as well as the text box. Ideally we would refactor Field's validation logic so we could
|
||||||
|
// re-use some of it.
|
||||||
|
this._validateRecoveryKeyOnChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRecoveryKeyFileChange = async e => {
|
||||||
|
if (e.target.files.length === 0) return;
|
||||||
|
|
||||||
|
const f = e.target.files[0];
|
||||||
|
|
||||||
|
if (f.size > KEY_FILE_MAX_SIZE) {
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyFileError: true,
|
||||||
|
recoveryKeyCorrect: false,
|
||||||
|
recoveryKeyValid: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const contents = await f.text();
|
||||||
|
// test it's within the base58 alphabet. We could be more strict here, eg. require the
|
||||||
|
// right number of characters, but it's really just to make sure that what we're reading is
|
||||||
|
// text because we'll put it in the text field.
|
||||||
|
if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\s]+$/.test(contents)) {
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyFileError: null,
|
||||||
|
recoveryKey: contents.trim(),
|
||||||
|
});
|
||||||
|
this._validateRecoveryKey();
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
recoveryKeyFileError: true,
|
||||||
|
recoveryKeyCorrect: false,
|
||||||
|
recoveryKeyValid: false,
|
||||||
|
recoveryKey: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRecoveryKeyFileUploadClick = () => {
|
||||||
|
this._fileUpload.current.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPassPhraseNext = async (e) => {
|
_onPassPhraseNext = async (e) => {
|
||||||
|
@ -106,6 +193,20 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKeyValidationText() {
|
||||||
|
if (this.state.recoveryKeyFileError) {
|
||||||
|
return _t("Wrong file type");
|
||||||
|
} else if (this.state.recoveryKeyCorrect) {
|
||||||
|
return _t("Looks good!");
|
||||||
|
} else if (this.state.recoveryKeyValid) {
|
||||||
|
return _t("Wrong Recovery Key");
|
||||||
|
} else if (this.state.recoveryKeyValid === null) {
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
return _t("Invalid Recovery Key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
|
@ -118,10 +219,12 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
let title;
|
let title;
|
||||||
|
let titleClass;
|
||||||
if (hasPassphrase && !this.state.forceRecoveryKey) {
|
if (hasPassphrase && !this.state.forceRecoveryKey) {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
title = _t("Enter recovery passphrase");
|
title = _t("Security Phrase");
|
||||||
|
titleClass = ['mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_securePhraseTitle'];
|
||||||
|
|
||||||
let keyStatus;
|
let keyStatus;
|
||||||
if (this.state.keyMatches === false) {
|
if (this.state.keyMatches === false) {
|
||||||
|
@ -137,12 +240,15 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
content = <div>
|
content = <div>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"<b>Warning</b>: You should only do this on a trusted computer.", {},
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.", {},
|
||||||
{ b: sub => <b>{sub}</b> },
|
{
|
||||||
)}</p>
|
button: s => <AccessibleButton className="mx_linkButton"
|
||||||
<p>{_t(
|
element="span"
|
||||||
"Access your secure message history and your cross-signing " +
|
onClick={this._onUseRecoveryKeyClick}
|
||||||
"identity for verifying other sessions by entering your recovery passphrase.",
|
>
|
||||||
|
{s}
|
||||||
|
</AccessibleButton>,
|
||||||
|
},
|
||||||
)}</p>
|
)}</p>
|
||||||
|
|
||||||
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onPassPhraseNext}>
|
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onPassPhraseNext}>
|
||||||
|
@ -153,10 +259,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
value={this.state.passPhrase}
|
value={this.state.passPhrase}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
|
placeholder={_t("Security Phrase")}
|
||||||
/>
|
/>
|
||||||
{keyStatus}
|
{keyStatus}
|
||||||
<DialogButtons
|
<DialogButtons
|
||||||
primaryButton={_t('Next')}
|
primaryButton={_t('Continue')}
|
||||||
onPrimaryButtonClick={this._onPassPhraseNext}
|
onPrimaryButtonClick={this._onPassPhraseNext}
|
||||||
hasCancel={true}
|
hasCancel={true}
|
||||||
onCancel={this._onCancel}
|
onCancel={this._onCancel}
|
||||||
|
@ -164,87 +271,61 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
primaryDisabled={this.state.passPhrase.length === 0}
|
primaryDisabled={this.state.passPhrase.length === 0}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
{_t(
|
|
||||||
"If you've forgotten your recovery passphrase you can "+
|
|
||||||
"<button1>use your recovery key</button1> or " +
|
|
||||||
"<button2>set up new recovery options</button2>."
|
|
||||||
, {}, {
|
|
||||||
button1: s => <AccessibleButton className="mx_linkButton"
|
|
||||||
element="span"
|
|
||||||
onClick={this._onUseRecoveryKeyClick}
|
|
||||||
>
|
|
||||||
{s}
|
|
||||||
</AccessibleButton>,
|
|
||||||
button2: s => <AccessibleButton className="mx_linkButton"
|
|
||||||
element="span"
|
|
||||||
onClick={this._onResetRecoveryClick}
|
|
||||||
>
|
|
||||||
{s}
|
|
||||||
</AccessibleButton>,
|
|
||||||
})}
|
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
title = _t("Enter recovery key");
|
title = _t("Security Key");
|
||||||
|
titleClass = ['mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_secureBackupTitle'];
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
||||||
|
|
||||||
let keyStatus;
|
const feedbackClasses = classNames({
|
||||||
if (this.state.recoveryKey.length === 0) {
|
'mx_AccessSecretStorageDialog_recoveryKeyFeedback': true,
|
||||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
|
'mx_AccessSecretStorageDialog_recoveryKeyFeedback_valid': this.state.recoveryKeyCorrect === true,
|
||||||
} else if (this.state.keyMatches === false) {
|
'mx_AccessSecretStorageDialog_recoveryKeyFeedback_invalid': this.state.recoveryKeyCorrect === false,
|
||||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
});
|
||||||
{"\uD83D\uDC4E "}{_t(
|
const recoveryKeyFeedback = <div className={feedbackClasses}>
|
||||||
"Unable to access secret storage. " +
|
{this.getKeyValidationText()}
|
||||||
"Please verify that you entered the correct recovery key.",
|
</div>;
|
||||||
)}
|
|
||||||
</div>;
|
|
||||||
} else if (this.state.recoveryKeyValid) {
|
|
||||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
|
||||||
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
|
|
||||||
</div>;
|
|
||||||
} else {
|
|
||||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
|
||||||
{"\uD83D\uDC4E "}{_t("Not a valid recovery key")}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
content = <div>
|
content = <div>
|
||||||
<p>{_t(
|
<p>{_t("Use your Security Key to continue.")}</p>
|
||||||
"<b>Warning</b>: You should only do this on a trusted computer.", {},
|
|
||||||
{ b: sub => <b>{sub}</b> },
|
|
||||||
)}</p>
|
|
||||||
<p>{_t(
|
|
||||||
"Access your secure message history and your cross-signing " +
|
|
||||||
"identity for verifying other sessions by entering your recovery key.",
|
|
||||||
)}</p>
|
|
||||||
|
|
||||||
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onRecoveryKeyNext}>
|
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onRecoveryKeyNext} spellCheck={false}>
|
||||||
<input className="mx_AccessSecretStorageDialog_recoveryKeyInput"
|
<div className="mx_AccessSecretStorageDialog_recoveryKeyEntry">
|
||||||
onChange={this._onRecoveryKeyChange}
|
<div className="mx_AccessSecretStorageDialog_recoveryKeyEntry_textInput">
|
||||||
value={this.state.recoveryKey}
|
<Field
|
||||||
autoFocus={true}
|
type="text"
|
||||||
/>
|
label={_t('Security Key')}
|
||||||
{keyStatus}
|
value={this.state.recoveryKey}
|
||||||
|
onChange={this._onRecoveryKeyChange}
|
||||||
|
forceValidity={this.state.recoveryKeyCorrect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText">
|
||||||
|
{_t("or")}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<input type="file"
|
||||||
|
className="mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
|
||||||
|
ref={this._fileUpload}
|
||||||
|
onChange={this._onRecoveryKeyFileChange}
|
||||||
|
/>
|
||||||
|
<AccessibleButton kind="primary" onClick={this._onRecoveryKeyFileUploadClick}>
|
||||||
|
{_t("Upload")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{recoveryKeyFeedback}
|
||||||
<DialogButtons
|
<DialogButtons
|
||||||
primaryButton={_t('Next')}
|
primaryButton={_t('Continue')}
|
||||||
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
||||||
hasCancel={true}
|
hasCancel={true}
|
||||||
|
cancelButton={_t("Go Back")}
|
||||||
|
cancelButtonClass='danger'
|
||||||
onCancel={this._onCancel}
|
onCancel={this._onCancel}
|
||||||
focus={false}
|
focus={false}
|
||||||
primaryDisabled={!this.state.recoveryKeyValid}
|
primaryDisabled={!this.state.recoveryKeyValid}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
{_t(
|
|
||||||
"If you've forgotten your recovery key you can "+
|
|
||||||
"<button>set up new recovery options</button>."
|
|
||||||
, {}, {
|
|
||||||
button: s => <AccessibleButton className="mx_linkButton"
|
|
||||||
element="span"
|
|
||||||
onClick={this._onResetRecoveryClick}
|
|
||||||
>
|
|
||||||
{s}
|
|
||||||
</AccessibleButton>,
|
|
||||||
})}
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,6 +333,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||||
<BaseDialog className='mx_AccessSecretStorageDialog'
|
<BaseDialog className='mx_AccessSecretStorageDialog'
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={title}
|
title={title}
|
||||||
|
titleClass={titleClass}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{content}
|
{content}
|
||||||
|
|
|
@ -50,7 +50,7 @@ interface IProps {
|
||||||
// to the user.
|
// to the user.
|
||||||
onValidate?: (input: IFieldState) => Promise<IValidationResult>;
|
onValidate?: (input: IFieldState) => Promise<IValidationResult>;
|
||||||
// If specified, overrides the value returned by onValidate.
|
// If specified, overrides the value returned by onValidate.
|
||||||
flagInvalid?: boolean;
|
forceValidity?: boolean;
|
||||||
// If specified, contents will appear as a tooltip on the element and
|
// If specified, contents will appear as a tooltip on the element and
|
||||||
// validation feedback tooltips will be suppressed.
|
// validation feedback tooltips will be suppressed.
|
||||||
tooltipContent?: React.ReactNode;
|
tooltipContent?: React.ReactNode;
|
||||||
|
@ -203,7 +203,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
element, prefixComponent, postfixComponent, className, onValidate, children,
|
element, prefixComponent, postfixComponent, className, onValidate, children,
|
||||||
tooltipContent, flagInvalid, tooltipClassName, list, ...inputProps} = this.props;
|
tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props;
|
||||||
|
|
||||||
// Set some defaults for the <input> element
|
// Set some defaults for the <input> element
|
||||||
const ref = input => this.input = input;
|
const ref = input => this.input = input;
|
||||||
|
@ -228,15 +228,15 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
postfixContainer = <span className="mx_Field_postfix">{postfixComponent}</span>;
|
postfixContainer = <span className="mx_Field_postfix">{postfixComponent}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasValidationFlag = flagInvalid !== null && flagInvalid !== undefined;
|
const hasValidationFlag = forceValidity !== null && forceValidity !== undefined;
|
||||||
const fieldClasses = classNames("mx_Field", `mx_Field_${this.props.element}`, className, {
|
const fieldClasses = classNames("mx_Field", `mx_Field_${this.props.element}`, className, {
|
||||||
// If we have a prefix element, leave the label always at the top left and
|
// If we have a prefix element, leave the label always at the top left and
|
||||||
// don't animate it, as it looks a bit clunky and would add complexity to do
|
// don't animate it, as it looks a bit clunky and would add complexity to do
|
||||||
// properly.
|
// properly.
|
||||||
mx_Field_labelAlwaysTopLeft: prefixComponent,
|
mx_Field_labelAlwaysTopLeft: prefixComponent,
|
||||||
mx_Field_valid: onValidate && this.state.valid === true,
|
mx_Field_valid: hasValidationFlag ? forceValidity : onValidate && this.state.valid === true,
|
||||||
mx_Field_invalid: hasValidationFlag
|
mx_Field_invalid: hasValidationFlag
|
||||||
? flagInvalid
|
? !forceValidity
|
||||||
: onValidate && this.state.valid === false,
|
: onValidate && this.state.valid === false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'ProgressBar',
|
|
||||||
propTypes: {
|
|
||||||
value: PropTypes.number,
|
|
||||||
max: PropTypes.number,
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
// Would use an HTML5 progress tag but if that doesn't animate if you
|
|
||||||
// use the HTML attributes rather than styles
|
|
||||||
const progressStyle = {
|
|
||||||
width: ((this.props.value / this.props.max) * 100)+"%",
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
28
src/components/views/elements/ProgressBar.tsx
Normal file
28
src/components/views/elements/ProgressBar.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
value: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProgressBar: React.FC<IProps> = ({value, max}) => {
|
||||||
|
return <progress className="mx_ProgressBar" max={max} value={value} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProgressBar;
|
|
@ -42,7 +42,7 @@ export default class StyledRadioButton extends React.PureComponent<IProps, IStat
|
||||||
<input type='radio' disabled={disabled} {...otherProps} />
|
<input type='radio' disabled={disabled} {...otherProps} />
|
||||||
{/* Used to render the radio button circle */}
|
{/* Used to render the radio button circle */}
|
||||||
<div><div></div></div>
|
<div><div></div></div>
|
||||||
<span>{children}</span>
|
<div className="mx_RadioButton_content">{children}</div>
|
||||||
<div className="mx_RadioButton_spacer" />
|
<div className="mx_RadioButton_spacer" />
|
||||||
</label>;
|
</label>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,8 @@ function StyledRadioGroup<T extends string>({name, definitions, value, className
|
||||||
};
|
};
|
||||||
|
|
||||||
return <React.Fragment>
|
return <React.Fragment>
|
||||||
{definitions.map(d => <React.Fragment>
|
{definitions.map(d => <React.Fragment key={d.value}>
|
||||||
<StyledRadioButton
|
<StyledRadioButton
|
||||||
key={d.value}
|
|
||||||
className={classNames(className, d.className)}
|
className={classNames(className, d.className)}
|
||||||
onChange={_onChange}
|
onChange={_onChange}
|
||||||
checked={d.value === value}
|
checked={d.value === value}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
import StyledRadioButton from "../elements/StyledRadioButton";
|
import StyledRadioButton from "../elements/StyledRadioButton";
|
||||||
import RoomListStore from "../../../stores/room-list/RoomListStore2";
|
import RoomListStore from "../../../stores/room-list/RoomListStore2";
|
||||||
import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorithms/models";
|
import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorithms/models";
|
||||||
import { TagID } from "../../../stores/room-list/models";
|
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||||
|
|
||||||
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
|
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
|
||||||
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
|
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
|
||||||
|
@ -91,6 +91,12 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
return (this.props.rooms || []).length;
|
return (this.props.rooms || []).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get numVisibleTiles(): number {
|
||||||
|
if (!this.props.layout) return 0;
|
||||||
|
const nVisible = Math.floor(this.props.layout.visibleTiles);
|
||||||
|
return Math.min(nVisible, this.numTiles);
|
||||||
|
}
|
||||||
|
|
||||||
public componentDidUpdate() {
|
public componentDidUpdate() {
|
||||||
this.state.notificationState.setRooms(this.props.rooms);
|
this.state.notificationState.setRooms(this.props.rooms);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +113,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => {
|
private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => {
|
||||||
const direction = e.movementY < 0 ? -1 : +1;
|
const direction = e.movementY < 0 ? -1 : +1;
|
||||||
const tileDiff = this.props.layout.pixelsToTiles(Math.abs(e.movementY)) * direction;
|
const tileDiff = this.props.layout.pixelsToTiles(Math.abs(e.movementY)) * direction;
|
||||||
this.props.layout.visibleTiles += tileDiff;
|
this.props.layout.setVisibleTilesWithin(tileDiff, this.numTiles);
|
||||||
this.forceUpdate(); // because the layout doesn't trigger a re-render
|
this.forceUpdate(); // because the layout doesn't trigger a re-render
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,13 +179,17 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderTiles(): React.ReactElement[] {
|
private renderVisibleTiles(): React.ReactElement[] {
|
||||||
if (this.props.layout && this.props.layout.isCollapsed) return []; // don't waste time on rendering
|
if (this.props.layout && this.props.layout.isCollapsed) {
|
||||||
|
// don't waste time on rendering
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const tiles: React.ReactElement[] = [];
|
const tiles: React.ReactElement[] = [];
|
||||||
|
|
||||||
if (this.props.rooms) {
|
if (this.props.rooms) {
|
||||||
for (const room of this.props.rooms) {
|
const visibleRooms = this.props.rooms.slice(0, this.numVisibleTiles);
|
||||||
|
for (const room of visibleRooms) {
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<RoomTile2
|
<RoomTile2
|
||||||
room={room}
|
room={room}
|
||||||
|
@ -196,6 +206,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderMenu(): React.ReactElement {
|
private renderMenu(): React.ReactElement {
|
||||||
|
// TODO: Get a proper invite context menu, or take invites out of the room list.
|
||||||
|
if (this.props.tagId === DefaultTagID.Invite) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let contextMenu = null;
|
let contextMenu = null;
|
||||||
if (this.state.menuDisplayed) {
|
if (this.state.menuDisplayed) {
|
||||||
const elementRect = this.menuButtonRef.current.getBoundingClientRect();
|
const elementRect = this.menuButtonRef.current.getBoundingClientRect();
|
||||||
|
@ -338,7 +353,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
public render(): React.ReactElement {
|
public render(): React.ReactElement {
|
||||||
// TODO: Error boundary: https://github.com/vector-im/riot-web/issues/14185
|
// TODO: Error boundary: https://github.com/vector-im/riot-web/issues/14185
|
||||||
|
|
||||||
const tiles = this.renderTiles();
|
const visibleTiles = this.renderVisibleTiles();
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'mx_RoomSublist2': true,
|
'mx_RoomSublist2': true,
|
||||||
|
@ -347,13 +362,10 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let content = null;
|
let content = null;
|
||||||
if (tiles.length > 0) {
|
if (visibleTiles.length > 0) {
|
||||||
const layout = this.props.layout; // to shorten calls
|
const layout = this.props.layout; // to shorten calls
|
||||||
|
|
||||||
const nVisible = Math.floor(layout.visibleTiles);
|
const maxTilesFactored = layout.tilesWithResizerBoxFactor(this.numTiles);
|
||||||
const visibleTiles = tiles.slice(0, nVisible);
|
|
||||||
|
|
||||||
const maxTilesFactored = layout.tilesWithResizerBoxFactor(tiles.length);
|
|
||||||
const showMoreBtnClasses = classNames({
|
const showMoreBtnClasses = classNames({
|
||||||
'mx_RoomSublist2_showNButton': true,
|
'mx_RoomSublist2_showNButton': true,
|
||||||
'mx_RoomSublist2_isCutting': this.state.isResizing && layout.visibleTiles < maxTilesFactored,
|
'mx_RoomSublist2_isCutting': this.state.isResizing && layout.visibleTiles < maxTilesFactored,
|
||||||
|
@ -363,9 +375,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
// floats above the resize handle, if we have one present. If the user has all
|
// floats above the resize handle, if we have one present. If the user has all
|
||||||
// tiles visible, it becomes 'show less'.
|
// tiles visible, it becomes 'show less'.
|
||||||
let showNButton = null;
|
let showNButton = null;
|
||||||
if (tiles.length > nVisible) {
|
if (this.numTiles > visibleTiles.length) {
|
||||||
// we have a cutoff condition - add the button to show all
|
// we have a cutoff condition - add the button to show all
|
||||||
const numMissing = tiles.length - visibleTiles.length;
|
const numMissing = this.numTiles - visibleTiles.length;
|
||||||
let showMoreText = (
|
let showMoreText = (
|
||||||
<span className='mx_RoomSublist2_showNButtonText'>
|
<span className='mx_RoomSublist2_showNButtonText'>
|
||||||
{_t("Show %(count)s more", {count: numMissing})}
|
{_t("Show %(count)s more", {count: numMissing})}
|
||||||
|
@ -380,7 +392,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
{showMoreText}
|
{showMoreText}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (tiles.length <= nVisible && tiles.length > this.props.layout.defaultVisibleTiles) {
|
} else if (this.numTiles <= visibleTiles.length && this.numTiles > this.props.layout.defaultVisibleTiles) {
|
||||||
// we have all tiles visible - add a button to show less
|
// we have all tiles visible - add a button to show less
|
||||||
let showLessText = (
|
let showLessText = (
|
||||||
<span className='mx_RoomSublist2_showNButtonText'>
|
<span className='mx_RoomSublist2_showNButtonText'>
|
||||||
|
@ -400,7 +412,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
// Figure out if we need a handle
|
// Figure out if we need a handle
|
||||||
let handles = ['s'];
|
let handles = ['s'];
|
||||||
if (layout.visibleTiles >= tiles.length && tiles.length <= layout.minVisibleTiles) {
|
if (layout.visibleTiles >= this.numTiles && this.numTiles <= layout.minVisibleTiles) {
|
||||||
handles = []; // no handles, we're at a minimum
|
handles = []; // no handles, we're at a minimum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,9 +431,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
if (showNButton) padding += SHOW_N_BUTTON_HEIGHT;
|
if (showNButton) padding += SHOW_N_BUTTON_HEIGHT;
|
||||||
padding += RESIZE_HANDLE_HEIGHT; // always append the handle height
|
padding += RESIZE_HANDLE_HEIGHT; // always append the handle height
|
||||||
|
|
||||||
const relativeTiles = layout.tilesWithPadding(tiles.length, padding);
|
const relativeTiles = layout.tilesWithPadding(this.numTiles, padding);
|
||||||
const minTilesPx = layout.calculateTilesToPixelsMin(relativeTiles, layout.minVisibleTiles, padding);
|
const minTilesPx = layout.calculateTilesToPixelsMin(relativeTiles, layout.minVisibleTiles, padding);
|
||||||
const maxTilesPx = layout.tilesToPixelsWithPadding(tiles.length, padding);
|
const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, padding);
|
||||||
const tilesWithoutPadding = Math.min(relativeTiles, layout.visibleTiles);
|
const tilesWithoutPadding = Math.min(relativeTiles, layout.visibleTiles);
|
||||||
const tilesPx = layout.calculateTilesToPixelsMin(relativeTiles, tilesWithoutPadding, padding);
|
const tilesPx = layout.calculateTilesToPixelsMin(relativeTiles, tilesWithoutPadding, padding);
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,13 @@ import NotificationBadge, {
|
||||||
TagSpecificNotificationState
|
TagSpecificNotificationState
|
||||||
} from "./NotificationBadge";
|
} from "./NotificationBadge";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { ContextMenu, ContextMenuButton } from "../../structures/ContextMenu";
|
import { ContextMenu, ContextMenuButton, MenuItemRadio } from "../../structures/ContextMenu";
|
||||||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||||
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
||||||
import RoomTileIcon from "./RoomTileIcon";
|
import RoomTileIcon from "./RoomTileIcon";
|
||||||
|
import { getRoomNotifsState, ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
|
||||||
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
|
import { setRoomNotifsState } from "../../../RoomNotifs";
|
||||||
|
|
||||||
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
|
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
|
||||||
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
|
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
|
||||||
|
@ -61,11 +64,46 @@ interface IState {
|
||||||
hover: boolean;
|
hover: boolean;
|
||||||
notificationState: INotificationState;
|
notificationState: INotificationState;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
notificationsMenuDisplayed: boolean;
|
||||||
generalMenuDisplayed: boolean;
|
generalMenuDisplayed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contextMenuBelow = (elementRect) => {
|
||||||
|
// align the context menu's icons with the icon which opened the context menu
|
||||||
|
const left = elementRect.left + window.pageXOffset - 9;
|
||||||
|
let top = elementRect.bottom + window.pageYOffset + 17;
|
||||||
|
const chevronFace = "none";
|
||||||
|
return {left, top, chevronFace};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface INotifOptionProps {
|
||||||
|
active: boolean;
|
||||||
|
iconClassName: string;
|
||||||
|
label: string;
|
||||||
|
onClick(ev: ButtonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotifOption: React.FC<INotifOptionProps> = ({active, onClick, iconClassName, label}) => {
|
||||||
|
const classes = classNames({
|
||||||
|
mx_RoomTile2_contextMenu_activeRow: active,
|
||||||
|
});
|
||||||
|
|
||||||
|
let activeIcon;
|
||||||
|
if (active) {
|
||||||
|
activeIcon = <span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconCheck" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItemRadio className={classes} onClick={onClick} active={active} label={label}>
|
||||||
|
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||||
|
<span className="mx_IconizedContextMenu_label">{ label }</span>
|
||||||
|
{ activeIcon }
|
||||||
|
</MenuItemRadio>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default class RoomTile2 extends React.Component<IProps, IState> {
|
export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
private roomTileRef: React.RefObject<HTMLDivElement> = createRef();
|
private notificationsMenuButtonRef: React.RefObject<HTMLButtonElement> = createRef();
|
||||||
private generalMenuButtonRef: React.RefObject<HTMLButtonElement> = createRef();
|
private generalMenuButtonRef: React.RefObject<HTMLButtonElement> = createRef();
|
||||||
|
|
||||||
// TODO: a11y: https://github.com/vector-im/riot-web/issues/14180
|
// TODO: a11y: https://github.com/vector-im/riot-web/issues/14180
|
||||||
|
@ -77,6 +115,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
hover: false,
|
hover: false,
|
||||||
notificationState: new TagSpecificNotificationState(this.props.room, this.props.tag),
|
notificationState: new TagSpecificNotificationState(this.props.room, this.props.tag),
|
||||||
selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId,
|
selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId,
|
||||||
|
notificationsMenuDisplayed: false,
|
||||||
generalMenuDisplayed: false,
|
generalMenuDisplayed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +150,18 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
this.setState({selected: isActive});
|
this.setState({selected: isActive});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onNotificationsMenuOpenClick = (ev: InputEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({notificationsMenuDisplayed: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
private onCloseNotificationsMenu = (ev: InputEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({notificationsMenuDisplayed: false});
|
||||||
|
};
|
||||||
|
|
||||||
private onGeneralMenuOpenClick = (ev: InputEvent) => {
|
private onGeneralMenuOpenClick = (ev: InputEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
@ -153,55 +204,129 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
this.setState({generalMenuDisplayed: false}); // hide the menu
|
this.setState({generalMenuDisplayed: false}); // hide the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async saveNotifState(ev: ButtonEvent, newState: ALL_MESSAGES_LOUD | ALL_MESSAGES | MENTIONS_ONLY | MUTE) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (MatrixClientPeg.get().isGuest()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO add local echo - https://github.com/vector-im/riot-web/issues/14280
|
||||||
|
await setRoomNotifsState(this.props.room.roomId, newState);
|
||||||
|
} catch (error) {
|
||||||
|
// TODO: some form of error notification to the user to inform them that their state change failed.
|
||||||
|
// https://github.com/vector-im/riot-web/issues/14281
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the context menu
|
||||||
|
this.setState({
|
||||||
|
notificationsMenuDisplayed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClickAllNotifs = ev => this.saveNotifState(ev, ALL_MESSAGES);
|
||||||
|
private onClickAlertMe = ev => this.saveNotifState(ev, ALL_MESSAGES_LOUD);
|
||||||
|
private onClickMentions = ev => this.saveNotifState(ev, MENTIONS_ONLY);
|
||||||
|
private onClickMute = ev => this.saveNotifState(ev, MUTE);
|
||||||
|
|
||||||
|
private renderNotificationsMenu(): React.ReactElement {
|
||||||
|
if (this.props.isMinimized || MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Invite) {
|
||||||
|
// the menu makes no sense in these cases so do not show one
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getRoomNotifsState(this.props.room.roomId);
|
||||||
|
|
||||||
|
let contextMenu = null;
|
||||||
|
if (this.state.notificationsMenuDisplayed) {
|
||||||
|
const elementRect = this.notificationsMenuButtonRef.current.getBoundingClientRect();
|
||||||
|
contextMenu = (
|
||||||
|
<ContextMenu {...contextMenuBelow(elementRect)} onFinished={this.onCloseNotificationsMenu}>
|
||||||
|
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile2_contextMenu">
|
||||||
|
<div className="mx_IconizedContextMenu_optionList">
|
||||||
|
<NotifOption
|
||||||
|
label={_t("Use default")}
|
||||||
|
active={state === ALL_MESSAGES}
|
||||||
|
iconClassName="mx_RoomTile2_iconBell"
|
||||||
|
onClick={this.onClickAllNotifs}
|
||||||
|
/>
|
||||||
|
<NotifOption
|
||||||
|
label={_t("All messages")}
|
||||||
|
active={state === ALL_MESSAGES_LOUD}
|
||||||
|
iconClassName="mx_RoomTile2_iconBellDot"
|
||||||
|
onClick={this.onClickAlertMe}
|
||||||
|
/>
|
||||||
|
<NotifOption
|
||||||
|
label={_t("Mentions & Keywords")}
|
||||||
|
active={state === MENTIONS_ONLY}
|
||||||
|
iconClassName="mx_RoomTile2_iconBellMentions"
|
||||||
|
onClick={this.onClickMentions}
|
||||||
|
/>
|
||||||
|
<NotifOption
|
||||||
|
label={_t("None")}
|
||||||
|
active={state === MUTE}
|
||||||
|
iconClassName="mx_RoomTile2_iconBellCrossed"
|
||||||
|
onClick={this.onClickMute}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContextMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes = classNames("mx_RoomTile2_notificationsButton", {
|
||||||
|
// Show bell icon for the default case too.
|
||||||
|
mx_RoomTile2_iconBell: state === ALL_MESSAGES_LOUD || state === ALL_MESSAGES,
|
||||||
|
mx_RoomTile2_iconBellDot: state === MENTIONS_ONLY,
|
||||||
|
mx_RoomTile2_iconBellCrossed: state === MUTE,
|
||||||
|
// XXX: RoomNotifs assumes ALL_MESSAGES is default, this is wrong,
|
||||||
|
// but cannot be fixed until FTUE Notifications lands.
|
||||||
|
mx_RoomTile2_notificationsButton_show: state !== ALL_MESSAGES,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<ContextMenuButton
|
||||||
|
className={classes}
|
||||||
|
onClick={this.onNotificationsMenuOpenClick}
|
||||||
|
inputRef={this.notificationsMenuButtonRef}
|
||||||
|
label={_t("Notification options")}
|
||||||
|
isExpanded={this.state.notificationsMenuDisplayed}
|
||||||
|
/>
|
||||||
|
{contextMenu}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private renderGeneralMenu(): React.ReactElement {
|
private renderGeneralMenu(): React.ReactElement {
|
||||||
if (this.props.isMinimized) return null; // no menu when minimized
|
if (this.props.isMinimized) return null; // no menu when minimized
|
||||||
|
|
||||||
|
// TODO: Get a proper invite context menu, or take invites out of the room list.
|
||||||
|
if (this.props.tag === DefaultTagID.Invite) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let contextMenu = null;
|
let contextMenu = null;
|
||||||
if (this.state.generalMenuDisplayed) {
|
if (this.state.generalMenuDisplayed) {
|
||||||
// The context menu appears within the list, so use the room tile as a reference point
|
const elementRect = this.generalMenuButtonRef.current.getBoundingClientRect();
|
||||||
const elementRect = this.roomTileRef.current.getBoundingClientRect();
|
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu
|
<ContextMenu {...contextMenuBelow(elementRect)} onFinished={this.onCloseGeneralMenu}>
|
||||||
chevronFace="none"
|
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile2_contextMenu">
|
||||||
left={elementRect.left}
|
|
||||||
top={elementRect.top + elementRect.height + 8}
|
|
||||||
onFinished={this.onCloseGeneralMenu}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile2_contextMenu"
|
|
||||||
style={{width: elementRect.width}}
|
|
||||||
>
|
|
||||||
<div className="mx_IconizedContextMenu_optionList">
|
<div className="mx_IconizedContextMenu_optionList">
|
||||||
<ul>
|
<AccessibleButton onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}>
|
||||||
<li>
|
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconStar" />
|
||||||
<AccessibleButton onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}>
|
<span className="mx_IconizedContextMenu_label">{_t("Favourite")}</span>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconStar" />
|
</AccessibleButton>
|
||||||
<span>{_t("Favourite")}</span>
|
<AccessibleButton onClick={this.onOpenRoomSettings}>
|
||||||
</AccessibleButton>
|
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconSettings" />
|
||||||
</li>
|
<span className="mx_IconizedContextMenu_label">{_t("Settings")}</span>
|
||||||
<li>
|
</AccessibleButton>
|
||||||
<AccessibleButton onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}>
|
|
||||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconArrowDown" />
|
|
||||||
<span>{_t("Low Priority")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<AccessibleButton onClick={this.onOpenRoomSettings}>
|
|
||||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconSettings" />
|
|
||||||
<span>{_t("Settings")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_IconizedContextMenu_optionList">
|
<div className="mx_IconizedContextMenu_optionList mx_RoomTile2_contextMenu_redRow">
|
||||||
<ul>
|
<AccessibleButton onClick={this.onLeaveRoomClick}>
|
||||||
<li className="mx_RoomTile2_contextMenu_redRow">
|
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconSignOut" />
|
||||||
<AccessibleButton onClick={this.onLeaveRoomClick}>
|
<span className="mx_IconizedContextMenu_label">{_t("Leave Room")}</span>
|
||||||
<span className="mx_IconizedContextMenu_icon mx_RoomTile2_iconSignOut" />
|
</AccessibleButton>
|
||||||
<span>{_t("Leave Room")}</span>
|
|
||||||
</AccessibleButton>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
|
@ -229,7 +354,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'mx_RoomTile2': true,
|
'mx_RoomTile2': true,
|
||||||
'mx_RoomTile2_selected': this.state.selected,
|
'mx_RoomTile2_selected': this.state.selected,
|
||||||
'mx_RoomTile2_hasMenuOpen': this.state.generalMenuDisplayed,
|
'mx_RoomTile2_hasMenuOpen': this.state.generalMenuDisplayed || this.state.notificationsMenuDisplayed,
|
||||||
'mx_RoomTile2_minimized': this.props.isMinimized,
|
'mx_RoomTile2_minimized': this.props.isMinimized,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -280,7 +405,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
const avatarSize = 32;
|
const avatarSize = 32;
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<RovingTabIndexWrapper inputRef={this.roomTileRef}>
|
<RovingTabIndexWrapper>
|
||||||
{({onFocus, isActive, ref}) =>
|
{({onFocus, isActive, ref}) =>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
|
@ -300,6 +425,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
||||||
<div className="mx_RoomTile2_badgeContainer">
|
<div className="mx_RoomTile2_badgeContainer">
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
|
{this.renderNotificationsMenu()}
|
||||||
{this.renderGeneralMenu()}
|
{this.renderGeneralMenu()}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
}
|
}
|
||||||
|
|
|
@ -413,7 +413,7 @@ export default class SetIdServer extends React.Component {
|
||||||
tooltipContent={this._getTooltip()}
|
tooltipContent={this._getTooltip()}
|
||||||
tooltipClassName="mx_SetIdServer_tooltip"
|
tooltipClassName="mx_SetIdServer_tooltip"
|
||||||
disabled={this.state.busy}
|
disabled={this.state.busy}
|
||||||
flagInvalid={!!this.state.error}
|
forceValidity={this.state.error ? false : null}
|
||||||
/>
|
/>
|
||||||
<AccessibleButton type="submit" kind="primary_sm"
|
<AccessibleButton type="submit" kind="primary_sm"
|
||||||
onClick={this._checkIdServer}
|
onClick={this._checkIdServer}
|
||||||
|
|
|
@ -28,6 +28,8 @@ export default class NotificationsSettingsTab extends React.Component {
|
||||||
roomId: PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_soundUpload = createRef();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -44,8 +46,6 @@ export default class NotificationsSettingsTab extends React.Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({currentSound: soundData.name || soundData.url});
|
this.setState({currentSound: soundData.name || soundData.url});
|
||||||
|
|
||||||
this._soundUpload = createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _triggerUploader(e) {
|
async _triggerUploader(e) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
import Field from "../../../elements/Field";
|
import Field from "../../../elements/Field";
|
||||||
import * as sdk from "../../../../..";
|
import * as sdk from "../../../../..";
|
||||||
import PlatformPeg from "../../../../../PlatformPeg";
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
|
import {RoomListStoreTempProxy} from "../../../../../stores/room-list/RoomListStoreTempProxy";
|
||||||
|
|
||||||
export default class PreferencesUserSettingsTab extends React.Component {
|
export default class PreferencesUserSettingsTab extends React.Component {
|
||||||
static ROOM_LIST_SETTINGS = [
|
static ROOM_LIST_SETTINGS = [
|
||||||
|
@ -31,6 +32,19 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
||||||
'breadcrumbs',
|
'breadcrumbs',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14231
|
||||||
|
static ROOM_LIST_2_SETTINGS = [
|
||||||
|
'breadcrumbs',
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14231
|
||||||
|
static eligibleRoomListSettings = () => {
|
||||||
|
if (RoomListStoreTempProxy.isUsingNewStore()) {
|
||||||
|
return PreferencesUserSettingsTab.ROOM_LIST_2_SETTINGS;
|
||||||
|
}
|
||||||
|
return PreferencesUserSettingsTab.ROOM_LIST_SETTINGS;
|
||||||
|
};
|
||||||
|
|
||||||
static COMPOSER_SETTINGS = [
|
static COMPOSER_SETTINGS = [
|
||||||
'MessageComposerInput.autoReplaceEmoji',
|
'MessageComposerInput.autoReplaceEmoji',
|
||||||
'MessageComposerInput.suggestEmoji',
|
'MessageComposerInput.suggestEmoji',
|
||||||
|
@ -175,7 +189,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
||||||
|
|
||||||
<div className="mx_SettingsTab_section">
|
<div className="mx_SettingsTab_section">
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Room list")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Room list")}</span>
|
||||||
{this._renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
|
{this._renderGroup(PreferencesUserSettingsTab.eligibleRoomListSettings())}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_SettingsTab_section">
|
<div className="mx_SettingsTab_section">
|
||||||
|
|
53
src/components/views/toasts/GenericExpiringToast.tsx
Normal file
53
src/components/views/toasts/GenericExpiringToast.tsx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import ToastStore from "../../../stores/ToastStore";
|
||||||
|
import GenericToast, { IProps as IGenericToastProps } from "./GenericToast";
|
||||||
|
import {useExpiringCounter} from "../../../hooks/useTimeout";
|
||||||
|
|
||||||
|
interface IProps extends IGenericToastProps {
|
||||||
|
toastKey: string;
|
||||||
|
numSeconds: number;
|
||||||
|
dismissLabel: string;
|
||||||
|
onDismiss?();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SECOND = 1000;
|
||||||
|
|
||||||
|
const GenericExpiringToast: React.FC<IProps> = ({description, acceptLabel, dismissLabel, onAccept, onDismiss, toastKey, numSeconds}) => {
|
||||||
|
const onReject = () => {
|
||||||
|
if (onDismiss) onDismiss();
|
||||||
|
ToastStore.sharedInstance().dismissToast(toastKey);
|
||||||
|
};
|
||||||
|
const counter = useExpiringCounter(onReject, SECOND, numSeconds);
|
||||||
|
|
||||||
|
let rejectLabel = dismissLabel;
|
||||||
|
if (counter > 0) {
|
||||||
|
rejectLabel += ` (${counter})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <GenericToast
|
||||||
|
description={description}
|
||||||
|
acceptLabel={acceptLabel}
|
||||||
|
onAccept={onAccept}
|
||||||
|
rejectLabel={rejectLabel}
|
||||||
|
onReject={onReject}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenericExpiringToast;
|
|
@ -19,7 +19,7 @@ import React, {ReactChild} from "react";
|
||||||
import FormButton from "../elements/FormButton";
|
import FormButton from "../elements/FormButton";
|
||||||
import {XOR} from "../../../@types/common";
|
import {XOR} from "../../../@types/common";
|
||||||
|
|
||||||
interface IProps {
|
export interface IProps {
|
||||||
description: ReactChild;
|
description: ReactChild;
|
||||||
acceptLabel: string;
|
acceptLabel: string;
|
||||||
|
|
||||||
|
|
67
src/hooks/useTimeout.ts
Normal file
67
src/hooks/useTimeout.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {useEffect, useRef, useState} from "react";
|
||||||
|
|
||||||
|
type Handler = () => void;
|
||||||
|
|
||||||
|
// Hook to simplify timeouts in functional components
|
||||||
|
export const useTimeout = (handler: Handler, timeoutMs: number) => {
|
||||||
|
// Create a ref that stores handler
|
||||||
|
const savedHandler = useRef<Handler>();
|
||||||
|
|
||||||
|
// Update ref.current value if handler changes.
|
||||||
|
useEffect(() => {
|
||||||
|
savedHandler.current = handler;
|
||||||
|
}, [handler]);
|
||||||
|
|
||||||
|
// Set up timer
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutID = setTimeout(() => {
|
||||||
|
savedHandler.current();
|
||||||
|
}, timeoutMs);
|
||||||
|
return () => clearTimeout(timeoutID);
|
||||||
|
}, [timeoutMs]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook to simplify intervals in functional components
|
||||||
|
export const useInterval = (handler: Handler, intervalMs: number) => {
|
||||||
|
// Create a ref that stores handler
|
||||||
|
const savedHandler = useRef<Handler>();
|
||||||
|
|
||||||
|
// Update ref.current value if handler changes.
|
||||||
|
useEffect(() => {
|
||||||
|
savedHandler.current = handler;
|
||||||
|
}, [handler]);
|
||||||
|
|
||||||
|
// Set up timer
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalID = setInterval(() => {
|
||||||
|
savedHandler.current();
|
||||||
|
}, intervalMs);
|
||||||
|
return () => clearInterval(intervalID);
|
||||||
|
}, [intervalMs]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook to simplify a variable counting down to 0, handler called when it reached 0
|
||||||
|
export const useExpiringCounter = (handler: Handler, intervalMs: number, initialCount: number) => {
|
||||||
|
const [count, setCount] = useState(initialCount);
|
||||||
|
useInterval(() => setCount(c => c - 1), intervalMs);
|
||||||
|
if (count === 0) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
|
@ -2325,7 +2325,7 @@
|
||||||
"Verify this login": "Потвърди тази сесия",
|
"Verify this login": "Потвърди тази сесия",
|
||||||
"Session verified": "Сесията беше потвърдена",
|
"Session verified": "Сесията беше потвърдена",
|
||||||
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Промяната на паролата ще нулира всички ключове за шифроване от-край-до-край по всички ваши сесии, правейки шифрованата история на чата нечетима. Настройте резервно копие на ключовете или експортирайте ключовете на стаите от друга сесия преди да промените паролата си.",
|
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Промяната на паролата ще нулира всички ключове за шифроване от-край-до-край по всички ваши сесии, правейки шифрованата история на чата нечетима. Настройте резервно копие на ключовете или експортирайте ключовете на стаите от друга сесия преди да промените паролата си.",
|
||||||
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Администраторът на сървъра е изключиш шифроване от край-до-край по подразбиране за лични стаи и за директни съобщения.",
|
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Администраторът на сървъра е изключил шифроване от край-до-край по подразбиране за лични стаи и за директни съобщения.",
|
||||||
"Emoji picker": "Избор на емоджи",
|
"Emoji picker": "Избор на емоджи",
|
||||||
"People": "Хора",
|
"People": "Хора",
|
||||||
"Show %(n)s more": "Покажи още %(n)s",
|
"Show %(n)s more": "Покажи още %(n)s",
|
||||||
|
@ -2446,5 +2446,27 @@
|
||||||
"A-Z": "Азбучен ред",
|
"A-Z": "Азбучен ред",
|
||||||
"Unread rooms": "Непрочетени стаи",
|
"Unread rooms": "Непрочетени стаи",
|
||||||
"Show %(count)s more|other": "Покажи още %(count)s",
|
"Show %(count)s more|other": "Покажи още %(count)s",
|
||||||
"Show %(count)s more|one": "Покажи още %(count)s"
|
"Show %(count)s more|one": "Покажи още %(count)s",
|
||||||
|
"Light": "Светла",
|
||||||
|
"Dark": "Тъмна",
|
||||||
|
"Use the improved room list (will refresh to apply changes)": "Използвай подобрения списък със стаи (ще презареди за да се приложи промяната)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Включи опцията за IRC изглед в раздел Изглед",
|
||||||
|
"Use custom size": "Използвай собствен размер",
|
||||||
|
"Use a system font": "Използвай системния шрифт",
|
||||||
|
"System font name": "Име на системния шрифт",
|
||||||
|
"Hey you. You're the best!": "Хей, ти. Върхът си!",
|
||||||
|
"Message layout": "Изглед на съобщенията",
|
||||||
|
"Compact": "Компактен",
|
||||||
|
"Modern": "Модерен",
|
||||||
|
"Customise your appearance": "Настройте изгледа",
|
||||||
|
"Appearance Settings only affect this Riot session.": "Настройките на изгледа влияят само на тази Riot сесия.",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "Автентичността на това шифровано съобщение не може да бъде гарантирана на това устройство.",
|
||||||
|
"Always show first": "Винаги показвай първо",
|
||||||
|
"Show": "Покажи",
|
||||||
|
"Message preview": "Преглед на съобщението",
|
||||||
|
"List options": "Опции на списъка",
|
||||||
|
"Leave Room": "Напусни стаята",
|
||||||
|
"Room options": "Настройки на стаята",
|
||||||
|
"Use Recovery Key or Passphrase": "Използвай ключ за възстановяване или парола",
|
||||||
|
"Use Recovery Key": "Използвай ключ за възстановяване"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2428,8 +2428,8 @@
|
||||||
"Set a room address to easily share your room with other people.": "Vergebe eine Raum-Adresse, um diesen Raum auf einfache Weise mit anderen Personen teilen zu können.",
|
"Set a room address to easily share your room with other people.": "Vergebe eine Raum-Adresse, um diesen Raum auf einfache Weise mit anderen Personen teilen zu können.",
|
||||||
"You've previously used a newer version of Riot with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "Du hast für diese Sitzung zuvor eine neuere Version von Riot verwendet. Um diese Version mit Ende-zu-Ende-Verschlüsselung wieder zu benutzen, musst du dich erst ab- und dann wieder anmelden.",
|
"You've previously used a newer version of Riot with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "Du hast für diese Sitzung zuvor eine neuere Version von Riot verwendet. Um diese Version mit Ende-zu-Ende-Verschlüsselung wieder zu benutzen, musst du dich erst ab- und dann wieder anmelden.",
|
||||||
"Delete the room address %(alias)s and remove %(name)s from the directory?": "Soll die Raum-Adresse %(alias)s gelöscht und %(name)s aus dem Raum-Verzeichnis entfernt werden?",
|
"Delete the room address %(alias)s and remove %(name)s from the directory?": "Soll die Raum-Adresse %(alias)s gelöscht und %(name)s aus dem Raum-Verzeichnis entfernt werden?",
|
||||||
"Switch to light mode": "Zum Light Mode wechseln",
|
"Switch to light mode": "Zum hellen Thema wechseln",
|
||||||
"Switch to dark mode": "Zum Dark Mode wechseln",
|
"Switch to dark mode": "Zum dunklen Thema wechseln",
|
||||||
"Switch theme": "Design ändern",
|
"Switch theme": "Design ändern",
|
||||||
"Security & privacy": "Sicherheit & Datenschutz",
|
"Security & privacy": "Sicherheit & Datenschutz",
|
||||||
"All settings": "Alle Einstellungen",
|
"All settings": "Alle Einstellungen",
|
||||||
|
@ -2462,5 +2462,79 @@
|
||||||
"Enter your Recovery Key to continue.": "Gib deinen Wiederherstellungsschlüssel ein um fortzufahren.",
|
"Enter your Recovery Key to continue.": "Gib deinen Wiederherstellungsschlüssel ein um fortzufahren.",
|
||||||
"Create a Recovery Key": "Erzeuge einen Wiederherstellungsschlüssel",
|
"Create a Recovery Key": "Erzeuge einen Wiederherstellungsschlüssel",
|
||||||
"Upgrade your Recovery Key": "Aktualisiere deinen Wiederherstellungsschlüssel",
|
"Upgrade your Recovery Key": "Aktualisiere deinen Wiederherstellungsschlüssel",
|
||||||
"Store your Recovery Key": "Speichere deinen Wiederherstellungsschlüssel"
|
"Store your Recovery Key": "Speichere deinen Wiederherstellungsschlüssel",
|
||||||
|
"Light": "Hell",
|
||||||
|
"Dark": "Dunkel",
|
||||||
|
"Use the improved room list (will refresh to apply changes)": "Verwende die verbesserte Raumliste (lädt die Anwendung neu)",
|
||||||
|
"Use custom size": "Verwende individuelle Größe",
|
||||||
|
"Hey you. You're the best!": "Hey du. Du bist der Beste!",
|
||||||
|
"Message layout": "Nachrichtenlayout",
|
||||||
|
"Compact": "Kompakt",
|
||||||
|
"Modern": "Modern",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Option für IRC Layout in den Erscheinungsbild-Einstellungen aktivieren",
|
||||||
|
"Use a system font": "Verwende die System-Schriftart",
|
||||||
|
"System font name": "System-Schriftart",
|
||||||
|
"Customise your appearance": "Verändere das Erscheinungsbild",
|
||||||
|
"Appearance Settings only affect this Riot session.": "Einstellungen zum Erscheinungsbild wirken sich nur auf diese Riot Sitzung aus.",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "Die Echtheit dieser verschlüsselten Nachricht kann auf diesem Gerät nicht garantiert werden.",
|
||||||
|
"You joined the call": "Du bist dem Anruf beigetreten",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s ist dem Anruf beigetreten",
|
||||||
|
"Call in progress": "Laufendes Gespräch",
|
||||||
|
"You left the call": "Du hast den Anruf verlassen",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s hat den Anruf verlassen",
|
||||||
|
"Call ended": "Anruf beendet",
|
||||||
|
"You started a call": "Du hast einen Anruf gestartet",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s hat einen Anruf gestartet",
|
||||||
|
"Waiting for answer": "Warte auf Antwort",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s ruft an",
|
||||||
|
"You created the room": "Du hast den Raum erstellt",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s hat den Raum erstellt",
|
||||||
|
"You made the chat encrypted": "Du hast den Raum verschlüsselt",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s hat den Raum verschlüsselt",
|
||||||
|
"You made history visible to new members": "Du hast die bisherige Kommunikation für neue Teilnehmern sichtbar gemacht",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s hat die bisherige Kommunikation für neue Teilnehmern sichtbar gemacht",
|
||||||
|
"You made history visible to anyone": "Du hast die bisherige Kommunikation für alle sichtbar gemacht",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s hat die bisherige Kommunikation für alle sichtbar gemacht",
|
||||||
|
"You made history visible to future members": "Du hast die bisherige Kommunikation für zukünftige Teilnehmer sichtbar gemacht",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s hat die bisherige Kommunikation für zukünftige Teilnehmer sichtbar gemacht",
|
||||||
|
"You were invited": "Du wurdest eingeladen",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s wurde eingeladen",
|
||||||
|
"You left": "Du hast den Raum verlassen",
|
||||||
|
"%(targetName)s left": "%(targetName)s hat den Raum verlassen",
|
||||||
|
"You were kicked (%(reason)s)": "Du wurdest herausgeworfen (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s wurde herausgeworfen (%(reason)s)",
|
||||||
|
"You were kicked": "Du wurdest herausgeworfen",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s wurde herausgeworfen",
|
||||||
|
"You rejected the invite": "Du hast die Einladung abgelehnt",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s hat die Einladung abgelehnt",
|
||||||
|
"You were uninvited": "Deine Einladung wurde zurückgezogen",
|
||||||
|
"%(targetName)s was uninvited": "Die Einladung für %(targetName)s wurde zurückgezogen",
|
||||||
|
"You were banned (%(reason)s)": "Du wurdest verbannt (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s wurde verbannt (%(reason)s)",
|
||||||
|
"You were banned": "Du wurdest verbannt",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s wurde verbannt",
|
||||||
|
"You joined": "Du bist beigetreten",
|
||||||
|
"%(targetName)s joined": "%(targetName)s ist beigetreten",
|
||||||
|
"You changed your name": "Du hast deinen Namen geändert",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s hat den Namen geändert",
|
||||||
|
"You changed your avatar": "Du hast deinen Avatar geändert",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s hat den Avatar geändert",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Du hast den Raumnamen geändert",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s hat den Raumnamen geändert",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Du hast die Einladung für %(targetName)s zurückgezogen",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s hat die Einladung für %(targetName)s zurückgezogen",
|
||||||
|
"You invited %(targetName)s": "Du hast %(targetName)s eingeladen",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s hat %(targetName)s eingeladen",
|
||||||
|
"You changed the room topic": "Du hast das Raumthema geändert",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s hat das Raumthema geändert",
|
||||||
|
"New spinner design": "Neue Warteanimation",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Verwende ein kompakteres 'modernes' Layout",
|
||||||
|
"Message deleted on %(date)s": "Nachricht am %(date)s gelöscht",
|
||||||
|
"Wrong file type": "Falscher Dateityp",
|
||||||
|
"Wrong Recovery Key": "Falscher Wiederherstellungsschlüssel",
|
||||||
|
"Invalid Recovery Key": "Ungültiger Wiederherstellungsschlüssel"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1218,8 +1218,11 @@
|
||||||
"%(count)s unread messages.|one": "1 unread message.",
|
"%(count)s unread messages.|one": "1 unread message.",
|
||||||
"Unread mentions.": "Unread mentions.",
|
"Unread mentions.": "Unread mentions.",
|
||||||
"Unread messages.": "Unread messages.",
|
"Unread messages.": "Unread messages.",
|
||||||
|
"Use default": "Use default",
|
||||||
|
"All messages": "All messages",
|
||||||
|
"Mentions & Keywords": "Mentions & Keywords",
|
||||||
|
"Notification options": "Notification options",
|
||||||
"Favourite": "Favourite",
|
"Favourite": "Favourite",
|
||||||
"Low Priority": "Low Priority",
|
|
||||||
"Leave Room": "Leave Room",
|
"Leave Room": "Leave Room",
|
||||||
"Room options": "Room options",
|
"Room options": "Room options",
|
||||||
"Add a topic": "Add a topic",
|
"Add a topic": "Add a topic",
|
||||||
|
@ -1839,17 +1842,16 @@
|
||||||
"Remember my selection for this widget": "Remember my selection for this widget",
|
"Remember my selection for this widget": "Remember my selection for this widget",
|
||||||
"Allow": "Allow",
|
"Allow": "Allow",
|
||||||
"Deny": "Deny",
|
"Deny": "Deny",
|
||||||
"Enter recovery passphrase": "Enter recovery passphrase",
|
"Wrong file type": "Wrong file type",
|
||||||
|
"Looks good!": "Looks good!",
|
||||||
|
"Wrong Recovery Key": "Wrong Recovery Key",
|
||||||
|
"Invalid Recovery Key": "Invalid Recovery Key",
|
||||||
|
"Security Phrase": "Security Phrase",
|
||||||
"Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.",
|
"Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.",
|
||||||
"<b>Warning</b>: You should only do this on a trusted computer.": "<b>Warning</b>: You should only do this on a trusted computer.",
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Enter your Security Phrase or <button>Use your Security Key</button> to continue.",
|
||||||
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.",
|
"Security Key": "Security Key",
|
||||||
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.": "If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.",
|
"Use your Security Key to continue.": "Use your Security Key to continue.",
|
||||||
"Enter recovery key": "Enter recovery key",
|
"Go Back": "Go Back",
|
||||||
"Unable to access secret storage. Please verify that you entered the correct recovery key.": "Unable to access secret storage. Please verify that you entered the correct recovery key.",
|
|
||||||
"This looks like a valid recovery key!": "This looks like a valid recovery key!",
|
|
||||||
"Not a valid recovery key": "Not a valid recovery key",
|
|
||||||
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.",
|
|
||||||
"If you've forgotten your recovery key you can <button>set up new recovery options</button>.": "If you've forgotten your recovery key you can <button>set up new recovery options</button>.",
|
|
||||||
"Restoring keys from backup": "Restoring keys from backup",
|
"Restoring keys from backup": "Restoring keys from backup",
|
||||||
"Fetching keys from server...": "Fetching keys from server...",
|
"Fetching keys from server...": "Fetching keys from server...",
|
||||||
"%(completed)s of %(total)s keys restored": "%(completed)s of %(total)s keys restored",
|
"%(completed)s of %(total)s keys restored": "%(completed)s of %(total)s keys restored",
|
||||||
|
@ -1863,9 +1865,13 @@
|
||||||
"Keys restored": "Keys restored",
|
"Keys restored": "Keys restored",
|
||||||
"Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!",
|
"Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!",
|
||||||
"Successfully restored %(sessionCount)s keys": "Successfully restored %(sessionCount)s keys",
|
"Successfully restored %(sessionCount)s keys": "Successfully restored %(sessionCount)s keys",
|
||||||
|
"Enter recovery passphrase": "Enter recovery passphrase",
|
||||||
"<b>Warning</b>: you should only set up key backup from a trusted computer.": "<b>Warning</b>: you should only set up key backup from a trusted computer.",
|
"<b>Warning</b>: you should only set up key backup from a trusted computer.": "<b>Warning</b>: you should only set up key backup from a trusted computer.",
|
||||||
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Access your secure message history and set up secure messaging by entering your recovery passphrase.",
|
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Access your secure message history and set up secure messaging by entering your recovery passphrase.",
|
||||||
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>",
|
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>",
|
||||||
|
"Enter recovery key": "Enter recovery key",
|
||||||
|
"This looks like a valid recovery key!": "This looks like a valid recovery key!",
|
||||||
|
"Not a valid recovery key": "Not a valid recovery key",
|
||||||
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
|
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
|
||||||
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
|
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
|
||||||
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>",
|
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>",
|
||||||
|
@ -1894,10 +1900,10 @@
|
||||||
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
||||||
"Notification settings": "Notification settings",
|
"Notification settings": "Notification settings",
|
||||||
"All messages (noisy)": "All messages (noisy)",
|
"All messages (noisy)": "All messages (noisy)",
|
||||||
"All messages": "All messages",
|
|
||||||
"Mentions only": "Mentions only",
|
"Mentions only": "Mentions only",
|
||||||
"Leave": "Leave",
|
"Leave": "Leave",
|
||||||
"Forget": "Forget",
|
"Forget": "Forget",
|
||||||
|
"Low Priority": "Low Priority",
|
||||||
"Direct Chat": "Direct Chat",
|
"Direct Chat": "Direct Chat",
|
||||||
"Clear status": "Clear status",
|
"Clear status": "Clear status",
|
||||||
"Update status": "Update status",
|
"Update status": "Update status",
|
||||||
|
@ -2189,7 +2195,6 @@
|
||||||
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
|
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
|
||||||
"Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
|
"Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
|
||||||
"Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.",
|
"Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.",
|
||||||
"Go Back": "Go Back",
|
|
||||||
"Failed to re-authenticate due to a homeserver problem": "Failed to re-authenticate due to a homeserver problem",
|
"Failed to re-authenticate due to a homeserver problem": "Failed to re-authenticate due to a homeserver problem",
|
||||||
"Incorrect password": "Incorrect password",
|
"Incorrect password": "Incorrect password",
|
||||||
"Failed to re-authenticate": "Failed to re-authenticate",
|
"Failed to re-authenticate": "Failed to re-authenticate",
|
||||||
|
@ -2228,48 +2233,56 @@
|
||||||
"Import": "Import",
|
"Import": "Import",
|
||||||
"Confirm encryption setup": "Confirm encryption setup",
|
"Confirm encryption setup": "Confirm encryption setup",
|
||||||
"Click the button below to confirm setting up encryption.": "Click the button below to confirm setting up encryption.",
|
"Click the button below to confirm setting up encryption.": "Click the button below to confirm setting up encryption.",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
|
||||||
|
"Generate a Security Key": "Generate a Security Key",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.",
|
||||||
|
"Enter a Security Phrase": "Enter a Security Phrase",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Use a secret phrase only you know, and optionally save a Security Key to use for backup.",
|
||||||
"Enter your account password to confirm the upgrade:": "Enter your account password to confirm the upgrade:",
|
"Enter your account password to confirm the upgrade:": "Enter your account password to confirm the upgrade:",
|
||||||
"Restore your key backup to upgrade your encryption": "Restore your key backup to upgrade your encryption",
|
"Restore your key backup to upgrade your encryption": "Restore your key backup to upgrade your encryption",
|
||||||
"Restore": "Restore",
|
"Restore": "Restore",
|
||||||
"You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.",
|
"You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.",
|
||||||
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
|
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
|
||||||
"Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:",
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.",
|
||||||
"Enter a recovery passphrase": "Enter a recovery passphrase",
|
"Enter a recovery passphrase": "Enter a recovery passphrase",
|
||||||
"Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.",
|
"Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.",
|
||||||
"Back up encrypted message keys": "Back up encrypted message keys",
|
|
||||||
"Set up with a recovery key": "Set up with a recovery key",
|
|
||||||
"That matches!": "That matches!",
|
"That matches!": "That matches!",
|
||||||
"Use a different passphrase?": "Use a different passphrase?",
|
"Use a different passphrase?": "Use a different passphrase?",
|
||||||
"That doesn't match.": "That doesn't match.",
|
"That doesn't match.": "That doesn't match.",
|
||||||
"Go back to set it again.": "Go back to set it again.",
|
"Go back to set it again.": "Go back to set it again.",
|
||||||
"Enter your recovery passphrase a second time to confirm it.": "Enter your recovery passphrase a second time to confirm it.",
|
"Enter your recovery passphrase a second time to confirm it.": "Enter your recovery passphrase a second time to confirm it.",
|
||||||
"Confirm your recovery passphrase": "Confirm your recovery passphrase",
|
"Confirm your recovery passphrase": "Confirm your recovery passphrase",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.",
|
||||||
|
"Download": "Download",
|
||||||
|
"Copy": "Copy",
|
||||||
|
"Unable to query secret storage status": "Unable to query secret storage status",
|
||||||
|
"Retry": "Retry",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "You can also set up Secure Backup & manage your keys in Settings.",
|
||||||
|
"Set up Secure backup": "Set up Secure backup",
|
||||||
|
"Upgrade your encryption": "Upgrade your encryption",
|
||||||
|
"Set a Security Phrase": "Set a Security Phrase",
|
||||||
|
"Confirm Security Phrase": "Confirm Security Phrase",
|
||||||
|
"Save your Security Key": "Save your Security Key",
|
||||||
|
"Unable to set up secret storage": "Unable to set up secret storage",
|
||||||
|
"We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.",
|
||||||
|
"For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.",
|
||||||
|
"Set up with a recovery key": "Set up with a recovery key",
|
||||||
|
"Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.",
|
||||||
|
"Repeat your recovery passphrase...": "Repeat your recovery passphrase...",
|
||||||
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.",
|
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.",
|
||||||
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.",
|
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.",
|
||||||
"Your recovery key": "Your recovery key",
|
"Your recovery key": "Your recovery key",
|
||||||
"Copy": "Copy",
|
|
||||||
"Download": "Download",
|
|
||||||
"Your recovery key has been <b>copied to your clipboard</b>, paste it to:": "Your recovery key has been <b>copied to your clipboard</b>, paste it to:",
|
"Your recovery key has been <b>copied to your clipboard</b>, paste it to:": "Your recovery key has been <b>copied to your clipboard</b>, paste it to:",
|
||||||
"Your recovery key is in your <b>Downloads</b> folder.": "Your recovery key is in your <b>Downloads</b> folder.",
|
"Your recovery key is in your <b>Downloads</b> folder.": "Your recovery key is in your <b>Downloads</b> folder.",
|
||||||
"<b>Print it</b> and store it somewhere safe": "<b>Print it</b> and store it somewhere safe",
|
"<b>Print it</b> and store it somewhere safe": "<b>Print it</b> and store it somewhere safe",
|
||||||
"<b>Save it</b> on a USB key or backup drive": "<b>Save it</b> on a USB key or backup drive",
|
"<b>Save it</b> on a USB key or backup drive": "<b>Save it</b> on a USB key or backup drive",
|
||||||
"<b>Copy it</b> to your personal cloud storage": "<b>Copy it</b> to your personal cloud storage",
|
"<b>Copy it</b> to your personal cloud storage": "<b>Copy it</b> to your personal cloud storage",
|
||||||
"Unable to query secret storage status": "Unable to query secret storage status",
|
|
||||||
"Retry": "Retry",
|
|
||||||
"You can now verify your other devices, and other users to keep your chats safe.": "You can now verify your other devices, and other users to keep your chats safe.",
|
|
||||||
"Upgrade your encryption": "Upgrade your encryption",
|
|
||||||
"Confirm recovery passphrase": "Confirm recovery passphrase",
|
|
||||||
"Make a copy of your recovery key": "Make a copy of your recovery key",
|
|
||||||
"You're done!": "You're done!",
|
|
||||||
"Unable to set up secret storage": "Unable to set up secret storage",
|
|
||||||
"We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.",
|
|
||||||
"For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.",
|
|
||||||
"Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.",
|
|
||||||
"Repeat your recovery passphrase...": "Repeat your recovery passphrase...",
|
|
||||||
"Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",
|
"Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",
|
||||||
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.",
|
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.",
|
||||||
"Set up Secure Message Recovery": "Set up Secure Message Recovery",
|
"Set up Secure Message Recovery": "Set up Secure Message Recovery",
|
||||||
"Secure your backup with a recovery passphrase": "Secure your backup with a recovery passphrase",
|
"Secure your backup with a recovery passphrase": "Secure your backup with a recovery passphrase",
|
||||||
|
"Make a copy of your recovery key": "Make a copy of your recovery key",
|
||||||
"Starting backup...": "Starting backup...",
|
"Starting backup...": "Starting backup...",
|
||||||
"Success!": "Success!",
|
"Success!": "Success!",
|
||||||
"Create key backup": "Create key backup",
|
"Create key backup": "Create key backup",
|
||||||
|
|
|
@ -1179,7 +1179,7 @@
|
||||||
"Use a longer keyboard pattern with more turns": "Usa un patrón de tecleo largo con más vueltas",
|
"Use a longer keyboard pattern with more turns": "Usa un patrón de tecleo largo con más vueltas",
|
||||||
"Enable Community Filter Panel": "Habilitar el Panel de Filtro de Comunidad",
|
"Enable Community Filter Panel": "Habilitar el Panel de Filtro de Comunidad",
|
||||||
"Verify this user by confirming the following emoji appear on their screen.": "Verifica este usuario confirmando que los siguientes emojis aparecen en su pantalla.",
|
"Verify this user by confirming the following emoji appear on their screen.": "Verifica este usuario confirmando que los siguientes emojis aparecen en su pantalla.",
|
||||||
"Your Riot is misconfigured": "Tu Riot está mal configurado",
|
"Your Riot is misconfigured": "Tu Riot tiene un error de configuración",
|
||||||
"Whether or not you're logged in (we don't record your username)": "Hayas o no iniciado sesión (no guardamos tu nombre de usuario)",
|
"Whether or not you're logged in (we don't record your username)": "Hayas o no iniciado sesión (no guardamos tu nombre de usuario)",
|
||||||
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Uses o no los 'breadcrumbs' (iconos sobre la lista de salas)",
|
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Uses o no los 'breadcrumbs' (iconos sobre la lista de salas)",
|
||||||
"A conference call could not be started because the integrations server is not available": "No se pudo iniciar la conferencia porque el servidor de integraciones no está disponible",
|
"A conference call could not be started because the integrations server is not available": "No se pudo iniciar la conferencia porque el servidor de integraciones no está disponible",
|
||||||
|
@ -1556,7 +1556,7 @@
|
||||||
"Sign In or Create Account": "Iniciar sesión o Crear una cuenta",
|
"Sign In or Create Account": "Iniciar sesión o Crear una cuenta",
|
||||||
"Use your account or create a new one to continue.": "Usa tu cuenta existente o crea una nueva para continuar.",
|
"Use your account or create a new one to continue.": "Usa tu cuenta existente o crea una nueva para continuar.",
|
||||||
"Create Account": "Crear cuenta",
|
"Create Account": "Crear cuenta",
|
||||||
"Sign In": "Registrarse",
|
"Sign In": "Iniciar sesión",
|
||||||
"Sends a message as html, without interpreting it as markdown": "Envía un mensaje como html, sin interpretarlo en markdown",
|
"Sends a message as html, without interpreting it as markdown": "Envía un mensaje como html, sin interpretarlo en markdown",
|
||||||
"Failed to set topic": "No se ha podido establecer el tema",
|
"Failed to set topic": "No se ha podido establecer el tema",
|
||||||
"Command failed": "El comando falló",
|
"Command failed": "El comando falló",
|
||||||
|
@ -2191,5 +2191,44 @@
|
||||||
"This homeserver does not support login using email address.": "Este servidor doméstico no admite iniciar sesión con una dirección de correo electrónico.",
|
"This homeserver does not support login using email address.": "Este servidor doméstico no admite iniciar sesión con una dirección de correo electrónico.",
|
||||||
"This account has been deactivated.": "Esta cuenta ha sido desactivada.",
|
"This account has been deactivated.": "Esta cuenta ha sido desactivada.",
|
||||||
"Room name or address": "Nombre o dirección de la sala",
|
"Room name or address": "Nombre o dirección de la sala",
|
||||||
"Address (optional)": "Dirección (opcional)"
|
"Address (optional)": "Dirección (opcional)",
|
||||||
|
"Help us improve Riot": "Ayúdanos a mejorar Riot",
|
||||||
|
"Send <UsageDataLink>anonymous usage data</UsageDataLink> which helps us improve Riot. This will use a <PolicyLink>cookie</PolicyLink>.": "Enviar <UsageDataLink>información anónima de uso</UsageDataLink> nos ayudaría bastante a mejorar Riot. Esto cuenta como utilizar <PolicyLink>una cookie</PolicyLink>.",
|
||||||
|
"I want to help": "Quiero ayudar",
|
||||||
|
"Ok": "Ok",
|
||||||
|
"Set password": "Establecer contraseña",
|
||||||
|
"To return to your account in future you need to set a password": "Para poder regresar a tu cuenta en un futuro necesitas establecer una contraseña",
|
||||||
|
"Restart": "Reiniciar",
|
||||||
|
"Upgrade your Riot": "Actualiza tu Riot",
|
||||||
|
"A new version of Riot is available!": "¡Una nueva versión de Riot se encuentra disponible!",
|
||||||
|
"You joined the call": "Te has unido a la llamada",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s se ha unido a la llamada",
|
||||||
|
"Call in progress": "Llamada en progreso",
|
||||||
|
"You left the call": "Has abandonado la llamada",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s dejo la llamada",
|
||||||
|
"Call ended": "La llamada ha finalizado",
|
||||||
|
"You started a call": "Has iniciado una llamada",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s inicio una llamada",
|
||||||
|
"Waiting for answer": "Esperado por una respuesta",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s está llamando",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s creo la sala",
|
||||||
|
"You were invited": "Has sido invitado",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s ha sido invitado",
|
||||||
|
"%(targetName)s left": "%(targetName)s se ha ido",
|
||||||
|
"You were kicked (%(reason)s)": "Has sido expulsado por %(reason)s",
|
||||||
|
"You rejected the invite": "Has rechazado la invitación",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s rechazo la invitación",
|
||||||
|
"You were banned (%(reason)s)": "Has sido baneado por %(reason)s",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s fue baneado por %(reason)s",
|
||||||
|
"You were banned": "Has sido baneado",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s fue baneado",
|
||||||
|
"You joined": "Te has unido",
|
||||||
|
"%(targetName)s joined": "%(targetName)s se ha unido",
|
||||||
|
"You changed your name": "Has cambiado tu nombre",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s cambio su nombre",
|
||||||
|
"You changed your avatar": "Ha cambiado su avatar",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s ha cambiado su avatar",
|
||||||
|
"You changed the room name": "Has cambiado el nombre de la sala",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s cambio el nombre de la sala",
|
||||||
|
"You invited %(targetName)s": "Has invitado a %(targetName)s"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1592,5 +1592,257 @@
|
||||||
"We encountered an error trying to restore your previous session.": "Meil tekkis eelmise sessiooni taastamisel viga.",
|
"We encountered an error trying to restore your previous session.": "Meil tekkis eelmise sessiooni taastamisel viga.",
|
||||||
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Kui sa varem oled kasutanud uuemat Riot'i versiooni, siis sinu pragune sessioon ei pruugi olla sellega ühilduv. Sulge see aken ja jätka selle uuema versiooni kasutamist.",
|
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Kui sa varem oled kasutanud uuemat Riot'i versiooni, siis sinu pragune sessioon ei pruugi olla sellega ühilduv. Sulge see aken ja jätka selle uuema versiooni kasutamist.",
|
||||||
"Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Brauseri andmeruumi tühjendamine võib selle vea lahendada, kui samas logid sa ka välja ning kogu krüptitud vestlusajalugu muutub loetamatuks.",
|
"Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Brauseri andmeruumi tühjendamine võib selle vea lahendada, kui samas logid sa ka välja ning kogu krüptitud vestlusajalugu muutub loetamatuks.",
|
||||||
"Verification Pending": "Verifikatsioon on ootel"
|
"Verification Pending": "Verifikatsioon on ootel",
|
||||||
|
"Back": "Tagasi",
|
||||||
|
"Send Custom Event": "Saada kohandatud sündmus",
|
||||||
|
"You must specify an event type!": "Sa pead määratlema sündmuse tüübi!",
|
||||||
|
"Event sent!": "Sündmus on saadetud!",
|
||||||
|
"Failed to send custom event.": "Kohandatud sündmuse saatmine ei õnnestunud.",
|
||||||
|
"Event Type": "Sündmuse tüüp",
|
||||||
|
"State Key": "Oleku võti",
|
||||||
|
"Event Content": "Sündmuse sisu",
|
||||||
|
"Send Account Data": "Saada kasutajakonto andmed",
|
||||||
|
"View Servers in Room": "Näita jututoas kasutatavaid servereid",
|
||||||
|
"Verification Requests": "Verifitseerimistaotlused",
|
||||||
|
"Toolbox": "Töövahendid",
|
||||||
|
"Developer Tools": "Arendusvahendid",
|
||||||
|
"An error has occurred.": "Tekkis viga.",
|
||||||
|
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Selle kasutaja usaldamiseks peaksid ta verifitseerima. Kui sa pruugid läbivalt krüptitud sõnumeid, siis kasutajate verifitseerimine tagab sulle täiendava meelerahu.",
|
||||||
|
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Selle kasutaja verifitseerimisel märgitakse tema sessioon usaldusväärseks ning samuti märgitakse sinu sessioon tema jaoks usaldusväärseks.",
|
||||||
|
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Selle seadme usaldamiseks peaksid ta verifitseerima. Kui sa pruugid läbivalt krüptitud sõnumeid, siis selle seadme usaldamine tagab sulle ja teistele kasutajatele täiendava meelerahu.",
|
||||||
|
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Selle seadme verifitseerimisel märgitakse ta usaldusväärseks ning kõik kasutajad, kes sinuga on verifitseerimise läbi teinud, loevad ka selle seadme usaldusväärseks.",
|
||||||
|
"Waiting for partner to confirm...": "Ootan teise osapoole kinnitust...",
|
||||||
|
"Incoming Verification Request": "Saabuv verifitseerimispalve",
|
||||||
|
"Integrations are disabled": "Lõimingud ei ole kasutusel",
|
||||||
|
"Enable 'Manage Integrations' in Settings to do this.": "Selle tegevuse jaoks määra seadetes \"Halda lõiminguid\" kasutuselevõetuks.",
|
||||||
|
"Integrations not allowed": "Lõimingute kasutamine ei ole lubatud",
|
||||||
|
"Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Sinu Riot ei võimalda selle tegevuse jaoks kasutada Lõimingute haldurit. Palun küsi lisateavet administraatorilt.",
|
||||||
|
"Failed to invite the following users to chat: %(csvUsers)s": "Järgnevate kasutajate vestlema kutsumine ei õnnestunud: %(csvUsers)s",
|
||||||
|
"We couldn't create your DM. Please check the users you want to invite and try again.": "Otsevestluse loomine ei õnnestunud. Palun kontrolli, et kasutajanimed oleks õiged ja proovi uuesti.",
|
||||||
|
"a new master key signature": "uus üldvõtme allkiri",
|
||||||
|
"a new cross-signing key signature": "uus risttunnustamise võtme allkiri",
|
||||||
|
"a device cross-signing signature": "seadme risttunnustamise allkiri",
|
||||||
|
"a key signature": "võtme allkiri",
|
||||||
|
"Riot encountered an error during upload of:": "Riot'is tekkis viga järgneva üleslaadimisel:",
|
||||||
|
"Cancelled signature upload": "Allkirja üleslaadimine on tühistatud",
|
||||||
|
"Unable to upload": "Üleslaadimine ei õnnestu",
|
||||||
|
"Signature upload success": "Allkirja üleslaadimine õnnestus",
|
||||||
|
"Signature upload failed": "Allkirja üleslaadimine ei õnnestunud",
|
||||||
|
"Address (optional)": "Aadress (valikuline)",
|
||||||
|
"Reject invitation": "Lükka kutse tagasi",
|
||||||
|
"Are you sure you want to reject the invitation?": "Kas sa oled kindel, et soovid lükata kutse tagasi?",
|
||||||
|
"Failed to set Direct Message status of room": "Jututoa otsevestluse oleku seadmine ei õnnestunud",
|
||||||
|
"Failed to forget room %(errCode)s": "Jututoa unustamine ei õnnestunud %(errCode)s",
|
||||||
|
"This homeserver would like to make sure you are not a robot.": "See server soovib kindlaks teha, et sa ei ole robot.",
|
||||||
|
"Country Dropdown": "Riikide valik",
|
||||||
|
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "Sa võid kasutada serveri kohandatud valikuid selleks, et määrates teise aadressi logida sisse teise Matrix'i serverisse. See võimaldab sul kasutada seda rakendust teises koduserveris hallatava olemasoleva Matrix'i kontoga.",
|
||||||
|
"Confirm your identity by entering your account password below.": "Tuvasta oma isik sisestades salasõna alljärgnevalt.",
|
||||||
|
"Please review and accept all of the homeserver's policies": "Palun vaata üle kõik koduserveri kasutustingimused ja nõustu nendega",
|
||||||
|
"Please review and accept the policies of this homeserver:": "Palun vaata üle selle koduserveri kasutustingimused ja nõustu nendega:",
|
||||||
|
"An email has been sent to %(emailAddress)s": "Saatsime e-kirja %(emailAddress)s aadressile",
|
||||||
|
"Please check your email to continue registration.": "Registreerimise jätkamiseks, palun vaata oma e-kirjad.",
|
||||||
|
"A text message has been sent to %(msisdn)s": "Saatsime tekstisõnumi telefoninumbrile %(msisdn)s",
|
||||||
|
"Please enter the code it contains:": "Palun sisesta seal kuvatud kood:",
|
||||||
|
"Code": "Kood",
|
||||||
|
"Submit": "Saada",
|
||||||
|
"Start authentication": "Alusta autentimist",
|
||||||
|
"Unable to validate homeserver/identity server": "Ei õnnestu valideerida koduserverit/isikutuvastusserverit",
|
||||||
|
"Your Modular server": "Sinu Modular-server",
|
||||||
|
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Sisesta oma Modular'i koduserveri aadress. See võib kasutada nii sinu oma domeeni, kui olla <a>modular.im</a> alamdomeen.",
|
||||||
|
"Sign in with SSO": "Logi sisse kasutades SSO'd ehk ühekordset autentimist",
|
||||||
|
"Failed to reject invitation": "Kutse tagasi lükkamine ei õnnestunud",
|
||||||
|
"This room is not public. You will not be able to rejoin without an invite.": "See ei ole avalik jututuba. Ilma kutseta sa ei saa uuesti liituda.",
|
||||||
|
"Failed to leave room": "Jututoast lahkumine ei õnnestunud",
|
||||||
|
"Can't leave Server Notices room": "Serveriteadete jututoast ei saa lahkuda",
|
||||||
|
"This room is used for important messages from the Homeserver, so you cannot leave it.": "Seda jututuba kasutatakse sinu koduserveri oluliste teadete jaoks ja seega sa ei saa sealt lahkuda.",
|
||||||
|
"Signed Out": "Välja logitud",
|
||||||
|
"A username can only contain lower case letters, numbers and '=_-./'": "Kasutajanimes võivad olla vaid väiketähed, numbrid ja need viis tähemärki =_-./",
|
||||||
|
"Username not available": "Selline kasutajanimi ei ole saadaval",
|
||||||
|
"Username invalid: %(errMessage)s": "Vigane kasutajanimi: %(errMessage)s",
|
||||||
|
"An error occurred: %(error_string)s": "Tekkis viga: %(error_string)s",
|
||||||
|
"Checking...": "Kontrollin...",
|
||||||
|
"Username available": "Kasutajanimi on saadaval",
|
||||||
|
"To get started, please pick a username!": "Alustamiseks palun vali kasutajanimi!",
|
||||||
|
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "See saab olema sinu kasutajanimi <span></span> koduserveris, aga sa võid valida ka <a>mõne teise serveri</a>.",
|
||||||
|
"If you already have a Matrix account you can <a>log in</a> instead.": "Kui sul juba on olemas Matrix'i konto, siis võid lihtsalt <a>sisse logida</a>.",
|
||||||
|
"You have successfully set a password!": "Salasõna loomine õnnestus!",
|
||||||
|
"You have successfully set a password and an email address!": "Salasõna loomine ja e-posti aadressi salvestamine õnnestus!",
|
||||||
|
"You can now return to your account after signing out, and sign in on other devices.": "Nüüd sa saad peale väljalogimist pöörduda tagasi oma konto juurde või logida sisse muudest seadmetest.",
|
||||||
|
"Remember, you can always set an email address in user settings if you change your mind.": "Jäta meelde, et sa saad alati hiljem määrata kasutajaseadetest oma e-posti aadressi.",
|
||||||
|
"Use bots, bridges, widgets and sticker packs": "Kasuta roboteid, võrgusildu, vidinaid või kleepsupakke",
|
||||||
|
"Upload all": "Lae kõik üles",
|
||||||
|
"This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "See fail on üleslaadimiseks <b>liiga suur</b>. Üleslaetavate failide mahupiir on %(limit)s, kuid selle faili suurus on %(sizeOfThisFile)s.",
|
||||||
|
"Appearance": "Välimus",
|
||||||
|
"Enter recovery passphrase": "Sisesta taastamise paroolifraas",
|
||||||
|
"For security, this session has been signed out. Please sign in again.": "Turvalisusega seotud põhjustel on see sessioon välja logitud. Palun logi uuesti sisse.",
|
||||||
|
"Review terms and conditions": "Vaata üle kasutustingimused",
|
||||||
|
"Old cryptography data detected": "Tuvastasin andmed, mille puhul on kasutatud vanemat tüüpi krüptimist",
|
||||||
|
"Self-verification request": "Päring enda verifitseerimiseks",
|
||||||
|
"Switch to light mode": "Kasuta heledat teemat",
|
||||||
|
"Switch to dark mode": "Kasuta tumedat teemat",
|
||||||
|
"Switch theme": "Vaheta teemat",
|
||||||
|
"Security & privacy": "Turvalisus ja privaatsus",
|
||||||
|
"All settings": "Kõik seadistused",
|
||||||
|
"Archived rooms": "Arhiveeritud jututoad",
|
||||||
|
"Feedback": "Tagasiside",
|
||||||
|
"Account settings": "Kasutajakonto seadistused",
|
||||||
|
"Use Single Sign On to continue": "Jätkamiseks kasuta ühekordset sisselogimist",
|
||||||
|
"Confirm adding this email address by using Single Sign On to prove your identity.": "Kinnita selle e-posti aadressi lisamine kasutades ühekordset sisselogimist oma isiku tuvastamiseks.",
|
||||||
|
"Single Sign On": "SSO Ühekordne sisselogimine",
|
||||||
|
"Confirm adding email": "Kinnita e-posti aadressi lisamine",
|
||||||
|
"Click the button below to confirm adding this email address.": "Klõpsi järgnevat nuppu e-posti aadressi lisamise kinnitamiseks.",
|
||||||
|
"Confirm adding this phone number by using Single Sign On to prove your identity.": "Kinnita selle telefoninumbri lisamine kasutades ühekordset sisselogimist oma isiku tuvastamiseks.",
|
||||||
|
"Confirm adding phone number": "Kinnita telefoninumbri lisamine",
|
||||||
|
"Click the button below to confirm adding this phone number.": "Klõpsi järgnevat nuppu telefoninumbri lisamise kinnitamiseks.",
|
||||||
|
"Add Phone Number": "Lisa telefoninumber",
|
||||||
|
"Default": "Tavakasutaja",
|
||||||
|
"Restricted": "Piiratud õigustega kasutaja",
|
||||||
|
"Moderator": "Moderaator",
|
||||||
|
"Admin": "Peakasutaja",
|
||||||
|
"Custom (%(level)s)": "Kohandatud õigused (%(level)s)",
|
||||||
|
"Failed to invite": "Kutse saatmine ei õnnestunud",
|
||||||
|
"Operation failed": "Toiming ei õnnestunud",
|
||||||
|
"Failed to invite users to the room:": "Kasutajate kutsumine jututuppa ei õnnestunud:",
|
||||||
|
"Failed to invite the following users to the %(roomName)s room:": "Järgnevate kasutajate kutsumine %(roomName)s jututuppa ei õnnestunud:",
|
||||||
|
"You need to be logged in.": "Sa peaksid olema sisse loginud.",
|
||||||
|
"You need to be able to invite users to do that.": "Selle tegevuse jaoks peaks sul olema õigus teistele kasutajatele kutse saatmiseks.",
|
||||||
|
"Unable to create widget.": "Vidina loomine ei õnnestunud.",
|
||||||
|
"Missing roomId.": "Jututoa tunnus ehk roomId on puudu.",
|
||||||
|
"Failed to send request.": "Päringu saatmine ei õnnestunud.",
|
||||||
|
"This room is not recognised.": "Seda jututuba ei õnnestu ära tunda.",
|
||||||
|
"Power level must be positive integer.": "Õiguste tase peab olema positiivne täisarv.",
|
||||||
|
"You are not in this room.": "Sa ei asu selles jututoas.",
|
||||||
|
"You do not have permission to do that in this room.": "Sinul pole selle toimingu jaoks selles jututoas õigusi.",
|
||||||
|
"Missing room_id in request": "Päringus puudub jututoa tunnus ehk room_id",
|
||||||
|
"Room %(roomId)s not visible": "Jututuba %(roomId)s ei ole nähtav",
|
||||||
|
"Missing user_id in request": "Päringus puudub kasutaja tunnus ehk user_id",
|
||||||
|
"Messages": "Sõnumid",
|
||||||
|
"Actions": "Tegevused",
|
||||||
|
"Other": "Muud",
|
||||||
|
"Usage": "Kasutus",
|
||||||
|
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Lisa ¯\\_(ツ)_/¯ smaili vormindamata teksti algusesse",
|
||||||
|
"Sends a message as plain text, without interpreting it as markdown": "Saadab sõnumi vormindamata tekstina ega tõlgenda seda markdown-vormindusena",
|
||||||
|
"Sends a message as html, without interpreting it as markdown": "Saadab sõnumi html'ina ega tõlgenda seda markdown-vormindusena",
|
||||||
|
"/ddg is not a command": "/ddg ei ole käsk",
|
||||||
|
"You joined the call": "Sina liitusid kõnega",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s liitus kõnega",
|
||||||
|
"Call in progress": "Kõne on pooleli",
|
||||||
|
"You left the call": "Sa lahkusid kõnest",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s lahkus kõnest",
|
||||||
|
"Call ended": "Kõne lõppes",
|
||||||
|
"You started a call": "Sa alustasid kõnet",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s alustas kõnet",
|
||||||
|
"Waiting for answer": "Ootan kõnele vastamist",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s helistab",
|
||||||
|
"You created the room": "Sa lõid jututoa",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s lõi jututoa",
|
||||||
|
"You made the chat encrypted": "Sina võtsid vestlusel kasutuse krüptimise",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s võttis vestlusel kasutuse krüptimise",
|
||||||
|
"You made history visible to new members": "Sina tegid jututoa ajaloo loetavaks uuetele liikmetele",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s tegi jututoa ajaloo loetavaks uuetele liikmetele",
|
||||||
|
"You made history visible to anyone": "Sina tegi jututoa ajaloo loetavaks kõikidele",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s tegi jututoa ajaloo loetavaks kõikidele",
|
||||||
|
"You made history visible to future members": "Sina tegid jututoa ajaloo loetavaks tulevastele liikmetele",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s tegi jututoa ajaloo loetavaks tulevastele liikmetele",
|
||||||
|
"You were invited": "Sina said kutse",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s sai kutse",
|
||||||
|
"You left": "Sina lahkusid",
|
||||||
|
"%(targetName)s left": "%(targetName)s lahkus",
|
||||||
|
"You were kicked (%(reason)s)": "Sind müksati jututoast välja (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s müksati jututoast välja (%(reason)s)",
|
||||||
|
"You were kicked": "Sind müksati jututoast välja",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s müksati jututoast välja",
|
||||||
|
"You rejected the invite": "Sa lükkasid kutse tagasi",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s lükkas kutse tagasi",
|
||||||
|
"You were uninvited": "Sinult võeti kutse tagasi",
|
||||||
|
"%(targetName)s was uninvited": "%(targetName)s'lt võeti kutse tagasi",
|
||||||
|
"You joined": "Sina liitusid",
|
||||||
|
"%(targetName)s joined": "%(targetName)s liitus",
|
||||||
|
"You changed your name": "Sa muutsid oma nime",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s muutis oma nime",
|
||||||
|
"You changed your avatar": "Sa muutsid oma tunnuspilti",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s muutis oma tunnuspilti",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Sina muutsid jututoa nime",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s muutis jututoa nime",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Sina võtsid tagasi kutse kasutajalt %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s võttis kutse tagasi kasutajalt %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Sina kutsusid kasutajat %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s kutsus kasutajat %(targetName)s",
|
||||||
|
"You changed the room topic": "Sina muutsid jututoa teemat",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s muutis jututoa teemat",
|
||||||
|
"Use the improved room list (will refresh to apply changes)": "Kasuta parandaatud jututubade loendit (muudatuse jõustamine eeldab andmete uuesti laadimist)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Näita välimuse seadistustes IRC-tüüpi paigutuse valikut",
|
||||||
|
"Use custom size": "Kasuta kohandatud suurust",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Kasuta veel kompaktsemat \"moodsat\" paigutust",
|
||||||
|
"Cross-signing public keys:": "Avalikud võtmed risttunnustamise jaoks:",
|
||||||
|
"in memory": "on mälus",
|
||||||
|
"not found": "pole leitavad",
|
||||||
|
"Delete %(count)s sessions|other": "Kustuta %(count)s sessiooni",
|
||||||
|
"Delete %(count)s sessions|one": "Kustuta %(count)s sessioon",
|
||||||
|
"ID": "Kasutaja ID",
|
||||||
|
"Public Name": "Avalik nimi",
|
||||||
|
"Last seen": "Viimati nähtud",
|
||||||
|
"Manage": "Halda",
|
||||||
|
"Enable": "Võta kasutusele",
|
||||||
|
"Error saving email notification preferences": "E-posti teel saadetavate teavituste eelistuste salvestamisel tekkis viga",
|
||||||
|
"An error occurred whilst saving your email notification preferences.": "E-posti teel saadetavate teavituste eelistuste salvestamisel tekkis viga.",
|
||||||
|
"Keywords": "Märksõnad",
|
||||||
|
"Enter keywords separated by a comma:": "Sisesta märksõnad ja kasuta eraldajaks koma:",
|
||||||
|
"Failed to change settings": "Seadistuste muutmine ei õnnestunud",
|
||||||
|
"Can't update user notification settings": "Kasutaja teavistuste eelistusi ei õnnestunud uuendada",
|
||||||
|
"Failed to update keywords": "Märksõnade uuendamine ei õnnestunud",
|
||||||
|
"Messages containing <span>keywords</span>": "Sõnumid, mis sisaldavad <span>märksõnu</span>",
|
||||||
|
"Notify me for anything else": "Teavita mind kõigest muust",
|
||||||
|
"Enable notifications for this account": "Võta sellel kasutajakontol kasutusele teavitused",
|
||||||
|
"Clear notifications": "Eemalda kõik teavitused",
|
||||||
|
"All notifications are currently disabled for all targets.": "Kõik teavituste liigid on välja lülitatud.",
|
||||||
|
"Add an email address to configure email notifications": "E-posti teel saadetavate teavituste seadistamiseks lisa e-posti aadress",
|
||||||
|
"Enable email notifications": "Võta kasutusele e-posti teel saadetavad teavitused",
|
||||||
|
"Notifications on the following keywords follow rules which can’t be displayed here:": "Alljärgnevate märksõnadega seotud teavitused järgivad reegleid, misa siin ei saa kuvada:",
|
||||||
|
"Disinvite this user from community?": "Kas võtame sellelt kasutajalt tagasi kutse kogukonnaga liitumiseks?",
|
||||||
|
"Failed to withdraw invitation": "Kutse tühistamine ei õnnestunud",
|
||||||
|
"<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> jututoas %(roomName)s",
|
||||||
|
"Failed to change power level": "Õiguste muutmine ei õnnestunud",
|
||||||
|
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Sa ei saa seda muudatust hiljem tagasi pöörata, sest annad teisele kasutajale samad õigused, mis sinul on.",
|
||||||
|
"Deactivate user?": "Kas blokeerime kasutaja?",
|
||||||
|
"Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Kasutaja blokeerimisel logitakse ta automaatselt välja ning ei lubata enam sisse logida. Lisaks lahkub ta kõikidest jututubadest, mille liige ta parasjagu on. Seda tegevust ei saa tagasi pöörata. Kas sa oled ikka kindel, et soovid selle kasutaja blokeerida?",
|
||||||
|
"Deactivate user": "Blokeeri kasutaja",
|
||||||
|
"Failed to deactivate user": "Kasutaja blokeerimine ei õnnestunud",
|
||||||
|
"This client does not support end-to-end encryption.": "See klient ei toeta läbivat krüptimist.",
|
||||||
|
"Security": "Turvalisus",
|
||||||
|
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Selle vidina kasutamisel võidakse jagada andmeid <helpIcon /> saitidega %(widgetDomain)s ning sinu vidinahalduriga.",
|
||||||
|
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Selle vidina kasutamisel võidakse jagada andmeid <helpIcon /> saitidega %(widgetDomain)s.",
|
||||||
|
"Widgets do not use message encryption.": "Erinevalt sõnumitest vidinad ei kasuta krüptimist.",
|
||||||
|
"Widget added by": "Vidina lisaja",
|
||||||
|
"This widget may use cookies.": "See vidin võib kasutada küpsiseid.",
|
||||||
|
"Delete Widget": "Kustuta vidin",
|
||||||
|
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Vidina kustutamisel eemaldatakse ta kõikide selle jututoa kasutajate jaoks. Kas sa kindlasti soovid seda vidinat eemaldada?",
|
||||||
|
"Delete widget": "Kustuta vidin",
|
||||||
|
"Minimize apps": "Vähenda rakendused",
|
||||||
|
"Maximize apps": "Suurenda rakendused",
|
||||||
|
"Popout widget": "Ava rakendus eraldi aknas",
|
||||||
|
"More options": "Täiendavad seadistused",
|
||||||
|
"Language Dropdown": "Keelevalik",
|
||||||
|
"Manage Integrations": "Halda lõiminguid",
|
||||||
|
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
|
||||||
|
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s liitusid %(count)s korda",
|
||||||
|
"%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s liitusid",
|
||||||
|
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)s liitus %(count)s korda",
|
||||||
|
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s liitus",
|
||||||
|
"%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s lahkusid %(count)s korda",
|
||||||
|
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s lahkusid",
|
||||||
|
"%(oneUser)sleft %(count)s times|other": "%(oneUser)s lahkus %(count)s korda",
|
||||||
|
"%(oneUser)sleft %(count)s times|one": "%(oneUser)s lahkus",
|
||||||
|
"%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s liitusid ja lahkusid %(count)s korda",
|
||||||
|
"%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s liitusid ja lahkusid",
|
||||||
|
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s liitus ja lahkus %(count)s korda",
|
||||||
|
"%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s liitus ja lahkus",
|
||||||
|
"%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s lahkusid ja liitusid uuesti %(count)s korda",
|
||||||
|
"%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s lahkusid ja liitusid uuesti",
|
||||||
|
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s lahkus ja liitus uuesti %(count)s korda",
|
||||||
|
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s lahkus ja liitus uuesti"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2367,5 +2367,55 @@
|
||||||
"Use Recovery Key or Passphrase": "Käytä palautusavainta tai salalausetta",
|
"Use Recovery Key or Passphrase": "Käytä palautusavainta tai salalausetta",
|
||||||
"Use Recovery Key": "Käytä palautusavainta",
|
"Use Recovery Key": "Käytä palautusavainta",
|
||||||
"Create a Recovery Key": "Luo palautusavain",
|
"Create a Recovery Key": "Luo palautusavain",
|
||||||
"Upgrade your Recovery Key": "Päivitä palautusavaimesi"
|
"Upgrade your Recovery Key": "Päivitä palautusavaimesi",
|
||||||
|
"You joined the call": "Liityit puheluun",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s liittyi puheluun",
|
||||||
|
"Call in progress": "Puhelu käynnissä",
|
||||||
|
"You left the call": "Poistuit puhelusta",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s poistui puhelusta",
|
||||||
|
"Call ended": "Puhelu päättyi",
|
||||||
|
"You started a call": "Aloitit puhelun",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s aloitti puhelun",
|
||||||
|
"Waiting for answer": "Odotetaan vastausta",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s soittaa",
|
||||||
|
"You created the room": "Loit huoneen",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s loi huoneen",
|
||||||
|
"You made the chat encrypted": "Otit salauksen käyttöön keskustelussa",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s otti salauksen käyttöön keskustelussa",
|
||||||
|
"You made history visible to new members": "Teit historiasta näkyvän uusille jäsenille",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s teki historiasta näkyvän uusille jäsenille",
|
||||||
|
"You made history visible to anyone": "Teit historiasta näkyvän kaikille",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s teki historiasta näkyvän kaikille",
|
||||||
|
"You made history visible to future members": "Teit historiasta näkyvän tuleville jäsenille",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s teki historiasta näkyvän tuleville jäsenille",
|
||||||
|
"You were invited": "Sinut kutsuttiin",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s kutsuttiin",
|
||||||
|
"You left": "Poistuit",
|
||||||
|
"%(targetName)s left": "%(targetName)s poistui",
|
||||||
|
"You rejected the invite": "Hylkäsit kutsun",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s hylkäsi kutsun",
|
||||||
|
"You were banned (%(reason)s)": "Sait porttikiellon (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s sai porttikiellon (%(reason)s)",
|
||||||
|
"You were banned": "Sait porttikiellon",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s sai porttikiellon",
|
||||||
|
"You joined": "Liityit",
|
||||||
|
"%(targetName)s joined": "%(targetName)s liittyi",
|
||||||
|
"You changed your name": "Vaihdoit nimeäsi",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s vaihtoi nimeään",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Vaihdoit huoneen nimeä",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s vaihtoi huoneen nimeä",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You invited %(targetName)s": "Kutsuit käyttäjän %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s kutsui käyttäjän %(targetName)s",
|
||||||
|
"You changed the room topic": "Vaihdoit huoneen aiheen",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s vaihtoi huoneen aiheen",
|
||||||
|
"Use custom size": "Käytä mukautettua kokoa",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Käytä tiiviimpää 'modernia' asettelua",
|
||||||
|
"Use a system font": "Käytä järjestelmän fonttia",
|
||||||
|
"System font name": "Järjestelmän fontin nimi",
|
||||||
|
"Message layout": "Viestiasettelu",
|
||||||
|
"Compact": "Tiivis",
|
||||||
|
"Modern": "Moderni"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2528,5 +2528,91 @@
|
||||||
"Upgrade your Recovery Key": "Mettre à jour votre clé de récupération",
|
"Upgrade your Recovery Key": "Mettre à jour votre clé de récupération",
|
||||||
"Store your Recovery Key": "Stocker votre clé de récupération",
|
"Store your Recovery Key": "Stocker votre clé de récupération",
|
||||||
"Use the improved room list (in development - will refresh to apply changes)": "Utiliser la liste de salons améliorée (en développement − actualisera pour appliquer les changements)",
|
"Use the improved room list (in development - will refresh to apply changes)": "Utiliser la liste de salons améliorée (en développement − actualisera pour appliquer les changements)",
|
||||||
"Use the improved room list (will refresh to apply changes)": "Utiliser la liste de salons améliorée (actualisera pour appliquer les changements)"
|
"Use the improved room list (will refresh to apply changes)": "Utiliser la liste de salons améliorée (actualisera pour appliquer les changements)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Activer l’option de mise en page IRC dans l’onglet d’apparence",
|
||||||
|
"Use custom size": "Utiliser une taille personnalisée",
|
||||||
|
"Hey you. You're the best!": "Eh vous. Vous êtes les meilleurs !",
|
||||||
|
"Message layout": "Mise en page des messages",
|
||||||
|
"Compact": "Compacte",
|
||||||
|
"Modern": "Moderne",
|
||||||
|
"Use a system font": "Utiliser une police du système",
|
||||||
|
"System font name": "Nom de la police du système",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "L’authenticité de ce message chiffré ne peut pas être garantie sur cet appareil.",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Utiliser une mise en page « moderne » plus compacte",
|
||||||
|
"You joined the call": "Vous avez rejoint l’appel",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s a rejoint l’appel",
|
||||||
|
"Call in progress": "Appel en cours",
|
||||||
|
"You left the call": "Vous avez quitté l’appel",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s a quitté l’appel",
|
||||||
|
"Call ended": "Appel terminé",
|
||||||
|
"You started a call": "Vous avez démarré un appel",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s a démarré un appel",
|
||||||
|
"Waiting for answer": "En attente d’une réponse",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s appelle",
|
||||||
|
"You created the room": "Vous avez créé le salon",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s a créé le salon",
|
||||||
|
"You made the chat encrypted": "Vous avez activé le chiffrement de la discussion",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s a activé le chiffrement de la discussion",
|
||||||
|
"You made history visible to new members": "Vous avez rendu l’historique visible aux nouveaux membres",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s a rendu l’historique visible aux nouveaux membres",
|
||||||
|
"You made history visible to anyone": "Vous avez rendu l’historique visible à tout le monde",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s a rendu l’historique visible à tout le monde",
|
||||||
|
"You made history visible to future members": "Vous avez rendu l’historique visible aux futurs membres",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s a rendu l’historique visible aux futurs membres",
|
||||||
|
"You were invited": "Vous avez été invité",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s a été invité·e",
|
||||||
|
"You left": "Vous êtes parti·e",
|
||||||
|
"%(targetName)s left": "%(targetName)s est parti·e",
|
||||||
|
"You were kicked (%(reason)s)": "Vous avez été expulsé·e (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s a été expulsé·e (%(reason)s)",
|
||||||
|
"You were kicked": "Vous avez été expulsé·e",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s a été expulsé·e",
|
||||||
|
"You rejected the invite": "Vous avez rejeté l’invitation",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s a rejeté l’invitation",
|
||||||
|
"You were uninvited": "Votre invitation a été révoquée",
|
||||||
|
"%(targetName)s was uninvited": "L’invitation de %(targetName)s a été révoquée",
|
||||||
|
"You were banned (%(reason)s)": "Vous avez été banni·e (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s a été banni·e (%(reason)s)",
|
||||||
|
"You were banned": "Vous avez été banni·e",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s a été banni·e",
|
||||||
|
"You joined": "Vous avez rejoint le salon",
|
||||||
|
"%(targetName)s joined": "%(targetName)s a rejoint le salon",
|
||||||
|
"You changed your name": "Vous avez changé votre nom",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s a changé son nom",
|
||||||
|
"You changed your avatar": "Vous avez changé votre avatar",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s a changé son avatar",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s : %(message)s",
|
||||||
|
"You changed the room name": "Vous avez changé le nom du salon",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s a changé le nom du salon",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s : %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s : %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Vous avez révoqué l’invitation de %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s a révoqué l’invitation de %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Vous avez invité %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s a invité %(targetName)s",
|
||||||
|
"You changed the room topic": "Vous avez changé le sujet du salon",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s a changé le sujet du salon",
|
||||||
|
"New spinner design": "Nouveau design du spinner",
|
||||||
|
"Message deleted on %(date)s": "Message supprimé le %(date)s",
|
||||||
|
"Wrong file type": "Mauvais type de fichier",
|
||||||
|
"Wrong Recovery Key": "Mauvaise clé de récupération",
|
||||||
|
"Invalid Recovery Key": "Clé de récupération non valide",
|
||||||
|
"Security Phrase": "Phrase de sécurité",
|
||||||
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Saisissez votre phrase de sécurité ou <button>utilisez votre clé de sécurité</button> pour continuer.",
|
||||||
|
"Security Key": "Clé de sécurité",
|
||||||
|
"Use your Security Key to continue.": "Utilisez votre clé de sécurité pour continuer.",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Protection afin d’éviter de perdre l’accès aux messages et données chiffrés en sauvegardant les clés de chiffrement sur votre serveur.",
|
||||||
|
"Generate a Security Key": "Générer une clé de sécurité",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "Nous génèrerons une clé de sécurité que vous devrez stocker dans un endroit sûr, comme un gestionnaire de mots de passe ou un coffre.",
|
||||||
|
"Enter a Security Phrase": "Saisir une phrase de sécurité",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Utilisez une phrase secrète que vous êtes seul·e à connaître et enregistrez éventuellement une clé de sécurité à utiliser pour la sauvegarde.",
|
||||||
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "Saisissez une phrase de sécurité que vous seul·e connaissez, car elle est utilisée pour protéger vos données. Pour plus de sécurité, vous ne devriez pas réutiliser le mot de passe de votre compte.",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "Stockez votre clé de sécurité dans un endroit sûr, comme un gestionnaire de mots de passe ou un coffre, car elle est utilisée pour protéger vos données chiffrées.",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Si vous annulez maintenant, vous pourriez perdre vos messages et données chiffrés si vous perdez l’accès à vos identifiants.",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "Vous pouvez aussi configurer la sauvegarde sécurisée et gérer vos clés depuis les paramètres.",
|
||||||
|
"Set up Secure backup": "Configurer la sauvegarde sécurisée",
|
||||||
|
"Set a Security Phrase": "Définir une phrase de sécurité",
|
||||||
|
"Confirm Security Phrase": "Confirmer la phrase de sécurité",
|
||||||
|
"Save your Security Key": "Sauvegarder votre clé de sécurité"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"This email address is already in use": "Xa se está a usar este correo",
|
"This email address is already in use": "Xa se está a usar este email",
|
||||||
"This phone number is already in use": "Xa se está a usar este teléfono",
|
"This phone number is already in use": "Xa se está a usar este teléfono",
|
||||||
"Failed to verify email address: make sure you clicked the link in the email": "Fallo na verificación do enderezo de correo: asegúrese de ter picado na ligazón do correo",
|
"Failed to verify email address: make sure you clicked the link in the email": "Fallo na verificación do enderezo de correo: asegúrese de ter picado na ligazón do correo",
|
||||||
"The remote side failed to pick up": "O interlocutor non respondeu",
|
"The remote side failed to pick up": "O correspondente non respondeu",
|
||||||
"Unable to capture screen": "Non se puido capturar a pantalla",
|
"Unable to capture screen": "Non se puido capturar a pantalla",
|
||||||
"Existing Call": "Rexistro de chamadas",
|
"Existing Call": "Rexistro de chamadas",
|
||||||
"You are already in a call.": "Xa está nunha chamada.",
|
"You are already in a call.": "Xa estás nunha chamada.",
|
||||||
"VoIP is unsupported": "Sen soporte para VoIP",
|
"VoIP is unsupported": "Sen soporte para VoIP",
|
||||||
"You cannot place VoIP calls in this browser.": "Non pode establecer chamadas VoIP neste navegador.",
|
"You cannot place VoIP calls in this browser.": "Non poden establecer chamadas VoIP neste navegador.",
|
||||||
"You cannot place a call with yourself.": "Non pode facer unha chamada a si mesmo.",
|
"You cannot place a call with yourself.": "Non podes facer unha chamada a ti mesma.",
|
||||||
"Warning!": "Aviso!",
|
"Warning!": "Aviso!",
|
||||||
"Call Failed": "Fallou a chamada",
|
"Call Failed": "Fallou a chamada",
|
||||||
"Review Devices": "Revisar dispositivos",
|
"Review Devices": "Revisar dispositivos",
|
||||||
|
@ -51,9 +51,9 @@
|
||||||
"Add rooms to the community": "Engadir salas á comunidade",
|
"Add rooms to the community": "Engadir salas á comunidade",
|
||||||
"Room name or alias": "Nome da sala ou alcume",
|
"Room name or alias": "Nome da sala ou alcume",
|
||||||
"Add to community": "Engadir á comunidade",
|
"Add to community": "Engadir á comunidade",
|
||||||
"Failed to invite the following users to %(groupId)s:": "Fallo ao convidar os seguintes usuarios a %(groupId)s:",
|
"Failed to invite the following users to %(groupId)s:": "Fallo ao convidar ás seguintes usuarias a %(groupId)s:",
|
||||||
"Failed to invite users to community": "Houbo un fallo convidando usuarios á comunidade",
|
"Failed to invite users to community": "Houbo un fallo convidando usuarias á comunidade",
|
||||||
"Failed to invite users to %(groupId)s": "Houbo un fallo convidando usuarios a %(groupId)s",
|
"Failed to invite users to %(groupId)s": "Houbo un fallo convidando usuarias a %(groupId)s",
|
||||||
"Failed to add the following rooms to %(groupId)s:": "Fallo ao engadir as seguintes salas a %(groupId)s:",
|
"Failed to add the following rooms to %(groupId)s:": "Fallo ao engadir as seguintes salas a %(groupId)s:",
|
||||||
"Riot does not have permission to send you notifications - please check your browser settings": "Riot non ten permiso para enviarlle notificacións: comprobe os axustes do navegador",
|
"Riot does not have permission to send you notifications - please check your browser settings": "Riot non ten permiso para enviarlle notificacións: comprobe os axustes do navegador",
|
||||||
"Riot was not given permission to send notifications - please try again": "Riot non ten permiso para enviar notificacións: inténteo de novo",
|
"Riot was not given permission to send notifications - please try again": "Riot non ten permiso para enviar notificacións: inténteo de novo",
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
"Start a chat": "Iniciar unha conversa",
|
"Start a chat": "Iniciar unha conversa",
|
||||||
"Operation failed": "Fallou a operación",
|
"Operation failed": "Fallou a operación",
|
||||||
"Failed to invite": "Fallou o convite",
|
"Failed to invite": "Fallou o convite",
|
||||||
"Failed to invite the following users to the %(roomName)s room:": "Houbo un fallo convidando os seguintes usuarios á sala %(roomName)s:",
|
"Failed to invite the following users to the %(roomName)s room:": "Houbo un fallo convidando as seguintes usuarias á sala %(roomName)s:",
|
||||||
"You need to be logged in.": "Precisa estar conectada.",
|
"You need to be logged in.": "Precisa estar conectada.",
|
||||||
"You need to be able to invite users to do that.": "Precisa autorización para convidar a outros usuarias para poder facer iso.",
|
"You need to be able to invite users to do that.": "Precisa autorización para convidar a outros usuarias para poder facer iso.",
|
||||||
"Unable to create widget.": "Non se puido crear o trebello.",
|
"Unable to create widget.": "Non se puido crear o trebello.",
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
"Unrecognised room alias:": "Alcumes de sala non recoñecidos:",
|
"Unrecognised room alias:": "Alcumes de sala non recoñecidos:",
|
||||||
"Ignored user": "Usuaria ignorada",
|
"Ignored user": "Usuaria ignorada",
|
||||||
"You are now ignoring %(userId)s": "Agora está a ignorar %(userId)s",
|
"You are now ignoring %(userId)s": "Agora está a ignorar %(userId)s",
|
||||||
"Unignored user": "Usuarios non ignorados",
|
"Unignored user": "Usuarias non ignoradas",
|
||||||
"You are no longer ignoring %(userId)s": "Xa non está a ignorar a %(userId)s",
|
"You are no longer ignoring %(userId)s": "Xa non está a ignorar a %(userId)s",
|
||||||
"Verified key": "Chave verificada",
|
"Verified key": "Chave verificada",
|
||||||
"Reason": "Razón",
|
"Reason": "Razón",
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
"Enable automatic language detection for syntax highlighting": "Activar a detección automática de idioma para o resalte da sintaxe",
|
"Enable automatic language detection for syntax highlighting": "Activar a detección automática de idioma para o resalte da sintaxe",
|
||||||
"Automatically replace plain text Emoji": "Substituír automaticamente Emoji en texto plano",
|
"Automatically replace plain text Emoji": "Substituír automaticamente Emoji en texto plano",
|
||||||
"Enable inline URL previews by default": "Activar por defecto as vistas previas en liña de URL",
|
"Enable inline URL previews by default": "Activar por defecto as vistas previas en liña de URL",
|
||||||
"Enable URL previews for this room (only affects you)": "Activar avista previa de URL nesta sala (só lle afecta a vostede)",
|
"Enable URL previews for this room (only affects you)": "Activar avista previa de URL nesta sala (só che afesta a ti)",
|
||||||
"Enable URL previews by default for participants in this room": "Activar a vista previa de URL por defecto para as participantes nesta sala",
|
"Enable URL previews by default for participants in this room": "Activar a vista previa de URL por defecto para as participantes nesta sala",
|
||||||
"Room Colour": "Cor da sala",
|
"Room Colour": "Cor da sala",
|
||||||
"Active call (%(roomName)s)": "Chamada activa (%(roomName)s)",
|
"Active call (%(roomName)s)": "Chamada activa (%(roomName)s)",
|
||||||
|
@ -200,15 +200,15 @@
|
||||||
"device id: ": "id dispositivo: ",
|
"device id: ": "id dispositivo: ",
|
||||||
"Disinvite": "Retirar convite",
|
"Disinvite": "Retirar convite",
|
||||||
"Kick": "Expulsar",
|
"Kick": "Expulsar",
|
||||||
"Disinvite this user?": "Retirar convite a este usuario?",
|
"Disinvite this user?": "Retirar convite a esta usuaria?",
|
||||||
"Kick this user?": "Expulsar este usuario?",
|
"Kick this user?": "Expulsar esta usuaria?",
|
||||||
"Failed to kick": "Fallo ao expulsar",
|
"Failed to kick": "Fallo ao expulsar",
|
||||||
"Unban": "Non bloquear",
|
"Unban": "Non bloquear",
|
||||||
"Ban": "Bloquear",
|
"Ban": "Bloquear",
|
||||||
"Unban this user?": "Non bloquear este usuario?",
|
"Unban this user?": "¿Non bloquear esta usuaria?",
|
||||||
"Ban this user?": "Bloquear a este usuario?",
|
"Ban this user?": "¿Bloquear a esta usuaria?",
|
||||||
"Failed to ban user": "Fallo ao bloquear usuario",
|
"Failed to ban user": "Fallo ao bloquear usuaria",
|
||||||
"Failed to mute user": "Fallo ao acalar usuario",
|
"Failed to mute user": "Fallo ó silenciar usuaria",
|
||||||
"Failed to toggle moderator status": "Fallo ao mudar a estado de moderador",
|
"Failed to toggle moderator status": "Fallo ao mudar a estado de moderador",
|
||||||
"Failed to change power level": "Fallo ao cambiar o nivel de permisos",
|
"Failed to change power level": "Fallo ao cambiar o nivel de permisos",
|
||||||
"Are you sure?": "Está segura?",
|
"Are you sure?": "Está segura?",
|
||||||
|
@ -288,9 +288,9 @@
|
||||||
"Banned by %(displayName)s": "Non aceptado por %(displayName)s",
|
"Banned by %(displayName)s": "Non aceptado por %(displayName)s",
|
||||||
"unknown error code": "código de fallo descoñecido",
|
"unknown error code": "código de fallo descoñecido",
|
||||||
"Failed to forget room %(errCode)s": "Fallo ao esquecer sala %(errCode)s",
|
"Failed to forget room %(errCode)s": "Fallo ao esquecer sala %(errCode)s",
|
||||||
"Privileged Users": "Usuarios con privilexios",
|
"Privileged Users": "Usuarias con privilexios",
|
||||||
"No users have specific privileges in this room": "Non hai usuarios con privilexios específicos nesta sala",
|
"No users have specific privileges in this room": "Non hai usuarias con privilexios específicos nesta sala",
|
||||||
"Banned users": "Usuarios excluídos",
|
"Banned users": "Usuarias excluídas",
|
||||||
"This room is not accessible by remote Matrix servers": "Esta sala non é accesible por servidores Matrix remotos",
|
"This room is not accessible by remote Matrix servers": "Esta sala non é accesible por servidores Matrix remotos",
|
||||||
"Leave room": "Deixar a sala",
|
"Leave room": "Deixar a sala",
|
||||||
"Favourite": "Favorita",
|
"Favourite": "Favorita",
|
||||||
|
@ -378,9 +378,9 @@
|
||||||
"You're not currently a member of any communities.": "Ate o momento non é membro de ningunha comunidade.",
|
"You're not currently a member of any communities.": "Ate o momento non é membro de ningunha comunidade.",
|
||||||
"Unknown Address": "Enderezo descoñecido",
|
"Unknown Address": "Enderezo descoñecido",
|
||||||
"Allow": "Permitir",
|
"Allow": "Permitir",
|
||||||
"Delete Widget": "Eliminar trebello",
|
"Delete Widget": "Eliminar widget",
|
||||||
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Quitando un trebello elimínao para todas os usuarios desta sala. Está seguro de querer eliminar este trebello?",
|
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Quitando un trebello elimínalo para todas as usuarias desta sala. ¿tes certeza de querer eliminar este widget?",
|
||||||
"Delete widget": "Eliminar trebello",
|
"Delete widget": "Eliminar widget",
|
||||||
"Minimize apps": "Minimizar apps",
|
"Minimize apps": "Minimizar apps",
|
||||||
"Edit": "Editar",
|
"Edit": "Editar",
|
||||||
"Create new room": "Crear unha nova sala",
|
"Create new room": "Crear unha nova sala",
|
||||||
|
@ -459,7 +459,7 @@
|
||||||
"Try using one of the following valid address types: %(validTypesList)s.": "Intentar utilizar algún dos seguintes tipos de enderezo válidos: %(validTypesList)s.",
|
"Try using one of the following valid address types: %(validTypesList)s.": "Intentar utilizar algún dos seguintes tipos de enderezo válidos: %(validTypesList)s.",
|
||||||
"You have entered an invalid address.": "Introduciu un enderezo non válido.",
|
"You have entered an invalid address.": "Introduciu un enderezo non válido.",
|
||||||
"Confirm Removal": "Confirme a retirada",
|
"Confirm Removal": "Confirme a retirada",
|
||||||
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Está certa de que quere quitar (eliminar) este evento? Saiba que si elimina un nome de sala ou cambia o asunto, podería desfacer o cambio.",
|
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Estás certa de que queres quitar (eliminar) este evento? Debes saber que se eliminas un nome de sala ou cambias o asunto, poderías desfacer o cambio.",
|
||||||
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "Os ID de comunidade só poden conter caracteres a-z, 0-9, or '=_-./'",
|
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "Os ID de comunidade só poden conter caracteres a-z, 0-9, or '=_-./'",
|
||||||
"Community IDs cannot be empty.": "O ID de comunidade non pode quedar baldeiro.",
|
"Community IDs cannot be empty.": "O ID de comunidade non pode quedar baldeiro.",
|
||||||
"Something went wrong whilst creating your community": "Algo fallou mentres se creaba a súa comunidade",
|
"Something went wrong whilst creating your community": "Algo fallou mentres se creaba a súa comunidade",
|
||||||
|
@ -481,7 +481,7 @@
|
||||||
"Ignore request": "Ignorar petición",
|
"Ignore request": "Ignorar petición",
|
||||||
"Encryption key request": "Petición de chave de cifrado",
|
"Encryption key request": "Petición de chave de cifrado",
|
||||||
"Unable to restore session": "Non se puido restaurar a sesión",
|
"Unable to restore session": "Non se puido restaurar a sesión",
|
||||||
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Si anteriormente utilizou unha versión máis recente de Riot, a súa sesión podería non ser compatible con esta versión. Peche esta ventá e volva a versión máis recente.",
|
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Se anteriormente utilizaches unha versión máis recente de Riot, a túa sesión podería non ser compatible con esta versión. Pecha esta ventá e volve á versión máis recente.",
|
||||||
"Invalid Email Address": "Enderezo de correo non válido",
|
"Invalid Email Address": "Enderezo de correo non válido",
|
||||||
"This doesn't appear to be a valid email address": "Este non semella ser un enderezo de correo válido",
|
"This doesn't appear to be a valid email address": "Este non semella ser un enderezo de correo válido",
|
||||||
"Verification Pending": "Verificación pendente",
|
"Verification Pending": "Verificación pendente",
|
||||||
|
@ -513,9 +513,9 @@
|
||||||
"Add a Room": "Engadir unha sala",
|
"Add a Room": "Engadir unha sala",
|
||||||
"Failed to remove the room from the summary of %(groupId)s": "Algo fallou ao quitar a sala do resumo de %(groupId)s",
|
"Failed to remove the room from the summary of %(groupId)s": "Algo fallou ao quitar a sala do resumo de %(groupId)s",
|
||||||
"The room '%(roomName)s' could not be removed from the summary.": "A sala '%(roomName)s' non se puido eliminar do resumo.",
|
"The room '%(roomName)s' could not be removed from the summary.": "A sala '%(roomName)s' non se puido eliminar do resumo.",
|
||||||
"Add users to the community summary": "Engadir usuarios ao resumo da comunidade",
|
"Add users to the community summary": "Engadir usuarias ó resumo da comunidade",
|
||||||
"Who would you like to add to this summary?": "A quen desexa engadir a este resumo?",
|
"Who would you like to add to this summary?": "A quen desexa engadir a este resumo?",
|
||||||
"Failed to add the following users to the summary of %(groupId)s:": "Algo fallou ao engadir aos seguintes usuarios ao resumo de %(groupId)s:",
|
"Failed to add the following users to the summary of %(groupId)s:": "Algo fallou ó engadir ás seguintes usuarias ó resumo de %(groupId)s:",
|
||||||
"Add a User": "Engadir unha usuaria",
|
"Add a User": "Engadir unha usuaria",
|
||||||
"Failed to remove a user from the summary of %(groupId)s": "Algo fallou ao eliminar a usuaria do resumo de %(groupId)s",
|
"Failed to remove a user from the summary of %(groupId)s": "Algo fallou ao eliminar a usuaria do resumo de %(groupId)s",
|
||||||
"The user '%(displayName)s' could not be removed from the summary.": "A usuaria '%(displayName)s' non se puido eliminar do resumo.",
|
"The user '%(displayName)s' could not be removed from the summary.": "A usuaria '%(displayName)s' non se puido eliminar do resumo.",
|
||||||
|
@ -530,9 +530,9 @@
|
||||||
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Estas salas móstranse aos membros da comunidade na páxina da comunidade. Os participantes da comunidade poden unirse ás salas premendo nelas.",
|
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Estas salas móstranse aos membros da comunidade na páxina da comunidade. Os participantes da comunidade poden unirse ás salas premendo nelas.",
|
||||||
"Add rooms to this community": "Engadir salas a esta comunidade",
|
"Add rooms to this community": "Engadir salas a esta comunidade",
|
||||||
"Featured Rooms:": "Salas destacadas:",
|
"Featured Rooms:": "Salas destacadas:",
|
||||||
"Featured Users:": "Usuarios destacados:",
|
"Featured Users:": "Usuarias destacadas:",
|
||||||
"%(inviter)s has invited you to join this community": "%(inviter)s convidoute a entrar nesta comunidade",
|
"%(inviter)s has invited you to join this community": "%(inviter)s convidoute a entrar nesta comunidade",
|
||||||
"You are an administrator of this community": "Vostede administra esta comunidade",
|
"You are an administrator of this community": "Administras esta comunidade",
|
||||||
"You are a member of this community": "É membro desta comunidade",
|
"You are a member of this community": "É membro desta comunidade",
|
||||||
"Your community hasn't got a Long Description, a HTML page to show to community members.<br />Click here to open settings and give it one!": "A súa comunidade non ten unha descrición longa, ou unha páxina HTML que lle mostrar aos seus participantes.<br />Pulse aquí para abrir os axustes e publicar unha!",
|
"Your community hasn't got a Long Description, a HTML page to show to community members.<br />Click here to open settings and give it one!": "A súa comunidade non ten unha descrición longa, ou unha páxina HTML que lle mostrar aos seus participantes.<br />Pulse aquí para abrir os axustes e publicar unha!",
|
||||||
"Long Description (HTML)": "Descrición longa (HTML)",
|
"Long Description (HTML)": "Descrición longa (HTML)",
|
||||||
|
@ -592,7 +592,7 @@
|
||||||
"Import E2E room keys": "Importar chaves E2E da sala",
|
"Import E2E room keys": "Importar chaves E2E da sala",
|
||||||
"Cryptography": "Criptografía",
|
"Cryptography": "Criptografía",
|
||||||
"Analytics": "Analytics",
|
"Analytics": "Analytics",
|
||||||
"Riot collects anonymous analytics to allow us to improve the application.": "Riot recolle información analítica anónima para permitirnos mellorar o aplicativo.",
|
"Riot collects anonymous analytics to allow us to improve the application.": "Riot recolle información analítica anónima para permitirnos mellorar a aplicación.",
|
||||||
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "A intimidade impórtanos, así que non recollemos información personal ou identificable nos datos dos nosos análises.",
|
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "A intimidade impórtanos, así que non recollemos información personal ou identificable nos datos dos nosos análises.",
|
||||||
"Learn more about how we use analytics.": "Saber máis sobre como utilizamos analytics.",
|
"Learn more about how we use analytics.": "Saber máis sobre como utilizamos analytics.",
|
||||||
"Labs": "Labs",
|
"Labs": "Labs",
|
||||||
|
@ -651,7 +651,7 @@
|
||||||
"Emoji": "Emoji",
|
"Emoji": "Emoji",
|
||||||
"Notify the whole room": "Notificar a toda a sala",
|
"Notify the whole room": "Notificar a toda a sala",
|
||||||
"Room Notification": "Notificación da sala",
|
"Room Notification": "Notificación da sala",
|
||||||
"Users": "Usuarios",
|
"Users": "Usuarias",
|
||||||
"unknown device": "dispositivo descoñecido",
|
"unknown device": "dispositivo descoñecido",
|
||||||
"NOT verified": "Non validado",
|
"NOT verified": "Non validado",
|
||||||
"verified": "validado",
|
"verified": "validado",
|
||||||
|
@ -676,12 +676,12 @@
|
||||||
"Confirm passphrase": "Confirme a frase de paso",
|
"Confirm passphrase": "Confirme a frase de paso",
|
||||||
"Export": "Exportar",
|
"Export": "Exportar",
|
||||||
"Import room keys": "Importar chaves de sala",
|
"Import room keys": "Importar chaves de sala",
|
||||||
"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.": "Este proceso permítelle importar chaves de cifrado que vostede exportou de outro cliente Matrix. Así poderá descifrar calquera mensaxe que o outro cliente puidese cifrar.",
|
"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.": "Este proceso permíteche importar chaves de cifrado que exportaches doutro cliente Matrix. Así poderás descifrar calquera mensaxe que o outro cliente puidese cifrar.",
|
||||||
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "O ficheiro exportado estará protexido con unha frase de paso. Debe introducir aquí esa frase de paso para descifrar o ficheiro.",
|
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "O ficheiro exportado estará protexido con unha frase de paso. Debe introducir aquí esa frase de paso para descifrar o ficheiro.",
|
||||||
"File to import": "Ficheiro a importar",
|
"File to import": "Ficheiro a importar",
|
||||||
"Import": "Importar",
|
"Import": "Importar",
|
||||||
"The information being sent to us to help make Riot.im better includes:": "A información enviada a Riot.im para axudarnos a mellorar inclúe:",
|
"The information being sent to us to help make Riot.im better includes:": "A información enviada a Riot.im para axudarnos a mellorar inclúe:",
|
||||||
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Se esta páxina inclúe información identificable como ID de grupo, usuario ou sala, estes datos son eliminados antes de ser enviados ao servidor.",
|
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Se esta páxina inclúe información identificable como ID de grupo, usuaria ou sala, estes datos son eliminados antes de ser enviados ó servidor.",
|
||||||
"The platform you're on": "A plataforma na que está",
|
"The platform you're on": "A plataforma na que está",
|
||||||
"The version of Riot.im": "A versión de Riot.im",
|
"The version of Riot.im": "A versión de Riot.im",
|
||||||
"Your language of choice": "A súa preferencia de idioma",
|
"Your language of choice": "A súa preferencia de idioma",
|
||||||
|
@ -703,12 +703,12 @@
|
||||||
"Display your community flair in rooms configured to show it.": "Mostrar a popularidade da túa comunidade nas salas configuradas para que a mostren.",
|
"Display your community flair in rooms configured to show it.": "Mostrar a popularidade da túa comunidade nas salas configuradas para que a mostren.",
|
||||||
"Did you know: you can use communities to filter your Riot.im experience!": "Sabías que podes usar as comunidades para filtrar a túa experiencia en Riot.im!",
|
"Did you know: you can use communities to filter your Riot.im experience!": "Sabías que podes usar as comunidades para filtrar a túa experiencia en Riot.im!",
|
||||||
"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.": "Para establecer un filtro, arrastra un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Podes premer nun avatar no panel de filtrado en calquera momento para ver só salas e xente asociada a esa comunidade.",
|
"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.": "Para establecer un filtro, arrastra un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Podes premer nun avatar no panel de filtrado en calquera momento para ver só salas e xente asociada a esa comunidade.",
|
||||||
"Deops user with given id": "Degradar o usuario con esa ID",
|
"Deops user with given id": "Degradar á usuaria con ese ID",
|
||||||
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Visto por %(displayName)s(%(userName)s en %(dateTime)s",
|
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Visto por %(displayName)s(%(userName)s en %(dateTime)s",
|
||||||
"Code": "Código",
|
"Code": "Código",
|
||||||
"Unable to join community": "Non te puideches unir a comunidade",
|
"Unable to join community": "Non te puideches unir a comunidade",
|
||||||
"Unable to leave community": "Non se puido deixar a comunidade",
|
"Unable to leave community": "Non se puido deixar a comunidade",
|
||||||
"Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Os cambios realizados a súa comunidade <bold1>name</bold1> e <bold2>avatar</bold2> poida que non os vexan outros usuarios ate dentro de 30 minutos.",
|
"Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Os cambios realizados á túa comunidade <bold1>nome</bold1> e <bold2>avatar</bold2> poida que non os vexan outras usuarias ate dentro de 30 minutos.",
|
||||||
"Join this community": "Únete a esta comunidade",
|
"Join this community": "Únete a esta comunidade",
|
||||||
"Leave this community": "Deixar esta comunidade",
|
"Leave this community": "Deixar esta comunidade",
|
||||||
"If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Se enviaches un informe de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso da aplicación incluíndo o teu nome de usuaria, os IDs ou alcumes das salas e grupos que visitaches e os nomes de usuaria de outras persoas. Non conteñen mensaxes.",
|
"If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Se enviaches un informe de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso da aplicación incluíndo o teu nome de usuaria, os IDs ou alcumes das salas e grupos que visitaches e os nomes de usuaria de outras persoas. Non conteñen mensaxes.",
|
||||||
|
@ -778,7 +778,7 @@
|
||||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot utiliza características avanzadas do navegador, algunhas das cales non están dispoñibles ou son experimentais no seu navegador actual.",
|
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot utiliza características avanzadas do navegador, algunhas das cales non están dispoñibles ou son experimentais no seu navegador actual.",
|
||||||
"Developer Tools": "Ferramentas para desenvolver",
|
"Developer Tools": "Ferramentas para desenvolver",
|
||||||
"Preparing to send logs": "Preparándose para enviar informe",
|
"Preparing to send logs": "Preparándose para enviar informe",
|
||||||
"Remember, you can always set an email address in user settings if you change your mind.": "Lembre que sempre poderá poñer un enderezo de correo nos axustes de usuario se cambiase de idea.",
|
"Remember, you can always set an email address in user settings if you change your mind.": "Lembra que sempre poderás poñer un enderezo de email nos axustes de usuaria se cambiases de idea.",
|
||||||
"Explore Account Data": "Ollar datos da conta",
|
"Explore Account Data": "Ollar datos da conta",
|
||||||
"All messages (noisy)": "Todas as mensaxes (alto)",
|
"All messages (noisy)": "Todas as mensaxes (alto)",
|
||||||
"Saturday": "Sábado",
|
"Saturday": "Sábado",
|
||||||
|
@ -819,7 +819,7 @@
|
||||||
"Back": "Atrás",
|
"Back": "Atrás",
|
||||||
"Reply": "Resposta",
|
"Reply": "Resposta",
|
||||||
"Show message in desktop notification": "Mostrar mensaxe nas notificacións de escritorio",
|
"Show message in desktop notification": "Mostrar mensaxe nas notificacións de escritorio",
|
||||||
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Os informes de depuración conteñen datos de utilización do aplicativo como o seu nome de usuario, os IDs ou alcumes de salas e grupos que vostede visitou e os nomes de usuarios doutras usuarias. Non conteñen mensaxes.",
|
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Os informes de depuración conteñen datos de utilización da aplicación como o teu nome de usuaria, os IDs ou alias de salas e grupos que visitachese os nomes de usuaria doutras usuarias. Non conteñen mensaxes.",
|
||||||
"Unhide Preview": "Desagochar a vista previa",
|
"Unhide Preview": "Desagochar a vista previa",
|
||||||
"Unable to join network": "Non se puido conectar ca rede",
|
"Unable to join network": "Non se puido conectar ca rede",
|
||||||
"You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Pode que os configurases nun cliente diferente de Riot. Non podes establecelos desde Riot pero aínda así aplicaranse",
|
"You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Pode que os configurases nun cliente diferente de Riot. Non podes establecelos desde Riot pero aínda así aplicaranse",
|
||||||
|
@ -845,7 +845,7 @@
|
||||||
"View Source": "Ver fonte",
|
"View Source": "Ver fonte",
|
||||||
"Event Content": "Contido do evento",
|
"Event Content": "Contido do evento",
|
||||||
"Thank you!": "Grazas!",
|
"Thank you!": "Grazas!",
|
||||||
"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!": "Co seu navegador actual a aparencia e uso do aplicativo poderían estar totalmente falseadas, e algunhas características poderían non funcionar. Se quere pode continuar, pero debe ser consciente de que poden haber fallos!",
|
"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!": "Co teu navegador actual a aparencia e uso da aplicación poderían estar totalmente falseadas, e algunhas características poderían non funcionar. Se queres podes continuar, pero debes ser consciente de que pode haber fallos!",
|
||||||
"Checking for an update...": "Comprobando as actualizacións...",
|
"Checking for an update...": "Comprobando as actualizacións...",
|
||||||
"There are advanced notifications which are not shown here": "Existen notificacións avanzadas que non se mostran aquí",
|
"There are advanced notifications which are not shown here": "Existen notificacións avanzadas que non se mostran aquí",
|
||||||
"Every page you use in the app": "Cada páxina que use na aplicación",
|
"Every page you use in the app": "Cada páxina que use na aplicación",
|
||||||
|
@ -867,18 +867,18 @@
|
||||||
"Enable widget screenshots on supported widgets": "Activar as capturas de trebellos para aqueles que as permiten",
|
"Enable widget screenshots on supported widgets": "Activar as capturas de trebellos para aqueles que as permiten",
|
||||||
"Share Link to User": "Compartir a ligazón coa usuaria",
|
"Share Link to User": "Compartir a ligazón coa usuaria",
|
||||||
"Share room": "Compartir sala",
|
"Share room": "Compartir sala",
|
||||||
"Muted Users": "Usuarios silenciados",
|
"Muted Users": "Usuarias silenciadas",
|
||||||
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Axuda a mellorar Riot.im enviando <UsageDataLink>os datos anónimos de uso</UsageDataLink>. Usaremos unha cookie (le aquí a nosa <PolicyLink>Política de Cookies</PolicyLink>).",
|
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Axuda a mellorar Riot.im enviando <UsageDataLink>os datos anónimos de uso</UsageDataLink>. Usaremos unha cookie (le aquí a nosa <PolicyLink>Política de Cookies</PolicyLink>).",
|
||||||
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Axuda a mellorar Riot.im enviando <UsageDataLink>datos anónimos de uso</UsageDataLink>. Esto usará unha cookie.",
|
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Axuda a mellorar Riot.im enviando <UsageDataLink>datos anónimos de uso</UsageDataLink>. Esto usará unha cookie.",
|
||||||
"Yes, I want to help!": "Si, quero axudar!",
|
"Yes, I want to help!": "Si, quero axudar!",
|
||||||
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "Iso fará que a súa deixe de ter uso de xeito permanente. Non poderá acceder e ninguén vai a poder volver a rexistrar esa mesma ID de usuario. Suporá que saía de todas as salas de conversas nas que estaba e eliminará os detalles da súa conta do servidores de identificación.<b>Isto non se poderá desfacer</b>",
|
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "Iso fará que a túa deixe de ter uso de xeito permanente. Non poderás acceder e ninguén vai a poder volver a rexistrar esa mesma ID de usuaria. Suporá que sairás de todalas salas de conversas nas que estabas e eliminarás os detalles da túa conta do servidores de identidade. <b>Esta acción non ten volta</b>",
|
||||||
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Desactivando a súa conta <b>non supón que por defecto esquezamos as súas mensaxes enviadas.</b> Se quere que nos esquezamos das súas mensaxes, prema na caixa de embaixo.",
|
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Desactivando a súa conta <b>non supón que por defecto esquezamos as súas mensaxes enviadas.</b> Se quere que nos esquezamos das súas mensaxes, prema na caixa de embaixo.",
|
||||||
"To continue, please enter your password:": "Para continuar introduza o seu contrasinal:",
|
"To continue, please enter your password:": "Para continuar introduza o seu contrasinal:",
|
||||||
"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.": "A visibilidade das mensaxes en Matrix é parecida ás dos correos electrónicos. Que esquezamos as súas mensaxes significa que as súas mensaxes non se van a compartir con ningún novo membro ou usuario que non estea rexistrado. Mais aqueles usuarios que xa tiveron acceso a estas mensaxes si que seguirán tendo acceso as súas propias copias desas mensaxes.",
|
"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.": "A visibilidade das mensaxes en Matrix é parecida á dos correos electrónicos. Que esquezamos as túas mensaxes significa que as mensaxes non se van a compartir con ningún novo membro ou usuaria que non estea rexistrada. Mais aqueles usuarias que xa tiveron acceso a estas mensaxes si que seguirán tendo acceso as súas propias copias desas mensaxes.",
|
||||||
"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)": "Esquezan todas as mensaxes que eu enviara no momento en que elimine a miña conta. (<b>Aviso</b>: iso suporá que os seguintes participantes só verán unha versión incompleta das conversas.)",
|
"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)": "Esquezan todas as mensaxes que eu enviara no momento en que elimine a miña conta. (<b>Aviso</b>: iso suporá que os seguintes participantes só verán unha versión incompleta das conversas.)",
|
||||||
"Share Room": "Compartir sala",
|
"Share Room": "Compartir sala",
|
||||||
"Link to most recent message": "Ligazón ás mensaxes máis recentes",
|
"Link to most recent message": "Ligazón ás mensaxes máis recentes",
|
||||||
"Share User": "Compartir usuario",
|
"Share User": "Compartir usuaria",
|
||||||
"Share Community": "Compartir comunidade",
|
"Share Community": "Compartir comunidade",
|
||||||
"Share Room Message": "Compartir unha mensaxe da sala",
|
"Share Room Message": "Compartir unha mensaxe da sala",
|
||||||
"Link to selected message": "Ligazón á mensaxe escollida",
|
"Link to selected message": "Ligazón á mensaxe escollida",
|
||||||
|
@ -893,10 +893,10 @@
|
||||||
"Audio Output": "Saída de audio",
|
"Audio Output": "Saída de audio",
|
||||||
"Call in Progress": "Chamada en progreso",
|
"Call in Progress": "Chamada en progreso",
|
||||||
"A call is already in progress!": "Xa hai unha chamada en progreso!",
|
"A call is already in progress!": "Xa hai unha chamada en progreso!",
|
||||||
"Permission Required": "Precísase de permisos",
|
"Permission Required": "Precísanse permisos",
|
||||||
"You do not have permission to start a conference call in this room": "Non ten permisos para comezar unha chamada de conferencia nesta sala",
|
"You do not have permission to start a conference call in this room": "Non tes permisos para comezar unha chamada de conferencia nesta sala",
|
||||||
"This event could not be displayed": "Non se puido amosar este evento",
|
"This event could not be displayed": "Non se puido amosar este evento",
|
||||||
"Demote yourself?": "Baixarse a si mesmo de rango?",
|
"Demote yourself?": "Baixarse a ti mesma de rango?",
|
||||||
"Demote": "Baixar de rango",
|
"Demote": "Baixar de rango",
|
||||||
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "Nas salas cifradas, como é esta, está desactivado por defecto a previsualización das URL co fin de asegurarse de que o servidor local (que é onde se gardan as previsualizacións) non poida recoller información sobre das ligazóns que se ven nesta sala.",
|
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "Nas salas cifradas, como é esta, está desactivado por defecto a previsualización das URL co fin de asegurarse de que o servidor local (que é onde se gardan as previsualizacións) non poida recoller información sobre das ligazóns que se ven nesta sala.",
|
||||||
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Cando alguén pon unha URL na mensaxe, esta previsualízarase para que así se coñezan xa cousas delas como o título, a descrición ou as imaxes que inclúe ese sitio web.",
|
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Cando alguén pon unha URL na mensaxe, esta previsualízarase para que así se coñezan xa cousas delas como o título, a descrición ou as imaxes que inclúe ese sitio web.",
|
||||||
|
@ -963,7 +963,7 @@
|
||||||
"Sign in and regain access to your account.": "Conéctate e recupera o acceso a túa conta.",
|
"Sign in and regain access to your account.": "Conéctate e recupera o acceso a túa conta.",
|
||||||
"You cannot sign in to your account. Please contact your homeserver admin for more information.": "Non podes conectar a conta. Contacta coa administración do teu servidor para máis información.",
|
"You cannot sign in to your account. Please contact your homeserver admin for more information.": "Non podes conectar a conta. Contacta coa administración do teu servidor para máis información.",
|
||||||
"Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Aviso: os teus datos personais (incluíndo chaves de cifrado) aínda están gardadas nesta sesión. Pechaa se remataches de usar esta sesión, ou se quere conectar con outra conta.",
|
"Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Aviso: os teus datos personais (incluíndo chaves de cifrado) aínda están gardadas nesta sesión. Pechaa se remataches de usar esta sesión, ou se quere conectar con outra conta.",
|
||||||
"Unable to load! Check your network connectivity and try again.": "Non cargou! Comproba a conexión a rede e volta a intentalo.",
|
"Unable to load! Check your network connectivity and try again.": "Non cargou! Comproba a conexión á rede e volta a intentalo.",
|
||||||
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Hai sesións descoñecidas nesta sala: se continúas sen verificalas será posible para alguén fisgar na túa chamada.",
|
"There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Hai sesións descoñecidas nesta sala: se continúas sen verificalas será posible para alguén fisgar na túa chamada.",
|
||||||
"Review Sessions": "Revisar Sesións",
|
"Review Sessions": "Revisar Sesións",
|
||||||
"Call failed due to misconfigured server": "Fallou a chamada porque o servidor está mal configurado",
|
"Call failed due to misconfigured server": "Fallou a chamada porque o servidor está mal configurado",
|
||||||
|
@ -1962,5 +1962,489 @@
|
||||||
"View Servers in Room": "Ver Servidores na Sala",
|
"View Servers in Room": "Ver Servidores na Sala",
|
||||||
"Verification Requests": "Solicitudes de Verificación",
|
"Verification Requests": "Solicitudes de Verificación",
|
||||||
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verifica esta usuaria para marcala como confiable. Ao confiar nas usuarias proporcionache tranquilidade extra cando usas cifrado de extremo-a-extremo.",
|
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verifica esta usuaria para marcala como confiable. Ao confiar nas usuarias proporcionache tranquilidade extra cando usas cifrado de extremo-a-extremo.",
|
||||||
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Ao verificar esta usuaria marcarás a súa sesión como confiable, e tamén marcará a túa sesión como confiable para elas."
|
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Ao verificar esta usuaria marcarás a súa sesión como confiable, e tamén marcará a túa sesión como confiable para elas.",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Activar opción de disposición IRC na pestana de aparencia",
|
||||||
|
"Use custom size": "Usar tamaño personalizado",
|
||||||
|
"Use a system font": "Usar tipo de letra do sistema",
|
||||||
|
"System font name": "Nome da letra do sistema",
|
||||||
|
"Hey you. You're the best!": "Ei ti. Es grande!",
|
||||||
|
"Message layout": "Disposición da mensaxe",
|
||||||
|
"Compact": "Compacta",
|
||||||
|
"Modern": "Moderna",
|
||||||
|
"Power level": "Poderío",
|
||||||
|
"Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Verifica este dispositivo para marcalo como confiable. Confiando neste dispositivo permite que ti e outras usuarias estedes máis tranquilas ao utilizar mensaxes cifradas.",
|
||||||
|
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Ao verificar este dispositivo marcaralo como confiable, e as usuarias que confiaron en ti tamén confiarán nel.",
|
||||||
|
"Waiting for partner to confirm...": "Agardando a que o compañeiro confirme...",
|
||||||
|
"Incoming Verification Request": "Solicitude entrante de verificación",
|
||||||
|
"Integrations are disabled": "As Integracións están desactivadas",
|
||||||
|
"Enable 'Manage Integrations' in Settings to do this.": "Activa 'Xestionar Integracións' nos Axustes para facer esto.",
|
||||||
|
"Integrations not allowed": "Non se permiten Integracións",
|
||||||
|
"Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "O teu Riot non permite que uses o Xestor de Integracións, contacta coa administración.",
|
||||||
|
"Confirm to continue": "Confirma para continuar",
|
||||||
|
"Click the button below to confirm your identity.": "Preme no botón inferior para confirmar a túa identidade.",
|
||||||
|
"Failed to invite the following users to chat: %(csvUsers)s": "Fallo ao convidar as seguintes usuarias a conversa: %(csvUsers)s",
|
||||||
|
"We couldn't create your DM. Please check the users you want to invite and try again.": "Non puidemos crear o teu MD. Comproba as usuarias que queres convidar e inténtao outra vez.",
|
||||||
|
"Something went wrong trying to invite the users.": "Algo fallou ao convidar as usuarias.",
|
||||||
|
"We couldn't invite those users. Please check the users you want to invite and try again.": "Non puidemos invitar esas usuarias. Comprobas que son correctas e intenta convidalas outra vez.",
|
||||||
|
"Failed to find the following users": "Non atopamos as seguintes usuarias",
|
||||||
|
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "As seguintes usuarias poderían non existir ou non son válidas, e non se poden convidar: %(csvNames)s",
|
||||||
|
"Recent Conversations": "Conversas recentes",
|
||||||
|
"Suggestions": "Suxestións",
|
||||||
|
"Recently Direct Messaged": "Mensaxes Directas recentes",
|
||||||
|
"Start a conversation with someone using their name, username (like <userId/>) or email address.": "Inicia a conversa con alguén usando o seu nome, nome de usuaria (como <userId/>) ou enderezo de email.",
|
||||||
|
"Go": "Ir",
|
||||||
|
"Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Convida alguén usando o seu nome, nome de usuaria (como <userId/>), enderezo de email ou <a>comparte esta sala</a>.",
|
||||||
|
"a new master key signature": "unha nova firma con chave mestra",
|
||||||
|
"a new cross-signing key signature": "unha nova firma con chave de sinatura-cruzada",
|
||||||
|
"a device cross-signing signature": "unha sinatura sinatura-cruzada de dispositivo",
|
||||||
|
"a key signature": "unha chave de sinatura",
|
||||||
|
"Riot encountered an error during upload of:": "Riot atopou un fallo ao subir:",
|
||||||
|
"Upload completed": "Subida completa",
|
||||||
|
"Cancelled signature upload": "Cancelada a subida da sinatura",
|
||||||
|
"Unable to upload": "Non foi posible a subida",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "A autenticidade desta mensaxe cifrada non está garantida neste dispositivo.",
|
||||||
|
"Signature upload success": "Subeuse correctamente a sinatura",
|
||||||
|
"Signature upload failed": "Fallou a subida da sinatura",
|
||||||
|
"You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "Anteriormente utilizaches Riot en %(host)s con carga preguiceira de membros. Nesta versión a carga preguiceira está desactivada. Como a caché local non é compatible entre as dúas configuracións, Riot precisa voltar a sincronizar a conta.",
|
||||||
|
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Se a outra versión de Riot aínda está aberta noutra lapela, péchaa por favor, pois podería haber fallos ao estar as dúas sesións traballando simultáneamente.",
|
||||||
|
"Incompatible local cache": "Caché local incompatible",
|
||||||
|
"Clear cache and resync": "Baleirar caché e sincronizar",
|
||||||
|
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot utiliza agora entre 3 e 5 veces menos memoria, cargando só información sobre as usuarias cando é preciso. Agarda mentras se sincroniza co servidor!",
|
||||||
|
"Updating Riot": "Actualizando Riot",
|
||||||
|
"I don't want my encrypted messages": "Non quero as miñas mensaxes cifradas",
|
||||||
|
"Manually export keys": "Exportar manualmente as chaves",
|
||||||
|
"You'll lose access to your encrypted messages": "Perderás o acceso as túas mensaxes cifradas",
|
||||||
|
"Confirm by comparing the following with the User Settings in your other session:": "Corfirma comparando o seguinte cos Axustes de Usuaria na outra sesión:",
|
||||||
|
"Confirm this user's session by comparing the following with their User Settings:": "Confirma a sesión desta usuaria comparando o seguinte cos seus Axustes de Usuaria:",
|
||||||
|
"Session name": "Nome da sesión",
|
||||||
|
"Session key": "Chave da sesión",
|
||||||
|
"If they don't match, the security of your communication may be compromised.": "Se non concordan, a seguridade da comunicación podería estar comprometida.",
|
||||||
|
"Verify session": "Verificar sesión",
|
||||||
|
"Your homeserver doesn't seem to support this feature.": "O servidor non semella soportar esta característica.",
|
||||||
|
"Guest": "Convidada",
|
||||||
|
"Message edits": "Edicións da mensaxe",
|
||||||
|
"Your account is not secure": "A túa conta non é segura",
|
||||||
|
"Your password": "O teu contrasinal",
|
||||||
|
"This session, or the other session": "Esta sesión, ou a outra sesión",
|
||||||
|
"The internet connection either session is using": "A conexión a internet que está a usar cada sesión",
|
||||||
|
"We recommend you change your password and recovery key in Settings immediately": "Recomendámosche cambiar inmediatamente o contrasinal e chave de recuperación nos Axustes",
|
||||||
|
"New session": "Nova sesión",
|
||||||
|
"Use this session to verify your new one, granting it access to encrypted messages:": "Usa esta seseión para verificar a nova, dándolle acceso ás mensaxes cifradas:",
|
||||||
|
"This wasn't me": "Non fun eu",
|
||||||
|
"If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Se atopas fallos ou queres compartir a túa experiencia, compárteos con nós en GitHub.",
|
||||||
|
"Report bugs & give feedback": "Informe de fallos & opinión",
|
||||||
|
"Please fill why you're reporting.": "Escribe a razón do informe.",
|
||||||
|
"Report Content to Your Homeserver Administrator": "Denuncia sobre contido á Administración do teu servidor",
|
||||||
|
"Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Ao denunciar esta mensaxe vasnos enviar o seu 'event ID' único á administración do servidor. Se as mensaxes da sala están cifradas, a administración do servidor non poderá ler o texto da mensaxe ou ver imaxes ou ficheiros.",
|
||||||
|
"Send report": "Enviar denuncia",
|
||||||
|
"Failed to upgrade room": "Fallou a actualización da sala",
|
||||||
|
"The room upgrade could not be completed": "A actualización da sala non se completou",
|
||||||
|
"Upgrade this room to version %(version)s": "Actualiza esta sala á versión %(version)s",
|
||||||
|
"Upgrade Room Version": "Actualiza a Versión da Sala",
|
||||||
|
"Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:": "Para actualizar a sala debes pechar a instancia actual da sala e crear unha nova sala no seu lugar. Para proporcionar a mellor experiencia de usuaria, imos:",
|
||||||
|
"Create a new room with the same name, description and avatar": "Crear unha nova sala co mesmo nome, descrición e avatar",
|
||||||
|
"Update any local room aliases to point to the new room": "Actualizar calquera alias local da sala para que apunte á nova sala",
|
||||||
|
"Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Evitar que as usuarias conversen na sala antiga e publicar unha mensaxe avisando ás usuarias para que veñan á nova sala",
|
||||||
|
"Put a link back to the old room at the start of the new room so people can see old messages": "Poñer unha ligazón na nova sala cara a antiga para que as persoas poidan ver as mensaxes antigas",
|
||||||
|
"Automatically invite users": "Convidar automáticamente ás usuarias",
|
||||||
|
"Upgrade private room": "Actualizar sala privada",
|
||||||
|
"Upgrade public room": "Actualizar sala pública",
|
||||||
|
"Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "A actualización da sala é unha acción avanzada e recomendada cando unha sala se volta inestable debido aos fallos, características obsoletas e vulnerabilidades da seguridade.",
|
||||||
|
"This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.": "Esto normalmente só afecta ao xeito en que a sala se procesa no servidor. Se tes problemas con Riot, <a>informa do problema</a>.",
|
||||||
|
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "Vas actualizar a sala da versión <oldVersion /> á <newVersion />.",
|
||||||
|
"A username can only contain lower case letters, numbers and '=_-./'": "Un nome de usuaria só pode ter minúsculas, números e '=_-./'",
|
||||||
|
"Checking...": "Comprobando...",
|
||||||
|
"Missing session data": "Faltan datos da sesión",
|
||||||
|
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Faltan algúns datos da sesión, incluíndo chaves de mensaxes cifradas. Desconecta e volve a conectar para arranxalo, restaurando as chaves desde a copia.",
|
||||||
|
"Your browser likely removed this data when running low on disk space.": "O navegador probablemente eliminou estos datos ao quedar con pouco espazo de disco.",
|
||||||
|
"Integration Manager": "Xestor de Integracións",
|
||||||
|
"Find others by phone or email": "Atopa a outras por teléfono ou email",
|
||||||
|
"Be found by phone or email": "Permite ser atopada polo email ou teléfono",
|
||||||
|
"Use bots, bridges, widgets and sticker packs": "Usa bots, pontes, widgets e paquetes de adhesivos",
|
||||||
|
"Terms of Service": "Termos do Servizo",
|
||||||
|
"To continue you need to accept the terms of this service.": "Para continuar tes que aceptar os termos deste servizo.",
|
||||||
|
"Service": "Servizo",
|
||||||
|
"Summary": "Resumo",
|
||||||
|
"Document": "Documento",
|
||||||
|
"Next": "Seguinte",
|
||||||
|
"Upload files (%(current)s of %(total)s)": "Subir ficheiros (%(current)s de %(total)s)",
|
||||||
|
"Upload files": "Subir ficheiros",
|
||||||
|
"Upload all": "Subir todo",
|
||||||
|
"This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Este ficheiro é <b>demasiado grande</b> para subilo. O límite é %(limit)s mais o ficheiro é %(sizeOfThisFile)s.",
|
||||||
|
"These files are <b>too large</b> to upload. The file size limit is %(limit)s.": "Estes ficheiros son <b>demasiado grandes</b> para subilos. O límite é %(limit)s.",
|
||||||
|
"Some files are <b>too large</b> to be uploaded. The file size limit is %(limit)s.": "Algúns ficheiros son <b>demasiado grandes</b> para subilos. O límite é %(limit)s.",
|
||||||
|
"Upload %(count)s other files|other": "Subir outros %(count)s ficheiros",
|
||||||
|
"Upload %(count)s other files|one": "Subir %(count)s ficheiro máis",
|
||||||
|
"Cancel All": "Cancelar todo",
|
||||||
|
"Upload Error": "Fallo ao subir",
|
||||||
|
"Verify other session": "Verificar outra sesión",
|
||||||
|
"Verification Request": "Solicitude de Verificación",
|
||||||
|
"A widget would like to verify your identity": "Un widget quere verificar a túa indentidade",
|
||||||
|
"A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "Un widget localizado en %(widgetUrl)s quere verificar a túa identidade. Se o permites, o widget poderá verificar o teu ID de usuaria, pero non realizar accións por ti.",
|
||||||
|
"Remember my selection for this widget": "Lembrar a miña decisión para este widget",
|
||||||
|
"Deny": "Denegar",
|
||||||
|
"Enter recovery passphrase": "Escribe a frase de paso de recuperación",
|
||||||
|
"Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Non se pode acceder ao almacenaxe segredo. Verifica que escribiches a frase de paso correta.",
|
||||||
|
"<b>Warning</b>: You should only do this on a trusted computer.": "<b>Aviso</b>: Só deberías facer esto nunha computadora de confianza.",
|
||||||
|
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Accede ó teu historial seguro de mensaxes e á túa identidade de sinatura-cruzada para verificar outras sesión escribindo a frase de paso de recuperación.",
|
||||||
|
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>.": "Se esqueceches a túa frase de paso de recuperación podes <button1>usar a chave de recuperación</button1> ou establecer <button2>novas opcións de recuperación</button2>.",
|
||||||
|
"Enter recovery key": "Escribe a chave de recuperación",
|
||||||
|
"Unable to access secret storage. Please verify that you entered the correct recovery key.": "Non se accedeu ó almacenaxe segredo. Verifica que escribiches a chave de recuperación correcta.",
|
||||||
|
"This looks like a valid recovery key!": "Semella unha chave de recuperación válida!",
|
||||||
|
"Not a valid recovery key": "Non é unha chave de recuperación válida",
|
||||||
|
"Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Accede ó teu historial de mensaxes seguras e á identidade de sinatura-cruzada para verificar outras sesión escribindo a achave de recuperación.",
|
||||||
|
"If you've forgotten your recovery key you can <button>set up new recovery options</button>.": "Se esqueceches a chave de recuperación podes <button>establecer novas opcións de recuperación</button>.",
|
||||||
|
"Restoring keys from backup": "Restablecendo chaves desde a copia",
|
||||||
|
"Fetching keys from server...": "Obtendo chaves desde o servidor...",
|
||||||
|
"%(completed)s of %(total)s keys restored": "%(completed)s de %(total)s chaves restablecidas",
|
||||||
|
"Unable to load backup status": "Non cargou o estado da copia",
|
||||||
|
"Recovery key mismatch": "A chave de recuperación non concorda",
|
||||||
|
"Backup could not be decrypted with this recovery key: please verify that you entered the correct recovery key.": "A copia non se puido descifrar con esta chave de recuperación: comproba que introduciches a chave de recuperación correcta.",
|
||||||
|
"Incorrect recovery passphrase": "Frase da paso de recuperación incorrecta",
|
||||||
|
"Backup could not be decrypted with this recovery passphrase: please verify that you entered the correct recovery passphrase.": "A copia non se descifrou con esta frase de paso: comproba que escribiches a frase de paso correcta.",
|
||||||
|
"Unable to restore backup": "Non se restableceu a copia",
|
||||||
|
"No backup found!": "Non se atopou copia!",
|
||||||
|
"Keys restored": "Chaves restablecidas",
|
||||||
|
"Failed to decrypt %(failedCount)s sessions!": "Fallo ao descifrar %(failedCount)s sesións!",
|
||||||
|
"Successfully restored %(sessionCount)s keys": "Restablecidas correctamente %(sessionCount)s chaves",
|
||||||
|
"<b>Warning</b>: you should only set up key backup from a trusted computer.": "<b>Aviso</b>: só deberías realizar a copia de apoio desde un ordenador de confianza.",
|
||||||
|
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Accede ó historial de mensaxes seguras escribindo a frase de paso de recuperación.",
|
||||||
|
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "Se esqueceches a frase de paso de recuperación pode <button1>usar a chave de recuperación</button1> ou establecer <button2>novas opcións de recuperación</button2>",
|
||||||
|
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Aviso</b>: só deberías configurar a copia das chaves desde un ordenador de confianza.",
|
||||||
|
"Access your secure message history and set up secure messaging by entering your recovery key.": "Accede ó teu historial de mensaxes seguras e configura a comunicación segura escribindo a chave de recuperación.",
|
||||||
|
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "Se esqueceches a chave de recuperación podes <button>establecer novas opcións de recuperación</button>",
|
||||||
|
"Address (optional)": "Enderezo (optativo)",
|
||||||
|
"Resend edit": "Editar reenvío",
|
||||||
|
"Resend %(unsentCount)s reaction(s)": "Reenviar %(unsentCount)s reacción(s)",
|
||||||
|
"Resend removal": "Reenviar retirada",
|
||||||
|
"Share Permalink": "Comparte ligazón permanente",
|
||||||
|
"Report Content": "Denunciar contido",
|
||||||
|
"Notification settings": "Axustes de notificacións",
|
||||||
|
"Clear status": "Baleirar estado",
|
||||||
|
"Update status": "Actualizar estado",
|
||||||
|
"Set status": "Establecer estado",
|
||||||
|
"Set a new status...": "Establecer novo estado...",
|
||||||
|
"Hide": "Agochar",
|
||||||
|
"Reload": "Recargar",
|
||||||
|
"Take picture": "Tomar foto",
|
||||||
|
"Remove for everyone": "Eliminar para todas",
|
||||||
|
"Remove for me": "Eliminar para min",
|
||||||
|
"User Status": "Estado da usuaria",
|
||||||
|
"This homeserver would like to make sure you are not a robot.": "Este servidor quere asegurarse de que non es un robot.",
|
||||||
|
"Country Dropdown": "Despregable de países",
|
||||||
|
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "Podes usar as opcións dun servidor personalizado para conectarte a outros servidores Matrix indicando o URL do servidor. Así poderás usar esta app cunha conta Matrix dun servidor diferente.",
|
||||||
|
"Confirm your identity by entering your account password below.": "Confirma a túa identidade escribindo o contrasinal da conta embaixo.",
|
||||||
|
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Falta a chave pública do captcha na configuración do servidor. Informa desto á administración do teu servidor.",
|
||||||
|
"Please review and accept all of the homeserver's policies": "Revisa e acepta todas as cláusulas do servidor",
|
||||||
|
"Please review and accept the policies of this homeserver:": "Revisa e acepta as cláusulas deste servidor:",
|
||||||
|
"Unable to validate homeserver/identity server": "Non se puido validar o servidor/servidor de identidade",
|
||||||
|
"Your Modular server": "O teu servidor Modular",
|
||||||
|
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Escribe a localización do teu servidor Modular. Podería utilizar o teu propio nome de dominio ou ser un subdominio de <a>modular.im</a>.",
|
||||||
|
"Server Name": "Nome do Servidor",
|
||||||
|
"Enter password": "Escribe contrasinal",
|
||||||
|
"Nice, strong password!": "Ben, bo contrasinal!",
|
||||||
|
"Password is allowed, but unsafe": "O contrasinal é admisible, pero inseguro",
|
||||||
|
"Keep going...": "Segue intentándoo...",
|
||||||
|
"The username field must not be blank.": "O campo de nome de usuaria non pode estar baleiro.",
|
||||||
|
"Username": "Nome de usuaria",
|
||||||
|
"Not sure of your password? <a>Set a new one</a>": "¿Non estás segura do contrasinal? <a>Crea un novo</a>",
|
||||||
|
"No identity server is configured so you cannot add an email address in order to reset your password in the future.": "Non hai un servidor de identidade configurado polo que non poderás engadir enderezos de email para poder restablecer o contrasinal no futuro.",
|
||||||
|
"Use an email address to recover your account": "Usa un enderezo de email para recuperar a túa conta",
|
||||||
|
"Enter email address (required on this homeserver)": "Escribe o enderzo de email (requerido neste servidor)",
|
||||||
|
"Doesn't look like a valid email address": "Non semella un enderezo válido",
|
||||||
|
"Passwords don't match": "Non concordan os contrasinais",
|
||||||
|
"Other users can invite you to rooms using your contact details": "Outras usuarias poden convidarte ás salas usando os teus detalles de contacto",
|
||||||
|
"Enter phone number (required on this homeserver)": "Escribe un número de teléfono (requerido neste servidor)",
|
||||||
|
"Doesn't look like a valid phone number": "Non semella un número de teléfono válido",
|
||||||
|
"Use lowercase letters, numbers, dashes and underscores only": "Usa só minúsculas, números, trazos e trazos baixos",
|
||||||
|
"Enter username": "Escribe nome de usuaria",
|
||||||
|
"Email (optional)": "Email (optativo)",
|
||||||
|
"Phone (optional)": "Teléfono (optativo)",
|
||||||
|
"Create your Matrix account on %(serverName)s": "Crea a conta Matrix en %(serverName)s",
|
||||||
|
"Create your Matrix account on <underlinedServerName />": "Crea a túa conta Matrix en <underlinedServerName />",
|
||||||
|
"Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Establece un email para recuperación da conta. Usa un email ou teléfono de xeito optativo para que poidan atoparte os contactos.",
|
||||||
|
"Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Establece un email para recuperación da conta. Optativamente usa un email para que poidan atoparte os contactos existentes.",
|
||||||
|
"Enter your custom homeserver URL <a>What does this mean?</a>": "Escribe o URL do servidor personalizado <a>¿Qué significa esto?</a>",
|
||||||
|
"Homeserver URL": "URL do servidor",
|
||||||
|
"Enter your custom identity server URL <a>What does this mean?</a>": "Escribe o URL do servidor de identidade personalizado <a>¿Que significa esto?</a>",
|
||||||
|
"Identity Server URL": "URL do servidor de identidade",
|
||||||
|
"Other servers": "Outros servidores",
|
||||||
|
"Free": "Gratuíto",
|
||||||
|
"Premium": "Premium",
|
||||||
|
"Premium hosting for organisations <a>Learn more</a>": "Hospedaxe Premium para organizacións <a>Saber máis</a>",
|
||||||
|
"Find other public servers or use a custom server": "Atopa outros servidores públicos ou usa un servidor personalizado",
|
||||||
|
"Couldn't load page": "Non se puido cargar a páxina",
|
||||||
|
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Administras esta comunidade. Non poderás voltar a unirte sen un convite doutra persoa administradora.",
|
||||||
|
"Want more than a community? <a>Get your own server</a>": "¿Queres algo máis que unha comunidade? <a>Monta o teu propio servidor</a>",
|
||||||
|
"This homeserver does not support communities": "Este servidor non soporta comunidades",
|
||||||
|
"Welcome to %(appName)s": "Benvida a %(appName)s",
|
||||||
|
"Liberate your communication": "Libera as túas comunicacións",
|
||||||
|
"Send a Direct Message": "Envía unha Mensaxe Directa",
|
||||||
|
"Create a Group Chat": "Crear unha Conversa en Grupo",
|
||||||
|
"Self-verification request": "Solicitude de auto-verificación",
|
||||||
|
"Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot non puido obter a lista de protocolos desde o servidor. O servidor podería ser moi antigo para soportar redes de terceiros.",
|
||||||
|
"Riot failed to get the public room list.": "Riot non puido obter a lista de salas públicas.",
|
||||||
|
"The homeserver may be unavailable or overloaded.": "O servidor podería non estar dispoñible ou con sobrecarga.",
|
||||||
|
"delete the address.": "eliminar o enderezo.",
|
||||||
|
"Preview": "Vista previa",
|
||||||
|
"View": "Vista",
|
||||||
|
"Find a room…": "Atopa unha sala…",
|
||||||
|
"Find a room… (e.g. %(exampleRoom)s)": "Atopa unha sala... (ex. %(exampleRoom)s)",
|
||||||
|
"Jump to first unread room.": "Vaite a primeira sala non lida.",
|
||||||
|
"Jump to first invite.": "Vai ó primeiro convite.",
|
||||||
|
"You have %(count)s unread notifications in a prior version of this room.|other": "Tes %(count)s notificacións non lidas nunha versión previa desta sala.",
|
||||||
|
"You have %(count)s unread notifications in a prior version of this room.|one": "Tes %(count)s notificacións non lidas nunha versión previa desta sala.",
|
||||||
|
"Your profile": "Perfil",
|
||||||
|
"Switch to light mode": "Cambiar a decorado claro",
|
||||||
|
"Switch to dark mode": "Cambiar a decorado escuro",
|
||||||
|
"Switch theme": "Cambiar decorado",
|
||||||
|
"Security & privacy": "Seguridade & privacidade",
|
||||||
|
"All settings": "Todos os axustes",
|
||||||
|
"Archived rooms": "Salas arquivadas",
|
||||||
|
"Feedback": "Comenta",
|
||||||
|
"Account settings": "Axustes da conta",
|
||||||
|
"Could not load user profile": "Non se cargou o perfil da usuaria",
|
||||||
|
"Verify this login": "Verifcar esta conexión",
|
||||||
|
"Session verified": "Sesión verificada",
|
||||||
|
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Ao cambiar o contrasinal vas restablecer todas as chaves de cifrado das túas sesións, impedindo ler o historial de conversa. Configura a Copia de Apoio das Chaves ou exporta as chaves da sala desde outra sesión antes de restablecer o contrasinal.",
|
||||||
|
"Your Matrix account on %(serverName)s": "A túa conta Matrix en %(serverName)s",
|
||||||
|
"Your Matrix account on <underlinedServerName />": "A túa conta Matrix en <underlinedServerName />",
|
||||||
|
"No identity server is configured: add one in server settings to reset your password.": "Non hai un Servidor de Identidade configurado: engade un nos axustes para restablecer o contrasinal.",
|
||||||
|
"You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Desconectaches todas as sesións e non recibirás notificacións push. Para reactivalas, conéctate outra vez nos dispositivos.",
|
||||||
|
"Set a new password": "Novo contrasinal",
|
||||||
|
"Invalid homeserver discovery response": "Resposta de descubrimento do servidor non válida",
|
||||||
|
"Failed to get autodiscovery configuration from server": "Fallo ó obter a configuración de autodescubrimento desde o servidor",
|
||||||
|
"Invalid base_url for m.homeserver": "base_url non válido para m.homeserver",
|
||||||
|
"Homeserver URL does not appear to be a valid Matrix homeserver": "O URL do servidor non semella ser un servidor Matrix válido",
|
||||||
|
"Invalid identity server discovery response": "Resposta de descubrimento de identidade do servidor non válida",
|
||||||
|
"Invalid base_url for m.identity_server": "base_url para m.identity_server non válida",
|
||||||
|
"Identity server URL does not appear to be a valid identity server": "O URL do servidor de identidade non semella ser un servidor de identidade válido",
|
||||||
|
"This account has been deactivated.": "Esta conta foi desactivada.",
|
||||||
|
"Failed to perform homeserver discovery": "Fallo ao intentar o descubrimento do servidor",
|
||||||
|
"Syncing...": "Sincronizando...",
|
||||||
|
"Signing In...": "Conectando con...",
|
||||||
|
"If you've joined lots of rooms, this might take a while": "Se te uniches a moitas salas, esto podería levarnos un anaco",
|
||||||
|
"Create account": "Crea unha conta",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Usa o deseño compacto 'Moderno'",
|
||||||
|
"Unable to query for supported registration methods.": "Non se puido consultar os métodos de rexistro soportados.",
|
||||||
|
"Registration has been disabled on this homeserver.": "O rexistro está desactivado neste servidor.",
|
||||||
|
"Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "A tú conta (%(newAccountId)s) foi rexistrada, pero estás conectada usando outra conta (%(loggedInUserId)s).",
|
||||||
|
"Continue with previous account": "Continúa coa conta anterior",
|
||||||
|
"<a>Log in</a> to your new account.": "<a>Conecta</a> usando a conta nova.",
|
||||||
|
"You can now close this window or <a>log in</a> to your new account.": "Podes pechar esta ventá ou <a>conectar</a> usando a conta nova.",
|
||||||
|
"Registration Successful": "Rexistro correcto",
|
||||||
|
"Create your account": "Crea a túa conta",
|
||||||
|
"Use Recovery Key or Passphrase": "Usa a Chave de recuperación ou Frase de paso",
|
||||||
|
"Use Recovery Key": "Usa chave de recuperación",
|
||||||
|
"Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Confirma a túa identidade verificando esta conexión desde unha das outras sesións, permitindo así acceder ás mensaxes cifradas.",
|
||||||
|
"This requires the latest Riot on your other devices:": "Require a última versión de Riot nos outros dispositivos:",
|
||||||
|
"or another cross-signing capable Matrix client": "ou outro cliente Matrix que permita a sinatura-cruzada",
|
||||||
|
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "A nova sesión foi verificada. Tes acceso ás mensaxes cifradas, e outras persoas verante como confiable.",
|
||||||
|
"Your new session is now verified. Other users will see it as trusted.": "A nova sesión foi verificada. Outras persoas verante como confiable.",
|
||||||
|
"Without completing security on this session, it won’t have access to encrypted messages.": "Sen non garantes a seguridade para esta sesión non poderá acceder a mensaxes cifradas.",
|
||||||
|
"Go Back": "Atrás",
|
||||||
|
"Failed to re-authenticate due to a homeserver problem": "Fallo ó reautenticar debido a un problema no servidor",
|
||||||
|
"Failed to re-authenticate": "Fallo na reautenticación",
|
||||||
|
"Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Recupera o acceso á túa conta e recupera as chaves de cifrado gardadas nesta sesión. Sen elas, non poderás ler as mensaxes seguras en calquera das sesións.",
|
||||||
|
"Forgotten your password?": "¿Esqueceches o contrasinal?",
|
||||||
|
"You're signed out": "Estás desconectada",
|
||||||
|
"Clear personal data": "Baleirar datos personais",
|
||||||
|
"Command Autocomplete": "Autocompletado de comandos",
|
||||||
|
"Community Autocomplete": "Autocompletado de comunidade",
|
||||||
|
"DuckDuckGo Results": "Resultados DuckDuckGo",
|
||||||
|
"Emoji Autocomplete": "Autocompletado emoticonas",
|
||||||
|
"Notification Autocomplete": "Autocompletado de notificacións",
|
||||||
|
"Room Autocomplete": "Autocompletado de Salas",
|
||||||
|
"User Autocomplete": "Autocompletados de Usuaria",
|
||||||
|
"Confirm encryption setup": "Confirma os axustes de cifrado",
|
||||||
|
"Click the button below to confirm setting up encryption.": "Preme no botón inferior para confirmar os axustes do cifrado.",
|
||||||
|
"Enter your account password to confirm the upgrade:": "Escribe o contrasinal para confirmar a actualización:",
|
||||||
|
"Restore your key backup to upgrade your encryption": "Restablece a copia das chaves para actualizar o cifrado",
|
||||||
|
"Restore": "Restablecer",
|
||||||
|
"You'll need to authenticate with the server to confirm the upgrade.": "Debes autenticarte no servidor para confirmar a actualización.",
|
||||||
|
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Actualiza esta sesión para permitirlle que verifique as outras sesións, outorgándolles acceso ás mensaxes cifradas e marcándoas como confiables para outras usuarias.",
|
||||||
|
"Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Establece unha frase de paso de recuperación para asegurar a información cifrada e recuperala se te desconectas. Esta frase debería ser diferente ó contrasinal da conta:",
|
||||||
|
"Enter a recovery passphrase": "Escribe a frase de paso de recuperación",
|
||||||
|
"Great! This recovery passphrase looks strong enough.": "Ben! Esta frase de paso de recuperación semella ser forte.",
|
||||||
|
"Back up encrypted message keys": "Fai copia das chaves das mensaxes cifradas",
|
||||||
|
"Set up with a recovery key": "Configura cunha chave de recuperación",
|
||||||
|
"That matches!": "Concorda!",
|
||||||
|
"Use a different passphrase?": "¿Usar unha frase de paso diferente?",
|
||||||
|
"That doesn't match.": "Non concorda.",
|
||||||
|
"Go back to set it again.": "Vai atrás e volve a escribila.",
|
||||||
|
"Enter your recovery passphrase a second time to confirm it.": "Escribe a frase de paso de recuperación por segunda vez para confirmala.",
|
||||||
|
"Confirm your recovery passphrase": "Confirma a frase de paso de recuperación",
|
||||||
|
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "A chave de recuperación é unha rede de seguridade - podes usala para recuperar o acceso ás mensaxes cifradas se esqueces a frase de paso de recuperación.",
|
||||||
|
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Garda unha copia nun lugar seguro, como un xestor de contrasinais ou nun lugar aínda máis seguro.",
|
||||||
|
"Your recovery key": "A chave de recuperación",
|
||||||
|
"Copy": "Copiar",
|
||||||
|
"Download": "Descargar",
|
||||||
|
"Your recovery key has been <b>copied to your clipboard</b>, paste it to:": "A chave de recuperación foi <b>copiada no portapapeis</b>, pégaa en:",
|
||||||
|
"Your recovery key is in your <b>Downloads</b> folder.": "A chave de recuperación está no teu cartafol de <b>Descargas</b>.",
|
||||||
|
"<b>Print it</b> and store it somewhere safe": "<b>Imprímea</b> e gárdaa nun lugar seguro",
|
||||||
|
"<b>Save it</b> on a USB key or backup drive": "<b>Gárdaa</b> nunha memoria USB ou disco duro",
|
||||||
|
"<b>Copy it</b> to your personal cloud storage": "<b>Copiaa</b> no almacenaxe personal na nube",
|
||||||
|
"Unable to query secret storage status": "Non se obtivo o estado do almacenaxe segredo",
|
||||||
|
"Retry": "Reintentar",
|
||||||
|
"You can now verify your other devices, and other users to keep your chats safe.": "Xa podes verificar os teus outros dispositivos e a outras usuarias para manter conversas seguras.",
|
||||||
|
"Upgrade your encryption": "Mellora o teu cifrado",
|
||||||
|
"Confirm recovery passphrase": "Confirma a frase de paso de recuperación",
|
||||||
|
"Make a copy of your recovery key": "Fai unha copia da túa chave de recuperación",
|
||||||
|
"You're done!": "Feito!",
|
||||||
|
"Unable to set up secret storage": "Non se configurou un almacenaxe segredo",
|
||||||
|
"We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "Imos gardar unha copia cifrada das túas chaves no noso servidor. Asegura a copia cunha frase de paso de recuperación.",
|
||||||
|
"For maximum security, this should be different from your account password.": "Para máxima seguridade, esta debería ser diferente ó contrasinal da túa conta.",
|
||||||
|
"Please enter your recovery passphrase a second time to confirm.": "Escribe a frase de paso de recuperación outra vez para confirmala.",
|
||||||
|
"Repeat your recovery passphrase...": "Repite a frase de paso de recuperación...",
|
||||||
|
"Your keys are being backed up (the first backup could take a few minutes).": "As chaves estanse a copiar (a primeira copia podería tardar un anaco).",
|
||||||
|
"Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Se non configuras Recuperación de Mensaxes Seguras, non poderás restablecer o historial de mensaxes cifradas se te desconectas ou usas outra sesión.",
|
||||||
|
"Set up Secure Message Recovery": "Cofigurar Recuperación de Mensaxes Seguras",
|
||||||
|
"Secure your backup with a recovery passphrase": "Asegura a túa copia cunha frase de paso de recuperación",
|
||||||
|
"Starting backup...": "Iniciando a copia...",
|
||||||
|
"Success!": "Feito!",
|
||||||
|
"Create key backup": "Crear copia da chave",
|
||||||
|
"Unable to create key backup": "Non se creou a copia da chave",
|
||||||
|
"Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Se non configuras a Recuperación de Mensaxes Seguras, perderás o acceso ó historial de mensaxes seguras cando te desconectes.",
|
||||||
|
"If you don't want to set this up now, you can later in Settings.": "Se non queres configurar esto agora, pódelo facer posteriormente nos Axustes.",
|
||||||
|
"Don't ask again": "Non preguntar outra vez",
|
||||||
|
"New Recovery Method": "Novo Método de Recuperación",
|
||||||
|
"A new recovery passphrase and key for Secure Messages have been detected.": "Detectouse un novo método de chave e frase de paso de recuperación para Mensaxes Seguras.",
|
||||||
|
"If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Se non configuras o novo método de recuperación, un atacante podería intentar o acceso á túa conta. Cambia inmediatamente o contrasinal da conta e configura un novo método de recuperación nos Axustes.",
|
||||||
|
"This session is encrypting history using the new recovery method.": "Esta sesión está cifrando o historial usando o novo método de recuperación.",
|
||||||
|
"Go to Settings": "Ir a Axustes",
|
||||||
|
"Set up Secure Messages": "Configurar Mensaxes Seguras",
|
||||||
|
"Recovery Method Removed": "Método de Recuperación eliminado",
|
||||||
|
"This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Esta sesión detectou que a túa frase de paso de recuperación e chave para Mensaxes Seguras foron eliminadas.",
|
||||||
|
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Se fixeches esto sen querer, podes configurar Mensaxes Seguras nesta sesión e volverá a cifrar as mensaxes da sesión cun novo método de recuperación.",
|
||||||
|
"If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Se non eliminaches o método de recuperación, un atacante podería estar a intentar acceder á túa conta. Cambia inmediatamente o contrasinal da conta e establece un novo método de recuperación nos Axustes.",
|
||||||
|
"If disabled, messages from encrypted rooms won't appear in search results.": "Se está desactivado, as mensaxes das salas cifradas non aparecerán nos resultados das buscas.",
|
||||||
|
"Disable": "Desactivar",
|
||||||
|
"Not currently indexing messages for any room.": "Non se están indexando as mensaxes de ningunha sala.",
|
||||||
|
"Currently indexing: %(currentRoom)s": "Indexando actualmente: %(currentRoom)s",
|
||||||
|
"Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot está gardando de xeito seguro na caché local mensaxes cifradas para que aparezan nos resultados das buscas:",
|
||||||
|
"Space used:": "Espazo utilizado:",
|
||||||
|
"Indexed messages:": "Mensaxes indexadas:",
|
||||||
|
"Indexed rooms:": "Salas indexadas:",
|
||||||
|
"%(doneRooms)s out of %(totalRooms)s": "%(doneRooms)s de %(totalRooms)s",
|
||||||
|
"New spinner design": "Novo deseño da roda",
|
||||||
|
"Message downloading sleep time(ms)": "Tempo de espera da mensaxe de descarga(ms)",
|
||||||
|
"Navigation": "Navegación",
|
||||||
|
"Calls": "Chamadas",
|
||||||
|
"Room List": "Lista de Salas",
|
||||||
|
"Autocomplete": "Autocompletado",
|
||||||
|
"Alt": "Alt",
|
||||||
|
"Alt Gr": "Alt Gr",
|
||||||
|
"Shift": "Maiús.",
|
||||||
|
"Super": "Super",
|
||||||
|
"Ctrl": "Ctrl",
|
||||||
|
"Toggle Bold": "Activa Resaltar",
|
||||||
|
"Toggle Italics": "Activa Cursiva",
|
||||||
|
"Toggle Quote": "Activa Citación",
|
||||||
|
"New line": "Nova liña",
|
||||||
|
"Navigate recent messages to edit": "Mira nas mensaxes recentes para editar",
|
||||||
|
"Jump to start/end of the composer": "Vai ó inicio/fin no editor",
|
||||||
|
"Navigate composer history": "Vai ó historial do editor",
|
||||||
|
"Cancel replying to a message": "Cancelar a resposta a mensaxe",
|
||||||
|
"Toggle microphone mute": "Acalar micrófono",
|
||||||
|
"Toggle video on/off": "Activar vídeo on/off",
|
||||||
|
"Scroll up/down in the timeline": "Desprazarse arriba/abaixo na cronoloxía",
|
||||||
|
"Dismiss read marker and jump to bottom": "Ignorar marcador de lectura e ir ó final",
|
||||||
|
"Jump to oldest unread message": "Ir á mensaxe máis antiga non lida",
|
||||||
|
"Upload a file": "Subir ficheiro",
|
||||||
|
"Jump to room search": "Ir a busca na sala",
|
||||||
|
"Navigate up/down in the room list": "Ir arriba/abaixo na lista de salas",
|
||||||
|
"Select room from the room list": "Escoller sala da lista de salas",
|
||||||
|
"Collapse room list section": "Contraer a sección de lista de salas",
|
||||||
|
"Expand room list section": "Expandir a sección da lista de salas",
|
||||||
|
"Previous/next unread room or DM": "Anterior/seguinte para salas non lidas ou MD",
|
||||||
|
"Previous/next room or DM": "Anterior/seguinte para sala ou MD",
|
||||||
|
"Toggle the top left menu": "Activar o menú superior esquerdo",
|
||||||
|
"Close dialog or context menu": "Pechar o diálogo ou menú contextual",
|
||||||
|
"Activate selected button": "Activar o botón seleccionado",
|
||||||
|
"Toggle right panel": "Activar panel dereito",
|
||||||
|
"Toggle this dialog": "Activar este diálogo",
|
||||||
|
"Move autocomplete selection up/down": "Mover selección autocompletado arriba/abaixo",
|
||||||
|
"Cancel autocomplete": "Cancelar autocompletado",
|
||||||
|
"Page Up": "Páxina superior",
|
||||||
|
"Page Down": "Páxina inferior",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Enter": "Intro",
|
||||||
|
"Space": "Espazo",
|
||||||
|
"End": "Fin",
|
||||||
|
"You joined the call": "Unícheste á chamada",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s uniuse á chamada",
|
||||||
|
"Call in progress": "Chamada en curso",
|
||||||
|
"You left the call": "Deixáchela chamada",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s deixou a chamada",
|
||||||
|
"Call ended": "Chamada rematada",
|
||||||
|
"You started a call": "Iniciaches unha chamada",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s iniciou unha chamada",
|
||||||
|
"Waiting for answer": "Agardando resposta",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s está chamando",
|
||||||
|
"You created the room": "Creaches a sala",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s creou a sala",
|
||||||
|
"You made the chat encrypted": "Cifraches a conversa",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s cifrou a conversa",
|
||||||
|
"You made history visible to new members": "Fixeches visible o historial para novos membros",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s fixo o historial visible para novos membros",
|
||||||
|
"You made history visible to anyone": "Fixeches que o historial sexa visible para todas",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s fixo o historial visible para todas",
|
||||||
|
"You made history visible to future members": "Fixeches o historial visible para membros futuros",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s fixo o historial visible para futuros membros",
|
||||||
|
"You were invited": "Foches convidada",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s foi convidada",
|
||||||
|
"You left": "Saíches",
|
||||||
|
"%(targetName)s left": "%(targetName)s saíu",
|
||||||
|
"You were kicked (%(reason)s)": "Expulsáronte (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s foi expulsada (%(reason)s)",
|
||||||
|
"You were kicked": "Foches expulsada",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s foi expulsada",
|
||||||
|
"You rejected the invite": "Rexeitaches o convite",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s rexeitou o convite",
|
||||||
|
"You were uninvited": "Retiraronche o convite",
|
||||||
|
"%(targetName)s was uninvited": "Retirouse o convite para %(targetName)s",
|
||||||
|
"You were banned (%(reason)s)": "Foches bloqueada (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s foi bloqueada (%(reason)s)",
|
||||||
|
"You were banned": "Foches bloqueada",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s foi bloqueada",
|
||||||
|
"You joined": "Unícheste",
|
||||||
|
"%(targetName)s joined": "%(targetName)s uneuse",
|
||||||
|
"You changed your name": "Cambiaches o nome",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s cambiou o seu nome",
|
||||||
|
"You changed your avatar": "Cambiáchelo avatar",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s cambiou o seu avatar",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Cambiaches o nome da sala",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s cambiou o nome da sala",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Retiraches o convite para %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s retiroulle o convite a %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Convidaches a %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s convidou a %(targetName)s",
|
||||||
|
"You changed the room topic": "Cambiaches o tema da sala",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s cambiou o asunto da sala",
|
||||||
|
"Message deleted on %(date)s": "Mensaxe eliminada o %(date)s",
|
||||||
|
"Wrong file type": "Tipo de ficheiro erróneo",
|
||||||
|
"Looks good!": "Pinta ben!",
|
||||||
|
"Wrong Recovery Key": "Chave de recuperación errónea",
|
||||||
|
"Invalid Recovery Key": "Chave de recuperación non válida",
|
||||||
|
"Security Phrase": "Frase de seguridade",
|
||||||
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Escribe a túa Frase de Seguridade ou <button>Utiliza a Chave de Seguridade</button> para continuar.",
|
||||||
|
"Security Key": "Chave de Seguridade",
|
||||||
|
"Use your Security Key to continue.": "Usa a túa Chave de Seguridade para continuar.",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Protección contra a perda do acceso ás mensaxes cifradas e datos facendo unha copia de apoio das chaves no servidor.",
|
||||||
|
"Generate a Security Key": "Crear unha Chave de Seguridade",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "Crearemos unha Chave de Seguridade para que a gardes nalgún lugar seguro, como un xestor de contrasinais ou caixa de seguridade.",
|
||||||
|
"Enter a Security Phrase": "Escribe unha Frase de Seguridade",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Usa unha frase segreda que só ti coñezas, e de xeito optativo unha Chave de Seguridade para usar como apoio.",
|
||||||
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "Escribe unha frase de seguridade que só ti coñezas, será utilizada para protexer os teus datos. Para maior seguridade, non deberías reutilizar o contrasinal da conta.",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "Garda a Chave de Seguridade nalgún lugar seguro, como un xestor de contrasinais ou caixa de seguridade, será utiizada para protexer os teus datos cifrados.",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Se cancelas agora, poderías perder mensaxes e datos cifrados se perdes o acceso ós datos de conexión.",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "Podes configurar a Copia de apoio Segura e xestionar as chaves en Axustes.",
|
||||||
|
"Set up Secure backup": "Configurar Copia de apoio Segura",
|
||||||
|
"Set a Security Phrase": "Establece a Frase de Seguridade",
|
||||||
|
"Confirm Security Phrase": "Confirma a Frase de Seguridade",
|
||||||
|
"Save your Security Key": "Garda a Chave de Seguridade"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2516,5 +2516,91 @@
|
||||||
"Upgrade your Recovery Key": "A Visszaállítási kulcs fejlesztése",
|
"Upgrade your Recovery Key": "A Visszaállítási kulcs fejlesztése",
|
||||||
"Store your Recovery Key": "Visszaállítási kulcs tárolása",
|
"Store your Recovery Key": "Visszaállítási kulcs tárolása",
|
||||||
"Use the improved room list (in development - will refresh to apply changes)": "Használd a fejlesztett szoba listát (fejlesztés alatt - a változások a frissítés után aktiválódnak)",
|
"Use the improved room list (in development - will refresh to apply changes)": "Használd a fejlesztett szoba listát (fejlesztés alatt - a változások a frissítés után aktiválódnak)",
|
||||||
"Use the improved room list (will refresh to apply changes)": "Használd a fejlesztett szoba listát (a változások életbe lépéséhez újra fog tölteni)"
|
"Use the improved room list (will refresh to apply changes)": "Használd a fejlesztett szoba listát (a változások életbe lépéséhez újra fog tölteni)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "IRC kinézet lehetőségének megjelenítése a kinézet fülön",
|
||||||
|
"Use custom size": "Egyedi méret használata",
|
||||||
|
"Use a system font": "Rendszer betűtípus használata",
|
||||||
|
"System font name": "Rendszer betűtípus neve",
|
||||||
|
"Hey you. You're the best!": "Hé te! Te vagy a legjobb!",
|
||||||
|
"Message layout": "Üzenet kinézete",
|
||||||
|
"Compact": "Egyszerű",
|
||||||
|
"Modern": "Modern",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "A titkosított üzenetek valódiságát ezen az eszközön nem lehet garantálni.",
|
||||||
|
"You joined the call": "Csatlakoztál a hívásba",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s csatlakozott a híváshoz",
|
||||||
|
"Call in progress": "Hívás folyamatban van",
|
||||||
|
"You left the call": "Kiléptél a hívásból",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s kilépett a hívásból",
|
||||||
|
"Call ended": "Hívás befejeződött",
|
||||||
|
"You started a call": "Hívást kezdeményeztél",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s hívást kezdeményezett",
|
||||||
|
"Waiting for answer": "Válaszra várakozás",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s hív",
|
||||||
|
"You created the room": "Létrehoztál egy szobát",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s létrehozott egy szobát",
|
||||||
|
"You made the chat encrypted": "A beszélgetést titkosítottá tetted",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s titkosítottá tette a beszélgetést",
|
||||||
|
"You made history visible to new members": "A régi beszélgetéseket láthatóvá tetted az új tagok számára",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s a régi beszélgetéseket láthatóvá tette az új tagok számára",
|
||||||
|
"You made history visible to anyone": "A régi beszélgetéseket láthatóvá tette mindenki számára",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s a régi beszélgetéseket láthatóvá tette mindenki számára",
|
||||||
|
"You made history visible to future members": "A régi beszélgetéseket láthatóvá tetted a leendő tagok számára",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s a régi beszélgetéseket láthatóvá tette a leendő tagok számára",
|
||||||
|
"You were invited": "Meghívtak",
|
||||||
|
"%(targetName)s was invited": "Meghívták őt: %(targetName)s",
|
||||||
|
"You left": "Távoztál",
|
||||||
|
"%(targetName)s left": "%(targetName)s távozott",
|
||||||
|
"You were kicked (%(reason)s)": "Kirúgtak (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "Kirúgták őt: %(targetName)s (%(reason)s)",
|
||||||
|
"You were kicked": "Kirúgtak",
|
||||||
|
"%(targetName)s was kicked": "Kirúgták őt: %(targetName)s",
|
||||||
|
"You rejected the invite": "A meghívót elutasítottad",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s elutasította a meghívót",
|
||||||
|
"You were uninvited": "A meghívódat visszavonták",
|
||||||
|
"%(targetName)s was uninvited": "A meghívóját visszavonták neki: %(targetName)s",
|
||||||
|
"You were banned (%(reason)s)": "Kitiltottak (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "Kitiltották őt: %(targetName)s (%(reason)s)",
|
||||||
|
"You were banned": "Kitiltottak",
|
||||||
|
"%(targetName)s was banned": "Kitiltották őt: %(targetName)s",
|
||||||
|
"You joined": "Beléptél",
|
||||||
|
"%(targetName)s joined": "%(targetName)s belépett",
|
||||||
|
"You changed your name": "A neved megváltoztattad",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s megváltoztatta a nevét",
|
||||||
|
"You changed your avatar": "A profilképedet megváltoztattad",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s megváltoztatta a profilképét",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "A szoba nevét megváltoztattad",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s megváltoztatta a szoba nevét",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "A meghívóját visszavontad neki: %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s visszavonta a meghívóját neki: %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Meghívtad őt: %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s meghívta őt: %(targetName)s",
|
||||||
|
"You changed the room topic": "A szoba témáját megváltoztattad",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s megváltoztatta a szoba témáját",
|
||||||
|
"New spinner design": "Új várakozási animáció",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Egyszerűbb 'Modern' kinézet használata",
|
||||||
|
"Message deleted on %(date)s": "Az üzenetet ekkor törölték: %(date)s",
|
||||||
|
"Wrong file type": "A fájl típus hibás",
|
||||||
|
"Wrong Recovery Key": "A Visszaállítási Kulcs hibás",
|
||||||
|
"Invalid Recovery Key": "A Visszaállítási Kulcs hibás",
|
||||||
|
"Security Phrase": "Biztonsági jelmondat",
|
||||||
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Add meg a Biztonsági jelmondatot vagy <button>Használd a Biztonsági Kulcsot</button> a folytatáshoz.",
|
||||||
|
"Security Key": "Biztonsági Kulcs",
|
||||||
|
"Use your Security Key to continue.": "Használd a Biztonsági Kulcsot a folytatáshoz.",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "A titkosított üzenetekhez és adatokhoz való hozzáférés elvesztése esetén használható biztonsági tartalék a titkosított kulcsok a szerveredre való elmentésével.",
|
||||||
|
"Generate a Security Key": "Biztonsági Kulcs elkészítése",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "A Biztonsági Kulcsodat elkészítjük neked amit tárolj valamilyen biztonságos helyen mint pl. a jelszókezelő vagy széf.",
|
||||||
|
"Enter a Security Phrase": "Biztonsági Jelmondat megadása",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Olyan biztonsági jelmondatot használj amit csak te ismersz és esetleg mentsd el a Biztonsági Kulcsot vésztartaléknak.",
|
||||||
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "Olyan biztonsági jelmondatot adj meg amit csak te ismersz, mert ez fogja az adataidat őrizni. Hogy biztonságos legyen ne használd a fiókod jelszavát.",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "A Biztonsági Kulcsot tárold biztonságos helyen, mint pl. a jelszókezelő vagy széf, mivel ez tartja biztonságban a titkosított adataidat.",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Ha most megszakítod, akkor a munkameneteidhez való hozzáférés elvesztésével elveszítheted a titkosított üzeneteidet és adataidat.",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "A Biztonsági mentést és a kulcsok kezelését beállíthatod a Beállításokban.",
|
||||||
|
"Set up Secure backup": "Biztonsági mentés beállítása",
|
||||||
|
"Set a Security Phrase": "Biztonsági Jelmondat beállítása",
|
||||||
|
"Confirm Security Phrase": "Biztonsági Jelmondat megerősítése",
|
||||||
|
"Save your Security Key": "Ments el a Biztonsági Kulcsodat"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2522,5 +2522,72 @@
|
||||||
"Create a Recovery Key": "Crea una chiave di recupero",
|
"Create a Recovery Key": "Crea una chiave di recupero",
|
||||||
"Upgrade your Recovery Key": "Aggiorna la chiave di recupero",
|
"Upgrade your Recovery Key": "Aggiorna la chiave di recupero",
|
||||||
"Store your Recovery Key": "Salva la chiave di recupero",
|
"Store your Recovery Key": "Salva la chiave di recupero",
|
||||||
"Use the improved room list (will refresh to apply changes)": "Usa l'elenco stanze migliorato (verrà ricaricato per applicare le modifiche)"
|
"Use the improved room list (will refresh to apply changes)": "Usa l'elenco stanze migliorato (verrà ricaricato per applicare le modifiche)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Attiva l'opzione per il layout IRC nella scheda dell'aspetto",
|
||||||
|
"Use custom size": "Usa dimensione personalizzata",
|
||||||
|
"Hey you. You're the best!": "Ehi tu. Sei il migliore!",
|
||||||
|
"Message layout": "Layout messaggio",
|
||||||
|
"Compact": "Compatto",
|
||||||
|
"Modern": "Moderno",
|
||||||
|
"Use a system font": "Usa un carattere di sistema",
|
||||||
|
"System font name": "Nome carattere di sistema",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "L'autenticità di questo messaggio cifrato non può essere garantita su questo dispositivo.",
|
||||||
|
"You joined the call": "Ti sei unito alla chiamata",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s si è unito alla chiamata",
|
||||||
|
"Call in progress": "Chiamata in corso",
|
||||||
|
"You left the call": "Hai abbandonato la chiamata",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s ha abbandonato la chiamata",
|
||||||
|
"Call ended": "Chiamata terminata",
|
||||||
|
"You started a call": "Hai iniziato una chiamata",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s ha iniziato una chiamata",
|
||||||
|
"Waiting for answer": "In attesa di risposta",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s sta chiamando",
|
||||||
|
"You created the room": "Hai creato la stanza",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s ha creato la stanza",
|
||||||
|
"You made the chat encrypted": "Hai reso la chat crittografata",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s ha reso la chat crittografata",
|
||||||
|
"You made history visible to new members": "Hai reso visibile la cronologia ai nuovi membri",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s ha reso visibile la cronologia ai nuovi membri",
|
||||||
|
"You made history visible to anyone": "Hai reso visibile la cronologia a chiunque",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s ha reso visibile la cronologia a chiunque",
|
||||||
|
"You made history visible to future members": "Hai reso visibile la cronologia ai membri futuri",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s ha reso visibile la cronologia ai membri futuri",
|
||||||
|
"You were invited": "Sei stato invitato",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s è stato invitato",
|
||||||
|
"You left": "Sei uscito",
|
||||||
|
"%(targetName)s left": "%(targetName)s è uscito",
|
||||||
|
"You were kicked (%(reason)s)": "Sei stato buttato fuori (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s è stato buttato fuori (%(reason)s)",
|
||||||
|
"You were kicked": "Sei stato buttato fuori",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s è stato buttato fuori",
|
||||||
|
"You rejected the invite": "Hai rifiutato l'invito",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s ha rifiutato l'invito",
|
||||||
|
"You were uninvited": "Ti è stato revocato l'invito",
|
||||||
|
"%(targetName)s was uninvited": "È stato revocato l'invito a %(targetName)s",
|
||||||
|
"You were banned (%(reason)s)": "Sei stato bandito (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s è stato bandito (%(reason)s)",
|
||||||
|
"You were banned": "Sei stato bandito",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s è stato bandito",
|
||||||
|
"You joined": "Ti sei unito",
|
||||||
|
"%(targetName)s joined": "%(targetName)s si è unito",
|
||||||
|
"You changed your name": "Hai cambiato il tuo nome",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s ha cambiato il suo nome",
|
||||||
|
"You changed your avatar": "Hai cambiato il tuo avatar",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s ha cambiato il suo avatar",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Hai cambiato il nome della stanza",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s ha cambiato il nome della stanza",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Hai revocato l'invito a %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s ha revocato l'invito a %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Hai invitato %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s ha invitato %(targetName)s",
|
||||||
|
"You changed the room topic": "Hai cambiato l'argomento della stanza",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s ha cambiato l'argomento della stanza",
|
||||||
|
"New spinner design": "Nuovo design dello spinner",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Usa un layout più compatto e moderno",
|
||||||
|
"Always show first": "Mostra sempre per prime",
|
||||||
|
"Message deleted on %(date)s": "Messaggio eliminato il %(date)s"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
"Confirm": "Sentem",
|
"Confirm": "Sentem",
|
||||||
"Analytics": "Tiselḍin",
|
"Analytics": "Tiselḍin",
|
||||||
"Error": "Tuccḍa",
|
"Error": "Tuccḍa",
|
||||||
"Dismiss": "Agwi",
|
"Dismiss": "Agi",
|
||||||
"OK": "IH",
|
"OK": "IH",
|
||||||
"Permission Required": "Tasiregt tlaq",
|
"Permission Required": "Tasiregt tlaq",
|
||||||
"Continue": "Kemmel",
|
"Continue": "Kemmel",
|
||||||
"Cancel": "Semmet",
|
"Cancel": "Sefsex",
|
||||||
"Sun": "Ace",
|
"Sun": "Iṭij",
|
||||||
"Mon": "Ari",
|
"Mon": "Ari",
|
||||||
"Tue": "Ara",
|
"Tue": "Ara",
|
||||||
"Wed": "Aha",
|
"Wed": "Aha",
|
||||||
|
@ -80,13 +80,13 @@
|
||||||
"Bicycle": "Azlalam",
|
"Bicycle": "Azlalam",
|
||||||
"Ball": "Balles",
|
"Ball": "Balles",
|
||||||
"Anchor": "Tamdeyt",
|
"Anchor": "Tamdeyt",
|
||||||
"Headphones": "Casque",
|
"Headphones": "Wennez",
|
||||||
"Folder": "Akaram",
|
"Folder": "Akaram",
|
||||||
"Upload": "Sali",
|
"Upload": "Sali",
|
||||||
"Remove": "Sfeḍ",
|
"Remove": "Sfeḍ",
|
||||||
"Show less": "Sken-d drus",
|
"Show less": "Sken-d drus",
|
||||||
"Show more": "Sken-d ugar",
|
"Show more": "Sken-d ugar",
|
||||||
"Warning!": "Ɣur-k!",
|
"Warning!": "Ɣur-k·m!",
|
||||||
"Current password": "Awal uffir amiran",
|
"Current password": "Awal uffir amiran",
|
||||||
"Password": "Awal uffir",
|
"Password": "Awal uffir",
|
||||||
"New Password": "Awal uffir amaynut",
|
"New Password": "Awal uffir amaynut",
|
||||||
|
@ -100,11 +100,11 @@
|
||||||
"Keywords": "Awalen tisura",
|
"Keywords": "Awalen tisura",
|
||||||
"Clear notifications": "Sfeḍ ilɣuyen",
|
"Clear notifications": "Sfeḍ ilɣuyen",
|
||||||
"Off": "Insa",
|
"Off": "Insa",
|
||||||
"Display Name": "Mefffer isem",
|
"Display Name": "Sken isem",
|
||||||
"Save": "Sekles",
|
"Save": "Sekles",
|
||||||
"Disconnect": "Yeffeɣ",
|
"Disconnect": "Ffeɣ seg tuqqna",
|
||||||
"Go back": "Uɣal ɣer deffir",
|
"Go back": "Uɣal ɣer deffir",
|
||||||
"Change": "Changer",
|
"Change": "Beddel",
|
||||||
"Theme": "Asentel",
|
"Theme": "Asentel",
|
||||||
"Success": "Yedda",
|
"Success": "Yedda",
|
||||||
"Profile": "Amaɣnu",
|
"Profile": "Amaɣnu",
|
||||||
|
@ -117,17 +117,17 @@
|
||||||
"Keyboard Shortcuts": "Inegzumen n unasiw",
|
"Keyboard Shortcuts": "Inegzumen n unasiw",
|
||||||
"Versions": "Ileqman",
|
"Versions": "Ileqman",
|
||||||
"None": "Ula yiwen",
|
"None": "Ula yiwen",
|
||||||
"Unsubscribe": "Se désabonner",
|
"Unsubscribe": "Sefsex ajerred",
|
||||||
"Ignore": "Ttu",
|
"Ignore": "Ttu",
|
||||||
"Subscribe": "Jerred",
|
"Subscribe": "Jerred",
|
||||||
"Preferences": "Tiwelhiwin",
|
"Preferences": "Ismenyifen",
|
||||||
"Composer": "Editeur",
|
"Composer": "Imsuddes",
|
||||||
"Timeline": "Amazray",
|
"Timeline": "Amazray",
|
||||||
"Microphone": "Asawaḍ",
|
"Microphone": "Asawaḍ",
|
||||||
"Camera": "Takamiṛatt",
|
"Camera": "Takamiṛatt",
|
||||||
"Sounds": "Imeslan",
|
"Sounds": "Imesla",
|
||||||
"Reset": "Ales awennez",
|
"Reset": "Wennez",
|
||||||
"Browse": "Snirem",
|
"Browse": "Inig",
|
||||||
"Default role": "Tamlilt tamzwert",
|
"Default role": "Tamlilt tamzwert",
|
||||||
"Permissions": "Tisirag",
|
"Permissions": "Tisirag",
|
||||||
"Anyone": "Yal yiwen",
|
"Anyone": "Yal yiwen",
|
||||||
|
@ -136,38 +136,38 @@
|
||||||
"Share": "Bḍu",
|
"Share": "Bḍu",
|
||||||
"Verification code": "Tangalt n usenqed",
|
"Verification code": "Tangalt n usenqed",
|
||||||
"Add": "Rnu",
|
"Add": "Rnu",
|
||||||
"Email Address": "Tansa imayl",
|
"Email Address": "Tansa n yimayl",
|
||||||
"Phone Number": "Uṭṭun n tiliɣri",
|
"Phone Number": "Uṭṭun n tiliɣri",
|
||||||
"Upload file": "Azen afaylu",
|
"Upload file": "Sali-d afaylu",
|
||||||
"Bold": "Azuran",
|
"Bold": "Azuran",
|
||||||
"Strikethrough": "Jerreḍ",
|
"Strikethrough": "Derrer",
|
||||||
"Quote": "Citation",
|
"Quote": "Tanebdurt",
|
||||||
"Loading...": "Yessalay-ed…",
|
"Loading...": "La d-yettali…",
|
||||||
"Idle": "Idle",
|
"Idle": "Arurmid",
|
||||||
"Unknown": "D arussin",
|
"Unknown": "Arussin",
|
||||||
"Settings": "Iɣewwaren",
|
"Settings": "Iɣewwaren",
|
||||||
"Search": "Nadi",
|
"Search": "Nadi",
|
||||||
"Favourites": "Ismenyifen",
|
"Favourites": "Ismenyifen",
|
||||||
"People": "Imdanen",
|
"People": "Imdanen",
|
||||||
"Sign Up": "Jerred",
|
"Sign Up": "Jerred",
|
||||||
"Reject": "Aggi",
|
"Reject": "Agi",
|
||||||
"Not now": "Mačči tura",
|
"Not now": "Mačči tura",
|
||||||
"Sort by": "Smizzwer s",
|
"Sort by": "Semyizwer s",
|
||||||
"Activity": "Armud",
|
"Activity": "Armud",
|
||||||
"A-Z": "A-Z",
|
"A-Z": "A-Z",
|
||||||
"Show": "Sken",
|
"Show": "Sken",
|
||||||
"Options": "Tinefrunin",
|
"Options": "Tixtiṛiyin",
|
||||||
"Server error": "Tuccḍa n uqeddac",
|
"Server error": "Tuccḍa n uqeddac",
|
||||||
"Members": "Imedrawen",
|
"Members": "Imettekkiyen",
|
||||||
"Files": "Ifuyla",
|
"Files": "Ifuyla",
|
||||||
"Trusted": "De confiance",
|
"Trusted": "Yettwattkal",
|
||||||
"Invite": "Nced…",
|
"Invite": "Nced",
|
||||||
"Unmute": "Susem",
|
"Unmute": "Rmed imesli",
|
||||||
"Mute": "Kkes imesli",
|
"Mute": "Sens imesli",
|
||||||
"Are you sure?": "Tebɣiḍ ?",
|
"Are you sure?": "Tebɣiḍ s tidet?",
|
||||||
"Security": "Taɣellist",
|
"Security": "Taɣellist",
|
||||||
"Yes": "Ih",
|
"Yes": "Ih",
|
||||||
"Verified": "Verified",
|
"Verified": "Yettwasenqed",
|
||||||
"Got it": "Awi-t",
|
"Got it": "Awi-t",
|
||||||
"Sunday": "Acer",
|
"Sunday": "Acer",
|
||||||
"Monday": "Arim",
|
"Monday": "Arim",
|
||||||
|
@ -178,48 +178,48 @@
|
||||||
"Saturday": "Sed",
|
"Saturday": "Sed",
|
||||||
"Today": "Ass-a",
|
"Today": "Ass-a",
|
||||||
"Yesterday": "Iḍelli",
|
"Yesterday": "Iḍelli",
|
||||||
"View Source": "Sken aɣbalu",
|
"View Source": "Wali aɣbalu",
|
||||||
"Reply": "Err",
|
"Reply": "Err",
|
||||||
"Edit": "Ẓreg",
|
"Edit": "Ẓreg",
|
||||||
"Attachment": "Attachement",
|
"Attachment": "Taceqquft yeddan",
|
||||||
"Error decrypting attachment": "Tuccḍa deg uzmak n ufaylu yeddan",
|
"Error decrypting attachment": "Tuccḍa deg uwgelhen n tceqquft yeddan",
|
||||||
"Show all": "Sken akk",
|
"Show all": "Sken akk",
|
||||||
"Message deleted": "Izen yettwakkes",
|
"Message deleted": "Izen yettwakksen",
|
||||||
"Copied!": "Yettusukken!",
|
"Copied!": "Yettwanɣel!",
|
||||||
"edited": "yeẓreg",
|
"edited": "yettwaẓreg",
|
||||||
"Food & Drink": "Učči aked tissit",
|
"Food & Drink": "Učči d tissit",
|
||||||
"Objects": "Tiɣawsiwin",
|
"Objects": "Tiɣawsiwin",
|
||||||
"Symbols": "Izamulen",
|
"Symbols": "Izamulen",
|
||||||
"Flags": "Anayen",
|
"Flags": "Anayen",
|
||||||
"Categories": "Taggayin",
|
"Categories": "Taggayin",
|
||||||
"More options": "Ugar n textirin",
|
"More options": "Ugar n textiṛiyin",
|
||||||
"Join": "Semlil",
|
"Join": "Rnu",
|
||||||
"No results": "Ulac igmad",
|
"No results": "Ulac igmad",
|
||||||
"collapse": "sneḍfes",
|
"collapse": "fneẓ",
|
||||||
"Rotate counter-clockwise": "Zzi di tnila tanemgalt n tsegnatin n temrilt",
|
"Rotate counter-clockwise": "Zzi mgal tanila n tessegnatin n temrilt",
|
||||||
"Rotate clockwise": "Zzi di tnila n tsegnatin n temrilt",
|
"Rotate clockwise": "Zzi almend n tnila n tsegnatin n temrilt",
|
||||||
"Add User": "Rnu aseqdac",
|
"Add User": "Rnu aseqdac",
|
||||||
"Server name": "Isem n uqeddac",
|
"Server name": "Isem n uqeddac",
|
||||||
"email address": "tansa imayl",
|
"email address": "tansa n yimayl",
|
||||||
"Close dialog": "Mdel tanaka n usdiwen",
|
"Close dialog": "Mdel adiwenni",
|
||||||
"Notes": "Tamawt",
|
"Notes": "Tamawin",
|
||||||
"Unavailable": "Ulac-it",
|
"Unavailable": "Ulac",
|
||||||
"Changelog": "Aɣmis n ibeddilen",
|
"Changelog": "Aɣmis n yisnifal",
|
||||||
"Removing…": "Tukksa…",
|
"Removing…": "Tukksa…",
|
||||||
"Confirm Removal": "Serggeg tukksa",
|
"Confirm Removal": "Sentem tukksa",
|
||||||
"Example": "Amedya",
|
"Example": "Amedya",
|
||||||
"example": "amedya",
|
"example": "amedya",
|
||||||
"Create": "Snulfu-d",
|
"Create": "Snulfu-d",
|
||||||
"Name": "Isem",
|
"Name": "Isem",
|
||||||
"Sign out": "Ffeɣ",
|
"Sign out": "Ffeɣ seg tuqqna",
|
||||||
"Back": "Retour",
|
"Back": "Uɣal ɣer deffir",
|
||||||
"Send": "Azen",
|
"Send": "Azen",
|
||||||
"Suggestions": "Isumar",
|
"Suggestions": "Isumar",
|
||||||
"Go": "Ddu",
|
"Go": "Ddu",
|
||||||
"Session name": "Nom de session",
|
"Session name": "Isem n tɣimit",
|
||||||
"Send report": "Azen aneqqis",
|
"Send report": "Azen aneqqis",
|
||||||
"Refresh": "Sismeḍ",
|
"Refresh": "Smiren",
|
||||||
"Email address": "Tansa email",
|
"Email address": "Tansa n yimayl",
|
||||||
"Skip": "Zgel",
|
"Skip": "Zgel",
|
||||||
"Username not available": "Ulac isem n useqdac",
|
"Username not available": "Ulac isem n useqdac",
|
||||||
"Checking...": "Asenqed...",
|
"Checking...": "Asenqed...",
|
||||||
|
@ -227,21 +227,21 @@
|
||||||
"Terms of Service": "Tiwtilin n useqdec",
|
"Terms of Service": "Tiwtilin n useqdec",
|
||||||
"Service": "Ameẓlu",
|
"Service": "Ameẓlu",
|
||||||
"Summary": "Agzul",
|
"Summary": "Agzul",
|
||||||
"Document": "isemli",
|
"Document": "Isemli",
|
||||||
"Next": "Ar zdat",
|
"Next": "Γer sdat",
|
||||||
"Upload files": "Azen ifuyla",
|
"Upload files": "Sali-d ifuyla",
|
||||||
"Appearance": "Udem",
|
"Appearance": "Arwes",
|
||||||
"Allow": "Sireg",
|
"Allow": "Sireg",
|
||||||
"Deny": "Agwi",
|
"Deny": "Agi",
|
||||||
"Custom": "Personnalisé",
|
"Custom": "Sagen",
|
||||||
"Source URL": "URL aγbalu",
|
"Source URL": "URL aɣbalu",
|
||||||
"Notification settings": "Iɣewwaṛen n yilɣa",
|
"Notification settings": "Iɣewwaren n yilɣa",
|
||||||
"Leave": "Ffeɣ",
|
"Leave": "Ffeɣ",
|
||||||
"Set status": "Sbadu addad",
|
"Set status": "Sbadu addaden",
|
||||||
"Hide": "Ffer",
|
"Hide": "Ffer",
|
||||||
"Home": "Agejdan",
|
"Home": "Agejdan",
|
||||||
"Sign in": "Qqen",
|
"Sign in": "Qqen",
|
||||||
"Help": "Tallelt",
|
"Help": "Tallalt",
|
||||||
"Reload": "Smiren",
|
"Reload": "Smiren",
|
||||||
"powered by Matrix": "s lmendad n Matrix",
|
"powered by Matrix": "s lmendad n Matrix",
|
||||||
"Custom Server Options": "Iɣewwaren n uqeddac udmawan",
|
"Custom Server Options": "Iɣewwaren n uqeddac udmawan",
|
||||||
|
@ -250,40 +250,40 @@
|
||||||
"Email": "Imayl",
|
"Email": "Imayl",
|
||||||
"Username": "Isem n useqdac",
|
"Username": "Isem n useqdac",
|
||||||
"Phone": "Tiliɣri",
|
"Phone": "Tiliɣri",
|
||||||
"Passwords don't match": "Awal uffiren ur menṭaḍen ara",
|
"Passwords don't match": "Awalen uffiren ur mṣadan ara",
|
||||||
"Email (optional)": "Imayl (Afrayan)",
|
"Email (optional)": "Imayl (Afrayan)",
|
||||||
"Register": "Jerred",
|
"Register": "Jerred",
|
||||||
"Free": "Ilelli",
|
"Free": "Ilelli",
|
||||||
"Failed to upload image": "Ur yezmir ad yessali tugna",
|
"Failed to upload image": "Tegguma ad d-tali tugna",
|
||||||
"Description": "Aseglem",
|
"Description": "Aglam",
|
||||||
"Explore": "Snirem",
|
"Explore": "Snirem",
|
||||||
"Filter": "Imsizdeg",
|
"Filter": "Imsizdeg",
|
||||||
"Explore rooms": "Snirem tixxamin",
|
"Explore rooms": "Snirem tixxamin",
|
||||||
"Unknown error": "Erreur inconnue",
|
"Unknown error": "Tuccḍa tarussint",
|
||||||
"Logout": "Tufɣa",
|
"Logout": "Tuffɣa",
|
||||||
"Preview": "Timeẓriwt",
|
"Preview": "Taskant",
|
||||||
"View": "Ɣeṛ",
|
"View": "Sken",
|
||||||
"Guest": "Inebgi",
|
"Guest": "Anerzaf",
|
||||||
"Your profile": "Amaɣnu-ik",
|
"Your profile": "Amaɣnu-ik/im",
|
||||||
"Feedback": "Tikti",
|
"Feedback": "Takti",
|
||||||
"Your password has been reset.": "Awal n uɛeddi inek yules awennez.",
|
"Your password has been reset.": "Awal uffir-inek/inem yettuwennez.",
|
||||||
"Syncing...": "Amtawi",
|
"Syncing...": "Amtawi...",
|
||||||
"Create account": "Rnu amiḍan",
|
"Create account": "Rnu amiḍan",
|
||||||
"Create your account": "Rnu amiḍan-ik",
|
"Create your account": "Rnu amiḍan-ik/im",
|
||||||
"Go Back": "Précédent",
|
"Go Back": "Uɣal ɣer deffir",
|
||||||
"Commands": "Tiludna",
|
"Commands": "Tiludna",
|
||||||
"Users": "Iseqdacen",
|
"Users": "Iseqdacen",
|
||||||
"Export": "Sifeḍ",
|
"Export": "Sifeḍ",
|
||||||
"Import": "Kter",
|
"Import": "Kter",
|
||||||
"Restore": "Err-d",
|
"Restore": "Err-d",
|
||||||
"Copy": "Nγel",
|
"Copy": "Nɣel",
|
||||||
"Download": "Sider",
|
"Download": "Sader",
|
||||||
"Retry": "Ɛreḍ tikkelt nniḍen",
|
"Retry": "Ɛreḍ tikkelt-nniḍen",
|
||||||
"Starting backup...": "Asenker n uḥraz...",
|
"Starting backup...": "Asenker n uḥraz...",
|
||||||
"Success!": "Akka d rrbeḥ !",
|
"Success!": "Akka d rrbeḥ !",
|
||||||
"Disable": "Désactiver",
|
"Disable": "Sens",
|
||||||
"Navigation": "Tunigin",
|
"Navigation": "Tunigin",
|
||||||
"Calls": "Appels",
|
"Calls": "isawalen",
|
||||||
"Alt": "Alt",
|
"Alt": "Alt",
|
||||||
"Shift": "Shift",
|
"Shift": "Shift",
|
||||||
"New line": "Izirig amaynut",
|
"New line": "Izirig amaynut",
|
||||||
|
@ -346,5 +346,120 @@
|
||||||
"Cancel replying to a message": "Sefsex tiririt ɣef yizen",
|
"Cancel replying to a message": "Sefsex tiririt ɣef yizen",
|
||||||
"Toggle microphone mute": "Rmed/sens tanusi n usawaḍ",
|
"Toggle microphone mute": "Rmed/sens tanusi n usawaḍ",
|
||||||
"Toggle video on/off": "Rmed/sens tavidyut",
|
"Toggle video on/off": "Rmed/sens tavidyut",
|
||||||
"Scroll up/down in the timeline": "Drurem gar afellay/addday n tesnakudt"
|
"Scroll up/down in the timeline": "Drurem gar afellay/addday n tesnakudt",
|
||||||
|
"Updating Riot": "Leqqem Riot",
|
||||||
|
"I don't want my encrypted messages": "Ur bɣiɣ ara izan-inu iwgelhanen",
|
||||||
|
"Manually export keys": "Sifeḍ s ufus tisura",
|
||||||
|
"Session ID": "Asulay n tqimit",
|
||||||
|
"Session key": "Tasarut n tɣimit",
|
||||||
|
"Please check your email and click on the link it contains. Once this is done, click continue.": "Ma ulac aɣilif, senqed imayl-ik/im syen sit ɣef useɣwen i yellan. Akken ara yemmed waya, sit ad tkemmleḍ.",
|
||||||
|
"This will allow you to reset your password and receive notifications.": "Ayagi ad ak(akem)-yeǧǧ ad twennzeḍ awal-ik/im uffir yerna ad d-tremseḍ ilɣa.",
|
||||||
|
"A username can only contain lower case letters, numbers and '=_-./'": "Isem n useqdac yezmer kan ad yegber isekkilen imeẓyanen, izwilen neɣ '=_-./'",
|
||||||
|
"Username invalid: %(errMessage)s": "Isem n useqdac d armeɣtu",
|
||||||
|
"An error occurred: %(error_string)s": "Tella-d tuccḍa: %(error_string)s",
|
||||||
|
"To get started, please pick a username!": "I wakken ad tebduḍ, ttxil-k/m fren isem n useqdac!",
|
||||||
|
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "Wagi ad yili d isem-ik/im deg <span></span> usebter agejdan, neɣ tzemreḍ ad tferneḍ <a>aqeddac-nniḍen</a>.",
|
||||||
|
"If you already have a Matrix account you can <a>log in</a> instead.": "Ma yella tesεiḍ yakan amiḍan di Matrix, tzemreḍ <a>ad tkecmeḍ</a> deg umḍq-nni.",
|
||||||
|
"Call Failed": "Ur iddi ara usiwel",
|
||||||
|
"Call Timeout": "Akud n uṛaǧu n usiwel",
|
||||||
|
"Try using turn.matrix.org": "Ɛreḍ aseqdec n turn.matrix.org",
|
||||||
|
"Unable to capture screen": "Tuṭṭfa n ugdil ulamek",
|
||||||
|
"Existing Call": "Asiwel amiran",
|
||||||
|
"You are already in a call.": "Aql-ak(qkem)-id yakan tessawaleḍ.",
|
||||||
|
"VoIP is unsupported": "VoIP ur tettusefrak ara",
|
||||||
|
"You cannot place VoIP calls in this browser.": "Ur tezmireḍ ara ad tesεeddiḍ asiwel VoIP deg yiminig-a.",
|
||||||
|
"You cannot place a call with yourself.": "Ur tezmireḍ ara a temsawaleḍ d yiman-ik.",
|
||||||
|
"Call in Progress": "Asiwel iteddu",
|
||||||
|
"A call is currently being placed!": "Yella usiwel ila iteddu!",
|
||||||
|
"A call is already in progress!": "Yella usiwel ila iteddun akka tura!",
|
||||||
|
"Replying With Files": "Tiririt s yifuyla",
|
||||||
|
"The file '%(fileName)s' failed to upload.": "Yegguma ad d-yali '%(fileName)s' ufaylu.",
|
||||||
|
"Upload Failed": "Asali ur yeddi ara",
|
||||||
|
"Enter passphrase": "Sekcem tafyirt tuffirt",
|
||||||
|
"Setting up keys": "Asebded n tsura",
|
||||||
|
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
|
||||||
|
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
||||||
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
||||||
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||||
|
"Who would you like to add to this community?": "Anwa i tebɣiḍ ad t-ternuḍ ɣer temɣiwent-a?",
|
||||||
|
"Name or Matrix ID": "Isem neɣ asulay n Matrix",
|
||||||
|
"Invite to Community": "Ɛreḍ-d ɣer temɣiwent",
|
||||||
|
"Room name or address": "Isem neɣ tansa n texxamt",
|
||||||
|
"Add to community": "Rnu ɣer temɣiwent",
|
||||||
|
"Unnamed Room": "Taxxamt war isem",
|
||||||
|
"The server does not support the room version specified.": "Aqeddac ur issefrek ara lqem n texxamt yettwafernen.",
|
||||||
|
"If you cancel now, you won't complete verifying the other user.": "Ma yella teffɣeḍ tura, asenqed n yiseqdacen-nniḍen ur ittemmed ara.",
|
||||||
|
"Cancel entering passphrase?": "Sefsex tafyirt tuffirt n uεeddi?",
|
||||||
|
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Γur-k: yal amdan ara ternuḍ ɣer temɣiwent ad d-iban s wudem azayaz i yal yiwen yessnen asulay n temɣiwent",
|
||||||
|
"Invite new community members": "Nced-d imttekkiyen imaynuten ɣer temɣiwent",
|
||||||
|
"Which rooms would you like to add to this community?": "Anti tixxamin i tebɣiḍ adternuḍ i temɣiwent-a?",
|
||||||
|
"Add rooms to the community": "Rnu tixxamin ɣer temɣiwent",
|
||||||
|
"Failed to invite the following users to %(groupId)s:": "Ancad n yiseqdacen i d-iteddun %(groupId)s ur yeddi ara:",
|
||||||
|
"Failed to invite users to community": "Ancad n yiseqdacen ɣer temɣiwent ur yeddi ara",
|
||||||
|
"Failed to invite users to %(groupId)s": "Ancad n yiseqdacen ɣer %(groupId)s ur yedi ara",
|
||||||
|
"Failed to add the following rooms to %(groupId)s:": "Timerna n texxamin i d-iteddun ɣer %(groupId)s ur yedi ara:",
|
||||||
|
"Identity server has no terms of service": "Timagit n uqeddac ulac ɣer-sen iferdisen n umeẓlu",
|
||||||
|
"%(name)s is requesting verification": "%(name)s yesra asenqed",
|
||||||
|
"Riot does not have permission to send you notifications - please check your browser settings": "Riot ulac ɣer-s tisirag i tuzna n yilɣa - ttxil-k/m senqed iɣewwaren n yiminig-ik/im",
|
||||||
|
"Riot was not given permission to send notifications - please try again": "Riot ur d-yefk ara tisirag i tuzna n yilɣa - ttxil-k/m εreḍ tikkelt-nniḍen",
|
||||||
|
"Unable to enable Notifications": "Sens irmad n yilɣa",
|
||||||
|
"This email address was not found": "Tansa-a n yimayl ulac-it",
|
||||||
|
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Tansa-ik/im n yimayl ur d-tban ara akken ad tettwacudd d usulay Matrix deg usebter-a agejdan.",
|
||||||
|
"Sign In or Create Account": "Kcem ɣer neɣ rnu amiḍan",
|
||||||
|
"Use your account or create a new one to continue.": "Seqdec amiḍan-ik/im neɣ snulfu-d yiwen akken ad tkemmleḍ.",
|
||||||
|
"Custom (%(level)s)": "Sagen (%(level)s)",
|
||||||
|
"Failed to invite": "Ulamek i d-tnecdeḍ",
|
||||||
|
"You need to be able to invite users to do that.": "Tesriḍ ad tizmireḍ ad d-tnecdeḍ iseqdacen ad gen ayagi.",
|
||||||
|
"Failed to send request.": "Tuzna n usuter ur teddi ara.",
|
||||||
|
"This room is not recognised.": "Taxxamt-a ur tṣeggem ara.",
|
||||||
|
"You are not in this room.": "Ulac-ik/ikem deg texxamt-a.",
|
||||||
|
"You do not have permission to do that in this room.": "Ur tesεiḍ ara tasiregt ad tgeḍ ayagi deg texxamt-a.",
|
||||||
|
"Missing room_id in request": "Ixuṣṣ taxxamt_asulay deg usuter",
|
||||||
|
"Room %(roomId)s not visible": "Taxxamt %(roomId)s ur d-tban ara",
|
||||||
|
"Missing user_id in request": "Ixuṣṣ useqdac_asulay deg usuter",
|
||||||
|
"Command error": "Tuccḍa n tladna",
|
||||||
|
"/ddg is not a command": "/ddg mačči d taladna",
|
||||||
|
"Upgrades a room to a new version": "Leqqem taxxamt ɣer lqem amaynut",
|
||||||
|
"Error upgrading room": "Tuccḍa deg uleqqem n texxamt",
|
||||||
|
"Changes your avatar in this current room only": "Snifel avatar-ik/im deg texxamat-agi kan tamirant",
|
||||||
|
"Changes your avatar in all rooms": "Snifel avatar/ik/om deg yixxamin",
|
||||||
|
"Use an identity server": "Seqdec timagit n uqeddac",
|
||||||
|
"Joins room with given address": "Kcem ɣer texxamt s tansa i d-yettunefken",
|
||||||
|
"Leave room": "Ffeɣ seg texxamt",
|
||||||
|
"Ignores a user, hiding their messages from you": "Anef iuseqdac, ffer iznan-ines sɣur-k",
|
||||||
|
"Ignored user": "Aseqdac yettunfen",
|
||||||
|
"You are now ignoring %(userId)s": "Aql-ak tura tunfeḍ i %(userId)s",
|
||||||
|
"Command failed": "Taladna ur teddi ara",
|
||||||
|
"Could not find user in room": "Ur yettwaf ara useqdac deg texxamt",
|
||||||
|
"(no answer)": "(ulac tiririt)",
|
||||||
|
"New login. Was this you?": "Anekcam amaynut. D kečč/kemm?",
|
||||||
|
"Verify the new login accessing your account: %(name)s": "Senqed anekcam amaynut i ikecmen ɣer umiḍan-ik/im: %(name)s",
|
||||||
|
"What's new?": "D acu-t umaynut?",
|
||||||
|
"Upgrade your Riot": "Leqqem Riot inek/inem",
|
||||||
|
"A new version of Riot is available!": "Lqem amaynut n Riot yella!",
|
||||||
|
"You: %(message)s": "Kečč/kemm: %(message)s",
|
||||||
|
"There was an error joining the room": "Tella-d tuccḍa deg unekcum ɣer texxamt",
|
||||||
|
"Sorry, your homeserver is too old to participate in this room.": "Suref-aɣ, asebter-ik/im agejdan d aqbur aṭas akken ad yettekki deg texxamt-a.",
|
||||||
|
"Please contact your homeserver administrator.": "Ttxil-k/m nermes anedbal-ik/im n usebter agejdan.",
|
||||||
|
"Failed to join room": "Anekcum ɣer texxamt ur yeddi ara",
|
||||||
|
"Custom user status messages": "Sagen addaden n yiznan n useqdac",
|
||||||
|
"Support adding custom themes": "Tallalt n tmerna n yisental udmawanen",
|
||||||
|
"Use custom size": "Seqdec teɣzi tudmawant",
|
||||||
|
"Show avatar changes": "Sken isnifal n avatar",
|
||||||
|
"Always show encryption icons": "Sken yal tikkelt tignitin tiwgelhanen",
|
||||||
|
"Send typing notifications": "Azen ilɣa yettuszemlen",
|
||||||
|
"Show typing notifications": "Azen ilɣa yettuszemlen",
|
||||||
|
"Room Colour": "Initen n texxamt",
|
||||||
|
"Show developer tools": "Sken ifecka n uneflay",
|
||||||
|
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Ama tseqdaceḍ askar-inek.inem n umaẓrag n uḍris anesbaɣur neɣ xaṭi",
|
||||||
|
"Whether you're using Riot on a device where touch is the primary input mechanism": "Γas ma tseqdaceḍ Riot inek.inem deg yibenk anida asami d ametwi agejdan n unekcum",
|
||||||
|
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Γas ma tseqdaceḍ tamahilt 'breadcrumbs' neɣ xaṭi(avatar nnig tebdert n texxamt)",
|
||||||
|
"Whether you're using Riot as an installed Progressive Web App": "Γas ma tseqdaceḍ Riot d asnas web n usfari i ibedden",
|
||||||
|
"The information being sent to us to help make Riot better includes:": "Talɣut i aɣ-d-yettwaznen ɣef tallalt n usnerni n Riot deg-s:",
|
||||||
|
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ma yili asebter-a degs talɣut tummilt, am texxamt neɣ aseqdac neɣ asulay n ugraw, isefka-a ad ttwakksen send ad ttwaznen i uqeddac.",
|
||||||
|
"Unable to load! Check your network connectivity and try again.": "Yegguma ad d-yali! Senqed tuqqna-inek.inem ɣer uzeṭṭa syen tεerḍeḍ tikkelt-nniḍen.",
|
||||||
|
"Failure to create room": "Timerna n texxamt ur teddi ara",
|
||||||
|
"If you cancel now, you won't complete verifying your other session.": "Ma yella teffɣeḍ tura, ur tessawaḍeḍ ara ad tesneqdeḍ akk tiɣimiyin-inek.inem.",
|
||||||
|
"If you cancel now, you won't complete your operation.": "Ma yella teffɣeḍ tura, tamhelt-ik.im ur tettemmed ara.",
|
||||||
|
"Show these rooms to non-members on the community page and room list?": "Sken tixxamin-a i wid ur nettekka ara deg usebter n temɣiwent d tebdert n texxamt?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2146,5 +2146,15 @@
|
||||||
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Сообщения в этой комнате зашифрованы сквозным шифрованием. Посмотрите подробности и подтвердите пользователя в его профиле.",
|
"Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Сообщения в этой комнате зашифрованы сквозным шифрованием. Посмотрите подробности и подтвердите пользователя в его профиле.",
|
||||||
"Send a Direct Message": "Отправить сообщение",
|
"Send a Direct Message": "Отправить сообщение",
|
||||||
"Light": "Светлая",
|
"Light": "Светлая",
|
||||||
"Dark": "Темная"
|
"Dark": "Темная",
|
||||||
|
"Recent Conversations": "Недавние Диалоги",
|
||||||
|
"Suggestions": "Предложения",
|
||||||
|
"a key signature": "отпечаток ключа",
|
||||||
|
"Upload completed": "Отправка успешно завершена",
|
||||||
|
"Cancelled signature upload": "Отправка отпечатка отменена",
|
||||||
|
"Unable to upload": "Невозможно отправить",
|
||||||
|
"Signature upload success": "Отпечаток успешно отправлен",
|
||||||
|
"Signature upload failed": "Сбой отправки отпечатка",
|
||||||
|
"Room name or address": "Имя или адрес комнаты",
|
||||||
|
"Unrecognised room address:": "Не удалось найти адрес комнаты:"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2517,5 +2517,91 @@
|
||||||
"Create a Recovery Key to store encryption keys & secrets with your account data. If you lose access to this login you’ll need it to unlock your data.": "Krijoni një Kyç Rimarrjesh që të depozitoni kyçe & të fshehta fshehtëzimi me të dhënat e llogarisë tuaj. Nëse humbni këto kredenciale, do t’ju duhet të shkyçni të dhënat tuaja.",
|
"Create a Recovery Key to store encryption keys & secrets with your account data. If you lose access to this login you’ll need it to unlock your data.": "Krijoni një Kyç Rimarrjesh që të depozitoni kyçe & të fshehta fshehtëzimi me të dhënat e llogarisë tuaj. Nëse humbni këto kredenciale, do t’ju duhet të shkyçni të dhënat tuaja.",
|
||||||
"Create a Recovery Key": "Krijoni një Kyç Rimarrjesh",
|
"Create a Recovery Key": "Krijoni një Kyç Rimarrjesh",
|
||||||
"Upgrade your Recovery Key": "Përmirësoni Kyçin tuaj të Rimarrjeve",
|
"Upgrade your Recovery Key": "Përmirësoni Kyçin tuaj të Rimarrjeve",
|
||||||
"Store your Recovery Key": "Depozitoni Kyçin tuaj të Rimarrjeve"
|
"Store your Recovery Key": "Depozitoni Kyçin tuaj të Rimarrjeve",
|
||||||
|
"Use the improved room list (will refresh to apply changes)": "Përdor listën e përmirësuar të dhomave (do të rifreskohet, që të aplikohen ndryshimet)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "Aktivizoni te skeda e dukjes mundësinë për skemë IRC",
|
||||||
|
"Use custom size": "Përdor madhësi vetjake",
|
||||||
|
"Hey you. You're the best!": "Hej, ju. S’u ka kush shokun!",
|
||||||
|
"Message layout": "Skemë mesazhesh",
|
||||||
|
"Compact": "Kompakte",
|
||||||
|
"Modern": "Moderne",
|
||||||
|
"Use a system font": "Përdor një palë shkronja sistemi",
|
||||||
|
"System font name": "Emër shkronjash sistemi",
|
||||||
|
"You joined the call": "U bëtë pjesë e thirrjes",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s u bë pjesë e thirrjes",
|
||||||
|
"Call in progress": "Thirrje në ecuri e sipër",
|
||||||
|
"You left the call": "E braktisët thirrjen",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s e braktisi thirrjen",
|
||||||
|
"Call ended": "Thirrja përfundoi",
|
||||||
|
"You started a call": "Filluat një thirrje",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s filluat një thirrje",
|
||||||
|
"Waiting for answer": "Po pritet për përgjigje",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s po thërret",
|
||||||
|
"You created the room": "Krijuat dhomën",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s krijoi dhomën",
|
||||||
|
"You made the chat encrypted": "E bëtë të fshehtëzuar fjalosjen",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s e bëri të fshehtëzuar fjalosjen",
|
||||||
|
"You made history visible to new members": "E bëtë historikun të dukshëm për anëtarë të rinj",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s e bëri historikun të dukshëm për anëtarë të rinj",
|
||||||
|
"You made history visible to anyone": "E bëtë historikun të dukshëm për këdo",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s e bëri historikun të dukshëm për këdo",
|
||||||
|
"You made history visible to future members": "E bëtë historikun të dukshëm për anëtarë të ardhshëm",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s e bëri historikun të dukshëm për anëtarë të ardhshëm",
|
||||||
|
"You were invited": "U ftuat",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s u ftua",
|
||||||
|
"You left": "Dolët",
|
||||||
|
"%(targetName)s left": "%(targetName)s doli",
|
||||||
|
"You were kicked (%(reason)s)": "U përzutë (%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s u përzu (%(reason)s)",
|
||||||
|
"You were kicked": "U përzutë",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s u përzu",
|
||||||
|
"You rejected the invite": "S’pranuat ftesën",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s s’pranoi ftesën",
|
||||||
|
"You were uninvited": "Ju shfuqizuan ftesën",
|
||||||
|
"%(targetName)s was uninvited": "%(targetName)s i shfuqizuan ftesën",
|
||||||
|
"You were banned (%(reason)s)": "U dëbuat (%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s u dëbua (%(reason)s)",
|
||||||
|
"You were banned": "U dëbuat",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s u dëbua",
|
||||||
|
"You joined": "U bëtë pjesë",
|
||||||
|
"%(targetName)s joined": "%(targetName)s u bë pjesë",
|
||||||
|
"You changed your name": "Ndryshuat emrin",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s ndryshoi emrin e vet",
|
||||||
|
"You changed your avatar": "Ndryshuat avatarin tuaj",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s ndryshoi avatarin e vet",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "Ndryshuat emrin e dhomës",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s ndryshoi emrin e dhomës",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "Shfuqizuat ftesën për %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s shfuqizoi ftesën për %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "Ftuat %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s ftoi %(targetName)s",
|
||||||
|
"You changed the room topic": "Ndryshuat temën e dhomës",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s ndryshoi temën e dhomës",
|
||||||
|
"Use a more compact ‘Modern’ layout": "Përdorni një skemë ‘Modern’ më kompakte",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "Mirëfilltësia e këtij mesazhi të fshehtëzuar s’mund të garantohet në këtë pajisje.",
|
||||||
|
"Message deleted on %(date)s": "Mesazh i fshirë më %(date)s",
|
||||||
|
"Wrong file type": "Lloj i gabuar kartele",
|
||||||
|
"Wrong Recovery Key": "Kyç Rimarrjesh i Gabuar",
|
||||||
|
"Invalid Recovery Key": "Kyç Rimarrjesh i Pavlefshëm",
|
||||||
|
"Security Phrase": "Frazë Sigurie",
|
||||||
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Që të vazhdohet, jepni Frazën tuaj të Sigurisë ose <button>Përdorni Kyçin tuaj të Sigurisë</button>.",
|
||||||
|
"Security Key": "Kyç Sigurie",
|
||||||
|
"Use your Security Key to continue.": "Që të vazhdohet përdorni Kyçin tuaj të Sigurisë.",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Mbrohuni kundër humbjes së hyrjes në mesazhe & të dhëna të fshehtëzuara duke kopjeruajtur kyçe fshehtëzimi në shërbyesin tuaj.",
|
||||||
|
"Generate a Security Key": "Prodhoni një Kyç Sigurie",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "Do të prodhojmë për ju një Kyç Sigurie që ta depozitoni diku të parrezik, bie fjala në një përgjegjës fjalëkalimesh ose në një kasafortë.",
|
||||||
|
"Enter a Security Phrase": "Jepni një Frazë Sigurie",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Jepni një frazë të fshehtë që e dini vetëm ju, dhe, në daçi, ruani një Kyç Sigurie për ta përdorur për kopjeruajtje.",
|
||||||
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "Jepni një frazë sigurie që e dini vetëm ju, ngaqë përdoret për të mbrojtur të dhënat tuaja. Që të jeni të sigurt, s’duhet të ripërdorni fjalëkalimin e llogarisë tuaj.",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "Depozitojeni Kyçin tuaj të Sigurisë diku të parrezik, bie fjala në një përgjegjës fjalëkalimesh ose në një kasafortë, ngaqë përdoret për të mbrojtur të dhënat tuaja të fshehtëzuara.",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Nëse e anuloni tani, mund të humbni mesazhe & të dhëna të fshehtëzuara, nëse humbni hyrjen te kredencialet tuaja të hyrjeve.",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "Mundeni edhe të ujdisni Kopjeruajtje të Sigurt & administroni kyçet tuaj që nga Rregullimet.",
|
||||||
|
"Set up Secure backup": "Ujdisni kopjeruajtje të Sigurt",
|
||||||
|
"Set a Security Phrase": "Caktoni një Frazë Sigurie",
|
||||||
|
"Confirm Security Phrase": "Ripohoni Frazë Sigurie",
|
||||||
|
"Save your Security Key": "Ruani Kyçin tuaj të Sigurisë"
|
||||||
}
|
}
|
||||||
|
|
|
@ -600,8 +600,8 @@
|
||||||
"Language and region": "Мова та регіон",
|
"Language and region": "Мова та регіон",
|
||||||
"Account management": "Керування обліківкою",
|
"Account management": "Керування обліківкою",
|
||||||
"Deactivating your account is a permanent action - be careful!": "Деактивація вашої обліківки є безповоротною дією — будьте обережні!",
|
"Deactivating your account is a permanent action - be careful!": "Деактивація вашої обліківки є безповоротною дією — будьте обережні!",
|
||||||
"Deactivate Account": "Деактивувати обліківку",
|
"Deactivate Account": "Знедіяти обліківку",
|
||||||
"Deactivate account": "Деактивувати обліківку",
|
"Deactivate account": "Знедіяти обліківку",
|
||||||
"Legal": "Правова інформація",
|
"Legal": "Правова інформація",
|
||||||
"Credits": "Подяки",
|
"Credits": "Подяки",
|
||||||
"For help with using Riot, click <a>here</a>.": "Якщо необхідна допомога у користуванні Riot'ом, клацніть <a>тут</a>.",
|
"For help with using Riot, click <a>here</a>.": "Якщо необхідна допомога у користуванні Riot'ом, клацніть <a>тут</a>.",
|
||||||
|
@ -624,7 +624,7 @@
|
||||||
"Link this email with your account in Settings to receive invites directly in Riot.": "Зв'яжіть цю е-пошту з вашою обліківкою у Налаштуваннях щоб отримувати сповіщення прямо у Riot.",
|
"Link this email with your account in Settings to receive invites directly in Riot.": "Зв'яжіть цю е-пошту з вашою обліківкою у Налаштуваннях щоб отримувати сповіщення прямо у Riot.",
|
||||||
"This invite to %(roomName)s was sent to %(email)s": "Це запрошення до %(roomName)s було надіслане на %(email)s",
|
"This invite to %(roomName)s was sent to %(email)s": "Це запрошення до %(roomName)s було надіслане на %(email)s",
|
||||||
"Use an identity server in Settings to receive invites directly in Riot.": "Використовувати сервер ідентифікації у Налаштуваннях щоб отримувати запрошення прямо у Riot.",
|
"Use an identity server in Settings to receive invites directly in Riot.": "Використовувати сервер ідентифікації у Налаштуваннях щоб отримувати запрошення прямо у Riot.",
|
||||||
"Are you sure you want to deactivate your account? This is irreversible.": "Ви впевнені у тому, що бажаєте деактивувати вашу обліківку? Це є безповоротним.",
|
"Are you sure you want to deactivate your account? This is irreversible.": "Ви впевнені у тому, що бажаєте знедіяти вашу обліківку? Це є безповоротним.",
|
||||||
"Confirm account deactivation": "Підтвердьте деактивацію обліківки",
|
"Confirm account deactivation": "Підтвердьте деактивацію обліківки",
|
||||||
"To continue, please enter your password:": "Щоб продовжити, введіть, будь ласка, ваш пароль:",
|
"To continue, please enter your password:": "Щоб продовжити, введіть, будь ласка, ваш пароль:",
|
||||||
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "Ваша обліківка стане назавжди невикористовною. Ви не матимете змоги увійти в неї і ніхто не зможе перереєструватись під цим користувацьким ID. Це призведе до виходу вашої обліківки з усіх кімнат та до видалення деталей вашої обліківки з вашого серверу ідентифікації. <b>Ця дія є безповоротною.</b>",
|
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "Ваша обліківка стане назавжди невикористовною. Ви не матимете змоги увійти в неї і ніхто не зможе перереєструватись під цим користувацьким ID. Це призведе до виходу вашої обліківки з усіх кімнат та до видалення деталей вашої обліківки з вашого серверу ідентифікації. <b>Ця дія є безповоротною.</b>",
|
||||||
|
@ -660,5 +660,12 @@
|
||||||
"Room": "Кімната",
|
"Room": "Кімната",
|
||||||
"Failed to reject invite": "Не вдалось відхилити запрошення",
|
"Failed to reject invite": "Не вдалось відхилити запрошення",
|
||||||
"You have %(count)s unread notifications in a prior version of this room.|other": "Ви маєте %(count)s непрочитаних сповіщень у попередній версії цієї кімнати.",
|
"You have %(count)s unread notifications in a prior version of this room.|other": "Ви маєте %(count)s непрочитаних сповіщень у попередній версії цієї кімнати.",
|
||||||
"You have %(count)s unread notifications in a prior version of this room.|one": "У вас одне непрочитане сповіщення у попередній версії цієї кімнати."
|
"You have %(count)s unread notifications in a prior version of this room.|one": "У вас одне непрочитане сповіщення у попередній версії цієї кімнати.",
|
||||||
|
"Deactivate user?": "Знедіяти користувача?",
|
||||||
|
"Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Знедіювання цього користувача виведе їх з системи і унеможливить вхід у майбутньому. До того ж, вони залишать усі кімнати, в яких перебувають. Ця дія є безповоротною. Ви впевнені, що хочете знедіяти цього користувача?",
|
||||||
|
"Deactivate user": "Знедіяти користувача",
|
||||||
|
"Failed to deactivate user": "Не вдалось знедіяти користувача",
|
||||||
|
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Знедіювання вашої обліківки <b>типово не призводить до забуття надісланих вами повідомлень.</b> Якщо ви бажаєте щоб ми забули ваші повідомлення, поставте прапорець внизу.",
|
||||||
|
"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 є схожою до е-пошти. Забування нами ваших повідомлень означає, що надіслані вами повідомлення не будуть поширені будь-яким новим чи незареєстрованим користувачам, але зареєстровані користувачі, які мають доступ до цих повідомлень, і надалі матимуть доступ до їхніх копій.",
|
||||||
|
"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)": "Забудьте, будь ласка, усі надіслані мною повідомлення після знедіювання моєї обліківки. (<b>Попередження:</b> після цього майбутні користувачі бачитимуть неповні бесіди)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2527,5 +2527,91 @@
|
||||||
"Upgrade your Recovery Key": "升級您的復原金鑰",
|
"Upgrade your Recovery Key": "升級您的復原金鑰",
|
||||||
"Store your Recovery Key": "儲存您的復原金鑰",
|
"Store your Recovery Key": "儲存您的復原金鑰",
|
||||||
"Use the improved room list (in development - will refresh to apply changes)": "使用改進的聊天室清單(開發中 ── 將會重新整理以套用變更)",
|
"Use the improved room list (in development - will refresh to apply changes)": "使用改進的聊天室清單(開發中 ── 將會重新整理以套用變更)",
|
||||||
"Use the improved room list (will refresh to apply changes)": "使用改進的聊天室清單(將會重新整理以套用變更)"
|
"Use the improved room list (will refresh to apply changes)": "使用改進的聊天室清單(將會重新整理以套用變更)",
|
||||||
|
"Enable IRC layout option in the appearance tab": "在外觀分頁中啟用 IRC 佈局選項",
|
||||||
|
"Use custom size": "使用自訂大小",
|
||||||
|
"Hey you. You're the best!": "你是最棒的!",
|
||||||
|
"Message layout": "訊息佈局",
|
||||||
|
"Compact": "簡潔",
|
||||||
|
"Modern": "現代",
|
||||||
|
"Use a system font": "使用系統字型",
|
||||||
|
"System font name": "系統字型名稱",
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.": "無法在此裝置上保證加密訊息的真實性。",
|
||||||
|
"You joined the call": "您加入了通話",
|
||||||
|
"%(senderName)s joined the call": "%(senderName)s 加入了通話",
|
||||||
|
"Call in progress": "通話進行中",
|
||||||
|
"You left the call": "您離開了通話",
|
||||||
|
"%(senderName)s left the call": "%(senderName)s 離開了通話",
|
||||||
|
"Call ended": "通話結束",
|
||||||
|
"You started a call": "您開始了通話",
|
||||||
|
"%(senderName)s started a call": "%(senderName)s 開始了通話",
|
||||||
|
"Waiting for answer": "正在等待回應",
|
||||||
|
"%(senderName)s is calling": "%(senderName)s 正在通話",
|
||||||
|
"You created the room": "您建立了聊天室",
|
||||||
|
"%(senderName)s created the room": "%(senderName)s 建立了聊天室",
|
||||||
|
"You made the chat encrypted": "您讓聊天加密",
|
||||||
|
"%(senderName)s made the chat encrypted": "%(senderName)s 讓聊天加密",
|
||||||
|
"You made history visible to new members": "您讓歷史紀錄對新成員可見",
|
||||||
|
"%(senderName)s made history visible to new members": "%(senderName)s 讓歷史紀錄對新成員可見",
|
||||||
|
"You made history visible to anyone": "您讓歷史紀錄對所有人可見",
|
||||||
|
"%(senderName)s made history visible to anyone": "%(senderName)s 讓歷史紀錄對所有人可見",
|
||||||
|
"You made history visible to future members": "您讓歷史紀錄對未來成員可見",
|
||||||
|
"%(senderName)s made history visible to future members": "%(senderName)s 讓歷史紀錄對未來成員可見",
|
||||||
|
"You were invited": "您被邀請",
|
||||||
|
"%(targetName)s was invited": "%(targetName)s 被邀請",
|
||||||
|
"You left": "您離開",
|
||||||
|
"%(targetName)s left": "%(targetName)s 離開",
|
||||||
|
"You were kicked (%(reason)s)": "您被踢除(%(reason)s)",
|
||||||
|
"%(targetName)s was kicked (%(reason)s)": "%(targetName)s 被踢除(%(reason)s)",
|
||||||
|
"You were kicked": "您被踢除",
|
||||||
|
"%(targetName)s was kicked": "%(targetName)s 被踢除",
|
||||||
|
"You rejected the invite": "您回絕了邀請",
|
||||||
|
"%(targetName)s rejected the invite": "%(targetName)s 回絕了邀請",
|
||||||
|
"You were uninvited": "您被取消邀請",
|
||||||
|
"%(targetName)s was uninvited": "%(targetName)s 被取消邀請",
|
||||||
|
"You were banned (%(reason)s)": "您被封鎖(%(reason)s)",
|
||||||
|
"%(targetName)s was banned (%(reason)s)": "%(targetName)s 被封鎖(%(reason)s)",
|
||||||
|
"You were banned": "您被封鎖",
|
||||||
|
"%(targetName)s was banned": "%(targetName)s 被封鎖",
|
||||||
|
"You joined": "您加入",
|
||||||
|
"%(targetName)s joined": "%(targetName)s 加入",
|
||||||
|
"You changed your name": "您變更了您的名稱",
|
||||||
|
"%(targetName)s changed their name": "%(targetName)s 變更了他們的名稱",
|
||||||
|
"You changed your avatar": "您變更了您的大頭貼",
|
||||||
|
"%(targetName)s changed their avatar": "%(targetName)s 變更了他們的大頭貼",
|
||||||
|
"%(senderName)s %(emote)s": "%(senderName)s %(emote)s",
|
||||||
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
|
"You changed the room name": "您變更了聊天室名稱",
|
||||||
|
"%(senderName)s changed the room name": "%(senderName)s 變更了聊天室名稱",
|
||||||
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"You uninvited %(targetName)s": "您取消邀請了 %(targetName)s",
|
||||||
|
"%(senderName)s uninvited %(targetName)s": "%(senderName)s 取消邀請了 %(targetName)s",
|
||||||
|
"You invited %(targetName)s": "您邀請了 %(targetName)s",
|
||||||
|
"%(senderName)s invited %(targetName)s": "%(senderName)s 邀請了 %(targetName)s",
|
||||||
|
"You changed the room topic": "您變更了聊天室主題",
|
||||||
|
"%(senderName)s changed the room topic": "%(senderName)s 變更了聊天室主題",
|
||||||
|
"New spinner design": "新的微調器設計",
|
||||||
|
"Use a more compact ‘Modern’ layout": "使用更簡潔的「現代」佈局",
|
||||||
|
"Message deleted on %(date)s": "訊息刪除於 %(date)s",
|
||||||
|
"Wrong file type": "錯誤的檔案類型",
|
||||||
|
"Wrong Recovery Key": "錯誤的復原金鑰",
|
||||||
|
"Invalid Recovery Key": "無效的復原金鑰",
|
||||||
|
"Security Phrase": "安全密語",
|
||||||
|
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "輸入您的安全密語或<button>使用您的安全金鑰</button>以繼續。",
|
||||||
|
"Security Key": "安全金鑰",
|
||||||
|
"Use your Security Key to continue.": "使用您的安全金鑰以繼續。",
|
||||||
|
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "透過備份您伺服器上的加密金鑰來防止失去對您已加密的訊息與資料的存取權。",
|
||||||
|
"Generate a Security Key": "生成加密金鑰",
|
||||||
|
"We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "我們將會為您生成一把安全金鑰,供您存放在安全的地方,如密碼管理員或保險櫃等。",
|
||||||
|
"Enter a Security Phrase": "輸入安全密語",
|
||||||
|
"Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "使用僅有您知道的祕密短語,並選擇性地儲存安全金鑰以供備用。",
|
||||||
|
"Enter a security phrase only you know, as it’s used to safeguard your data. To be secure, you shouldn’t re-use your account password.": "輸入僅有您知道的安全短語,其用於保護您的資料。安全起見,請勿重複使用您的帳號密碼。",
|
||||||
|
"Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "將您的安全金鑰存放在某個安全的地方,如密碼管理員或保險櫃,其用於保護您的加密資料。",
|
||||||
|
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "如果您現在取消,在您失去對您的登入的存取權時可能會遺失已加密的訊息與資料。",
|
||||||
|
"You can also set up Secure Backup & manage your keys in Settings.": "您也可以在設定中設定安全備份並管理您的金鑰。",
|
||||||
|
"Set up Secure backup": "設定安全備份",
|
||||||
|
"Set a Security Phrase": "設定安全密語",
|
||||||
|
"Confirm Security Phrase": "確認安全密語",
|
||||||
|
"Save your Security Key": "儲存您的安全金鑰"
|
||||||
}
|
}
|
||||||
|
|
|
@ -478,11 +478,13 @@ export const SETTINGS = {
|
||||||
deny: [],
|
deny: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14231
|
||||||
"RoomList.orderAlphabetically": {
|
"RoomList.orderAlphabetically": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
displayName: _td("Order rooms by name"),
|
displayName: _td("Order rooms by name"),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
// TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14231
|
||||||
"RoomList.orderByImportance": {
|
"RoomList.orderByImportance": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
displayName: _td("Show rooms with unread notifications first"),
|
displayName: _td("Show rooms with unread notifications first"),
|
||||||
|
|
|
@ -24,7 +24,7 @@ export interface IToast<C extends keyof JSX.IntrinsicElements | JSXElementConstr
|
||||||
title: string;
|
title: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
component: C;
|
component: C;
|
||||||
props?: React.ComponentProps<C>;
|
props?: Omit<React.ComponentProps<C>, "toastKey">; // toastKey is injected by ToastContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,9 +18,9 @@ import { TagID } from "./models";
|
||||||
|
|
||||||
const TILE_HEIGHT_PX = 44;
|
const TILE_HEIGHT_PX = 44;
|
||||||
|
|
||||||
// the .65 comes from the CSS where the show more button is
|
// this comes from the CSS where the show more button is
|
||||||
// mathematically 65% of a tile when floating.
|
// mathematically this percent of a tile when floating.
|
||||||
const RESIZER_BOX_FACTOR = 0.65;
|
const RESIZER_BOX_FACTOR = 0.78;
|
||||||
|
|
||||||
interface ISerializedListLayout {
|
interface ISerializedListLayout {
|
||||||
numTiles: number;
|
numTiles: number;
|
||||||
|
@ -85,10 +85,16 @@ export class ListLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get defaultVisibleTiles(): number {
|
public get defaultVisibleTiles(): number {
|
||||||
// TODO: Remove dogfood flag: https://github.com/vector-im/riot-web/issues/14231
|
// 10 is what "feels right", and mostly subject to design's opinion.
|
||||||
// TODO: Resolve dogfooding: https://github.com/vector-im/riot-web/issues/14137
|
return 10 + RESIZER_BOX_FACTOR;
|
||||||
const val = Number(localStorage.getItem("mx_dogfood_rl_defTiles") || 4);
|
}
|
||||||
return val + RESIZER_BOX_FACTOR;
|
|
||||||
|
public setVisibleTilesWithin(diff: number, maxPossible: number) {
|
||||||
|
if (this.visibleTiles > maxPossible) {
|
||||||
|
this.visibleTiles = maxPossible + diff;
|
||||||
|
} else {
|
||||||
|
this.visibleTiles += diff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public calculateTilesToPixelsMin(maxTiles: number, n: number, possiblePadding: number): number {
|
public calculateTilesToPixelsMin(maxTiles: number, n: number, possiblePadding: number): number {
|
||||||
|
@ -122,6 +128,10 @@ export class ListLayout {
|
||||||
return px / this.tileHeight;
|
return px / this.tileHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
localStorage.removeItem(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
private save() {
|
private save() {
|
||||||
localStorage.setItem(this.key, JSON.stringify(this.serialize()));
|
localStorage.setItem(this.key, JSON.stringify(this.serialize()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ import { IFilterCondition } from "./filters/IFilterCondition";
|
||||||
import { TagWatcher } from "./TagWatcher";
|
import { TagWatcher } from "./TagWatcher";
|
||||||
import RoomViewStore from "../RoomViewStore";
|
import RoomViewStore from "../RoomViewStore";
|
||||||
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
||||||
|
import { EffectiveMembership, getEffectiveMembership } from "./membership";
|
||||||
|
import { ListLayout } from "./ListLayout";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
tagsEnabled?: boolean;
|
tagsEnabled?: boolean;
|
||||||
|
@ -49,8 +51,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
private tagWatcher = new TagWatcher(this);
|
private tagWatcher = new TagWatcher(this);
|
||||||
|
|
||||||
private readonly watchedSettings = [
|
private readonly watchedSettings = [
|
||||||
'RoomList.orderAlphabetically',
|
|
||||||
'RoomList.orderByImportance',
|
|
||||||
'feature_custom_tags',
|
'feature_custom_tags',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -97,8 +97,10 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
this.algorithm.stickyRoom = null;
|
this.algorithm.stickyRoom = null;
|
||||||
} else if (activeRoomId) {
|
} else if (activeRoomId) {
|
||||||
const activeRoom = this.matrixClient.getRoom(activeRoomId);
|
const activeRoom = this.matrixClient.getRoom(activeRoomId);
|
||||||
if (!activeRoom) throw new Error(`${activeRoomId} is current in RVS but missing from client`);
|
if (!activeRoom) {
|
||||||
if (activeRoom !== this.algorithm.stickyRoom) {
|
console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`);
|
||||||
|
this.algorithm.stickyRoom = null;
|
||||||
|
} else if (activeRoom !== this.algorithm.stickyRoom) {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log(`Changing sticky room to ${activeRoomId}`);
|
console.log(`Changing sticky room to ${activeRoomId}`);
|
||||||
this.algorithm.stickyRoom = activeRoom;
|
this.algorithm.stickyRoom = activeRoom;
|
||||||
|
@ -187,10 +189,13 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()} in ${updatedRoom.roomId}`);
|
console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()} in ${updatedRoom.roomId}`);
|
||||||
if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') {
|
if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log(`[RoomListDebug] Got tombstone event - regenerating room list`);
|
console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`);
|
||||||
// TODO: We could probably be smarter about this: https://github.com/vector-im/riot-web/issues/14035
|
const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']);
|
||||||
await this.regenerateAllLists();
|
if (newRoom) {
|
||||||
return; // don't pass the update down - we will have already handled it in the regen
|
// If we have the new room, then the new room check will have seen the predecessor
|
||||||
|
// and did the required updates, so do nothing here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.handleRoomUpdate(updatedRoom, RoomUpdateCause.Timeline);
|
await this.handleRoomUpdate(updatedRoom, RoomUpdateCause.Timeline);
|
||||||
};
|
};
|
||||||
|
@ -242,18 +247,49 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
}
|
}
|
||||||
} else if (payload.action === 'MatrixActions.Room.myMembership') {
|
} else if (payload.action === 'MatrixActions.Room.myMembership') {
|
||||||
const membershipPayload = (<any>payload); // TODO: Type out the dispatcher types
|
const membershipPayload = (<any>payload); // TODO: Type out the dispatcher types
|
||||||
if (membershipPayload.oldMembership !== "join" && membershipPayload.membership === "join") {
|
const oldMembership = getEffectiveMembership(membershipPayload.oldMembership);
|
||||||
|
const newMembership = getEffectiveMembership(membershipPayload.membership);
|
||||||
|
if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`);
|
console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`);
|
||||||
await this.algorithm.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
|
|
||||||
|
// If we're joining an upgraded room, we'll want to make sure we don't proliferate
|
||||||
|
// the dead room in the list.
|
||||||
|
const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", "");
|
||||||
|
if (createEvent && createEvent.getContent()['predecessor']) {
|
||||||
|
console.log(`[RoomListDebug] Room has a predecessor`);
|
||||||
|
const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']);
|
||||||
|
if (prevRoom) {
|
||||||
|
const isSticky = this.algorithm.stickyRoom === prevRoom;
|
||||||
|
if (isSticky) {
|
||||||
|
console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`);
|
||||||
|
await this.algorithm.setStickyRoomAsync(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we hit the algorithm instead of our handleRoomUpdate() function to
|
||||||
|
// avoid redundant updates.
|
||||||
|
console.log(`[RoomListDebug] Removing previous room from room list`);
|
||||||
|
await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[RoomListDebug] Adding new room to room list`);
|
||||||
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) {
|
||||||
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
|
console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`);
|
||||||
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not a join, it's transitioning into a different list (possibly historical)
|
// If it's not a join, it's transitioning into a different list (possibly historical)
|
||||||
if (membershipPayload.oldMembership !== membershipPayload.membership) {
|
if (oldMembership !== newMembership) {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`);
|
console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`);
|
||||||
await this.algorithm.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange);
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,11 +337,8 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateAlgorithmInstances() {
|
private async updateAlgorithmInstances() {
|
||||||
const orderByImportance = SettingsStore.getValue("RoomList.orderByImportance");
|
const defaultSort = SortAlgorithm.Alphabetic;
|
||||||
const orderAlphabetically = SettingsStore.getValue("RoomList.orderAlphabetically");
|
const defaultOrder = ListAlgorithm.Natural;
|
||||||
|
|
||||||
const defaultSort = orderAlphabetically ? SortAlgorithm.Alphabetic : SortAlgorithm.Recent;
|
|
||||||
const defaultOrder = orderByImportance ? ListAlgorithm.Importance : ListAlgorithm.Natural;
|
|
||||||
|
|
||||||
for (const tag of Object.keys(this.orderedLists)) {
|
for (const tag of Object.keys(this.orderedLists)) {
|
||||||
const definedSort = this.getTagSorting(tag);
|
const definedSort = this.getTagSorting(tag);
|
||||||
|
@ -364,6 +397,15 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
||||||
this.emit(LISTS_UPDATE_EVENT, this);
|
this.emit(LISTS_UPDATE_EVENT, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: this primarily exists for debugging, and isn't really intended to be used by anything.
|
||||||
|
public async resetLayouts() {
|
||||||
|
console.warn("Resetting layouts for room list");
|
||||||
|
for (const tagId of Object.keys(this.orderedLists)) {
|
||||||
|
new ListLayout(tagId).reset();
|
||||||
|
}
|
||||||
|
await this.regenerateAllLists();
|
||||||
|
}
|
||||||
|
|
||||||
public addFilter(filter: IFilterCondition): void {
|
public addFilter(filter: IFilterCondition): void {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log("Adding filter condition:", filter);
|
console.log("Adding filter condition:", filter);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import {
|
||||||
SortAlgorithm
|
SortAlgorithm
|
||||||
} from "./models";
|
} from "./models";
|
||||||
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "../filters/IFilterCondition";
|
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "../filters/IFilterCondition";
|
||||||
import { EffectiveMembership, splitRoomsByMembership } from "../membership";
|
import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } from "../membership";
|
||||||
import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
|
import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
|
||||||
import { getListAlgorithmInstance } from "./list-ordering";
|
import { getListAlgorithmInstance } from "./list-ordering";
|
||||||
|
|
||||||
|
@ -99,6 +99,14 @@ export class Algorithm extends EventEmitter {
|
||||||
return this._cachedRooms;
|
return this._cachedRooms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Awaitable version of the sticky room setter.
|
||||||
|
* @param val The new room to sticky.
|
||||||
|
*/
|
||||||
|
public async setStickyRoomAsync(val: Room) {
|
||||||
|
await this.updateStickyRoom(val);
|
||||||
|
}
|
||||||
|
|
||||||
public getTagSorting(tagId: TagID): SortAlgorithm {
|
public getTagSorting(tagId: TagID): SortAlgorithm {
|
||||||
return this.sortAlgorithms[tagId];
|
return this.sortAlgorithms[tagId];
|
||||||
}
|
}
|
||||||
|
@ -160,10 +168,13 @@ export class Algorithm extends EventEmitter {
|
||||||
// It's possible to have no selected room. In that case, clear the sticky room
|
// It's possible to have no selected room. In that case, clear the sticky room
|
||||||
if (!val) {
|
if (!val) {
|
||||||
if (this._stickyRoom) {
|
if (this._stickyRoom) {
|
||||||
|
const stickyRoom = this._stickyRoom.room;
|
||||||
|
this._stickyRoom = null; // clear before we go to update the algorithm
|
||||||
|
|
||||||
// Lie to the algorithm and re-add the room to the algorithm
|
// Lie to the algorithm and re-add the room to the algorithm
|
||||||
await this.handleRoomUpdate(this._stickyRoom.room, RoomUpdateCause.NewRoom);
|
await this.handleRoomUpdate(stickyRoom, RoomUpdateCause.NewRoom);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this._stickyRoom = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,6 +300,8 @@ export class Algorithm extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected recalculateFilteredRoomsForTag(tagId: TagID): void {
|
protected recalculateFilteredRoomsForTag(tagId: TagID): void {
|
||||||
|
if (!this.hasFilters) return; // don't bother doing work if there's nothing to do
|
||||||
|
|
||||||
console.log(`Recalculating filtered rooms for ${tagId}`);
|
console.log(`Recalculating filtered rooms for ${tagId}`);
|
||||||
delete this.filteredRooms[tagId];
|
delete this.filteredRooms[tagId];
|
||||||
const rooms = this.cachedRooms[tagId].map(r => r); // cheap clone
|
const rooms = this.cachedRooms[tagId].map(r => r); // cheap clone
|
||||||
|
@ -428,6 +441,13 @@ export class Algorithm extends EventEmitter {
|
||||||
if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`);
|
if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`);
|
||||||
if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`);
|
if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`);
|
||||||
|
|
||||||
|
console.warn("Resetting known rooms, initiating regeneration");
|
||||||
|
|
||||||
|
// Before we go any further we need to clear (but remember) the sticky room to
|
||||||
|
// avoid accidentally duplicating it in the list.
|
||||||
|
const oldStickyRoom = this._stickyRoom;
|
||||||
|
await this.updateStickyRoom(null);
|
||||||
|
|
||||||
this.rooms = rooms;
|
this.rooms = rooms;
|
||||||
|
|
||||||
const newTags: ITagMap = {};
|
const newTags: ITagMap = {};
|
||||||
|
@ -458,14 +478,7 @@ export class Algorithm extends EventEmitter {
|
||||||
|
|
||||||
// Now process all the joined rooms. This is a bit more complicated
|
// Now process all the joined rooms. This is a bit more complicated
|
||||||
for (const room of memberships[EffectiveMembership.Join]) {
|
for (const room of memberships[EffectiveMembership.Join]) {
|
||||||
let tags = Object.keys(room.tags || {});
|
const tags = this.getTagsOfJoinedRoom(room);
|
||||||
|
|
||||||
if (tags.length === 0) {
|
|
||||||
// Check to see if it's a DM if it isn't anything else
|
|
||||||
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
|
|
||||||
tags = [DefaultTagID.DM];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inTag = false;
|
let inTag = false;
|
||||||
if (tags.length > 0) {
|
if (tags.length > 0) {
|
||||||
|
@ -494,6 +507,54 @@ export class Algorithm extends EventEmitter {
|
||||||
|
|
||||||
this.cachedRooms = newTags;
|
this.cachedRooms = newTags;
|
||||||
this.updateTagsFromCache();
|
this.updateTagsFromCache();
|
||||||
|
this.recalculateFilteredRooms();
|
||||||
|
|
||||||
|
// Now that we've finished generation, we need to update the sticky room to what
|
||||||
|
// it was. It's entirely possible that it changed lists though, so if it did then
|
||||||
|
// we also have to update the position of it.
|
||||||
|
if (oldStickyRoom && oldStickyRoom.room) {
|
||||||
|
await this.updateStickyRoom(oldStickyRoom.room);
|
||||||
|
if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan
|
||||||
|
if (this._stickyRoom.tag !== oldStickyRoom.tag) {
|
||||||
|
// We put the sticky room at the top of the list to treat it as an obvious tag change.
|
||||||
|
this._stickyRoom.position = 0;
|
||||||
|
this.recalculateStickyRoom(this._stickyRoom.tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTagsForRoom(room: Room): TagID[] {
|
||||||
|
// XXX: This duplicates a lot of logic from setKnownRooms above, but has a slightly
|
||||||
|
// different use case and therefore different performance curve
|
||||||
|
|
||||||
|
const tags: TagID[] = [];
|
||||||
|
|
||||||
|
const membership = getEffectiveMembership(room.getMyMembership());
|
||||||
|
if (membership === EffectiveMembership.Invite) {
|
||||||
|
tags.push(DefaultTagID.Invite);
|
||||||
|
} else if (membership === EffectiveMembership.Leave) {
|
||||||
|
tags.push(DefaultTagID.Archived);
|
||||||
|
} else {
|
||||||
|
tags.push(...this.getTagsOfJoinedRoom(room));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tags.length) tags.push(DefaultTagID.Untagged);
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTagsOfJoinedRoom(room: Room): TagID[] {
|
||||||
|
let tags = Object.keys(room.tags || {});
|
||||||
|
|
||||||
|
if (tags.length === 0) {
|
||||||
|
// Check to see if it's a DM if it isn't anything else
|
||||||
|
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
|
||||||
|
tags = [DefaultTagID.DM];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -548,6 +609,14 @@ export class Algorithm extends EventEmitter {
|
||||||
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||||
if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from");
|
if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from");
|
||||||
|
|
||||||
|
if (cause === RoomUpdateCause.NewRoom) {
|
||||||
|
const roomTags = this.roomIdsToTags[room.roomId];
|
||||||
|
if (roomTags && roomTags.length > 0) {
|
||||||
|
console.warn(`${room.roomId} is reportedly new but is already known - assuming TagChange instead`);
|
||||||
|
cause = RoomUpdateCause.PossibleTagChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cause === RoomUpdateCause.PossibleTagChange) {
|
if (cause === RoomUpdateCause.PossibleTagChange) {
|
||||||
// TODO: Be smarter and splice rather than regen the planet. https://github.com/vector-im/riot-web/issues/14035
|
// TODO: Be smarter and splice rather than regen the planet. https://github.com/vector-im/riot-web/issues/14035
|
||||||
// TODO: No-op if no change. https://github.com/vector-im/riot-web/issues/14035
|
// TODO: No-op if no change. https://github.com/vector-im/riot-web/issues/14035
|
||||||
|
@ -566,6 +635,19 @@ export class Algorithm extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cause === RoomUpdateCause.NewRoom && !this.roomIdsToTags[room.roomId]) {
|
||||||
|
console.log(`[RoomListDebug] Updating tags for new room ${room.roomId} (${room.name})`);
|
||||||
|
|
||||||
|
// Get the tags for the room and populate the cache
|
||||||
|
const roomTags = this.getTagsForRoom(room).filter(t => !isNullOrUndefined(this.cachedRooms[t]));
|
||||||
|
|
||||||
|
// "This should never happen" condition - we specify DefaultTagID.Untagged in getTagsForRoom(),
|
||||||
|
// which means we should *always* have a tag to go off of.
|
||||||
|
if (!roomTags.length) throw new Error(`Tags cannot be determined for ${room.roomId}`);
|
||||||
|
|
||||||
|
this.roomIdsToTags[room.roomId] = roomTags;
|
||||||
|
}
|
||||||
|
|
||||||
let tags = this.roomIdsToTags[room.roomId];
|
let tags = this.roomIdsToTags[room.roomId];
|
||||||
if (!tags) {
|
if (!tags) {
|
||||||
console.warn(`No tags known for "${room.name}" (${room.roomId})`);
|
console.warn(`No tags known for "${room.name}" (${room.roomId})`);
|
||||||
|
|
|
@ -179,45 +179,51 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||||
if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) {
|
try {
|
||||||
return this.handleSplice(room, cause);
|
await this.updateLock.acquireAsync();
|
||||||
|
|
||||||
|
if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) {
|
||||||
|
return this.handleSplice(room, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) {
|
||||||
|
throw new Error(`Unsupported update cause: ${cause}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const category = this.getRoomCategory(room);
|
||||||
|
if (this.sortingAlgorithm === SortAlgorithm.Manual) {
|
||||||
|
return; // Nothing to do here.
|
||||||
|
}
|
||||||
|
|
||||||
|
const roomIdx = this.getRoomIndex(room);
|
||||||
|
if (roomIdx === -1) {
|
||||||
|
throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to avoid doing array operations if we don't have to: only move rooms within
|
||||||
|
// the categories if we're jumping categories
|
||||||
|
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
||||||
|
if (oldCategory !== category) {
|
||||||
|
// Move the room and update the indices
|
||||||
|
this.moveRoomIndexes(1, oldCategory, category, this.indices);
|
||||||
|
this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position)
|
||||||
|
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||||
|
// Note: if moveRoomIndexes() is called after the splice then the insert operation
|
||||||
|
// will happen in the wrong place. Because we would have already adjusted the index
|
||||||
|
// for the category, we don't need to determine how the room is moving in the list.
|
||||||
|
// If we instead tried to insert before updating the indices, we'd have to determine
|
||||||
|
// whether the room was moving later (towards IDLE) or earlier (towards RED) from its
|
||||||
|
// current position, as it'll affect the category's start index after we remove the
|
||||||
|
// room from the array.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the category now that we've dumped the room in
|
||||||
|
await this.sortCategory(category);
|
||||||
|
|
||||||
|
return true; // change made
|
||||||
|
} finally {
|
||||||
|
await this.updateLock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) {
|
|
||||||
throw new Error(`Unsupported update cause: ${cause}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const category = this.getRoomCategory(room);
|
|
||||||
if (this.sortingAlgorithm === SortAlgorithm.Manual) {
|
|
||||||
return; // Nothing to do here.
|
|
||||||
}
|
|
||||||
|
|
||||||
const roomIdx = this.getRoomIndex(room);
|
|
||||||
if (roomIdx === -1) {
|
|
||||||
throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to avoid doing array operations if we don't have to: only move rooms within
|
|
||||||
// the categories if we're jumping categories
|
|
||||||
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
|
||||||
if (oldCategory !== category) {
|
|
||||||
// Move the room and update the indices
|
|
||||||
this.moveRoomIndexes(1, oldCategory, category, this.indices);
|
|
||||||
this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position)
|
|
||||||
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
|
||||||
// Note: if moveRoomIndexes() is called after the splice then the insert operation
|
|
||||||
// will happen in the wrong place. Because we would have already adjusted the index
|
|
||||||
// for the category, we don't need to determine how the room is moving in the list.
|
|
||||||
// If we instead tried to insert before updating the indices, we'd have to determine
|
|
||||||
// whether the room was moving later (towards IDLE) or earlier (towards RED) from its
|
|
||||||
// current position, as it'll affect the category's start index after we remove the
|
|
||||||
// room from the array.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the category now that we've dumped the room in
|
|
||||||
await this.sortCategory(category);
|
|
||||||
|
|
||||||
return true; // change made
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sortCategory(category: Category) {
|
private async sortCategory(category: Category) {
|
||||||
|
|
|
@ -38,23 +38,29 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleRoomUpdate(room, cause): Promise<boolean> {
|
public async handleRoomUpdate(room, cause): Promise<boolean> {
|
||||||
const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved;
|
try {
|
||||||
const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt;
|
await this.updateLock.acquireAsync();
|
||||||
if (!isSplice && !isInPlace) {
|
|
||||||
throw new Error(`Unsupported update cause: ${cause}`);
|
const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved;
|
||||||
|
const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt;
|
||||||
|
if (!isSplice && !isInPlace) {
|
||||||
|
throw new Error(`Unsupported update cause: ${cause}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cause === RoomUpdateCause.NewRoom) {
|
||||||
|
this.cachedOrderedRooms.push(room);
|
||||||
|
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||||
|
const idx = this.cachedOrderedRooms.indexOf(room);
|
||||||
|
if (idx >= 0) this.cachedOrderedRooms.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14035
|
||||||
|
// For example, we can skip updates to alphabetic (sometimes) and manually ordered tags
|
||||||
|
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
await this.updateLock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cause === RoomUpdateCause.NewRoom) {
|
|
||||||
this.cachedOrderedRooms.push(room);
|
|
||||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
|
||||||
const idx = this.cachedOrderedRooms.indexOf(room);
|
|
||||||
if (idx >= 0) this.cachedOrderedRooms.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14035
|
|
||||||
// For example, we can skip updates to alphabetic (sometimes) and manually ordered tags
|
|
||||||
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { RoomUpdateCause, TagID } from "../../models";
|
import { RoomUpdateCause, TagID } from "../../models";
|
||||||
import { SortAlgorithm } from "../models";
|
import { SortAlgorithm } from "../models";
|
||||||
|
import AwaitLock from "await-lock";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a list ordering algorithm. Subclasses should populate the
|
* Represents a list ordering algorithm. Subclasses should populate the
|
||||||
|
@ -25,6 +26,7 @@ import { SortAlgorithm } from "../models";
|
||||||
export abstract class OrderingAlgorithm {
|
export abstract class OrderingAlgorithm {
|
||||||
protected cachedOrderedRooms: Room[];
|
protected cachedOrderedRooms: Room[];
|
||||||
protected sortingAlgorithm: SortAlgorithm;
|
protected sortingAlgorithm: SortAlgorithm;
|
||||||
|
protected readonly updateLock = new AwaitLock();
|
||||||
|
|
||||||
protected constructor(protected tagId: TagID, initialSortingAlgorithm: SortAlgorithm) {
|
protected constructor(protected tagId: TagID, initialSortingAlgorithm: SortAlgorithm) {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
|
|
|
@ -22,8 +22,11 @@ import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
export class ReactionEventPreview implements IPreview {
|
export class ReactionEventPreview implements IPreview {
|
||||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||||
const reaction = event.getRelation().key;
|
const relation = event.getRelation();
|
||||||
if (!reaction) return;
|
if (!relation) return null; // invalid reaction (probably redacted)
|
||||||
|
|
||||||
|
const reaction = relation.key;
|
||||||
|
if (!reaction) return null; // invalid reaction (unknown format)
|
||||||
|
|
||||||
if (isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
if (isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||||
return reaction;
|
return reaction;
|
||||||
|
|
|
@ -40,19 +40,20 @@ describe("AccessSecretStorageDialog", function() {
|
||||||
testInstance.getInstance()._onRecoveryKeyNext(e);
|
testInstance.getInstance()._onRecoveryKeyNext(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Considers a valid key to be valid", function() {
|
it("Considers a valid key to be valid", async function() {
|
||||||
const testInstance = TestRenderer.create(
|
const testInstance = TestRenderer.create(
|
||||||
<AccessSecretStorageDialog
|
<AccessSecretStorageDialog
|
||||||
checkPrivateKey={() => true}
|
checkPrivateKey={() => true}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const v = "asfd";
|
const v = "asdf";
|
||||||
const e = { target: { value: v } };
|
const e = { target: { value: v } };
|
||||||
stubClient();
|
stubClient();
|
||||||
MatrixClientPeg.get().isValidRecoveryKey = function(k) {
|
MatrixClientPeg.get().keyBackupKeyFromRecoveryKey = () => 'a raw key';
|
||||||
return k == v;
|
MatrixClientPeg.get().checkSecretStorageKey = () => true;
|
||||||
};
|
|
||||||
testInstance.getInstance()._onRecoveryKeyChange(e);
|
testInstance.getInstance()._onRecoveryKeyChange(e);
|
||||||
|
// force a validation now because it debounces
|
||||||
|
await testInstance.getInstance()._validateRecoveryKey();
|
||||||
const { recoveryKeyValid } = testInstance.getInstance().state;
|
const { recoveryKeyValid } = testInstance.getInstance().state;
|
||||||
expect(recoveryKeyValid).toBe(true);
|
expect(recoveryKeyValid).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -65,17 +66,21 @@ describe("AccessSecretStorageDialog", function() {
|
||||||
);
|
);
|
||||||
const e = { target: { value: "a" } };
|
const e = { target: { value: "a" } };
|
||||||
stubClient();
|
stubClient();
|
||||||
MatrixClientPeg.get().isValidRecoveryKey = () => true;
|
MatrixClientPeg.get().keyBackupKeyFromRecoveryKey = () => {
|
||||||
|
throw new Error("that's no key");
|
||||||
|
};
|
||||||
testInstance.getInstance()._onRecoveryKeyChange(e);
|
testInstance.getInstance()._onRecoveryKeyChange(e);
|
||||||
await testInstance.getInstance()._onRecoveryKeyNext({ preventDefault: () => {} });
|
// force a validation now because it debounces
|
||||||
const { keyMatches } = testInstance.getInstance().state;
|
await testInstance.getInstance()._validateRecoveryKey();
|
||||||
expect(keyMatches).toBe(false);
|
|
||||||
|
const { recoveryKeyValid, recoveryKeyCorrect } = testInstance.getInstance().state;
|
||||||
|
expect(recoveryKeyValid).toBe(false);
|
||||||
|
expect(recoveryKeyCorrect).toBe(false);
|
||||||
const notification = testInstance.root.findByProps({
|
const notification = testInstance.root.findByProps({
|
||||||
className: "mx_AccessSecretStorageDialog_keyStatus",
|
className: "mx_AccessSecretStorageDialog_recoveryKeyFeedback " +
|
||||||
|
"mx_AccessSecretStorageDialog_recoveryKeyFeedback_invalid",
|
||||||
});
|
});
|
||||||
expect(notification.props.children).toEqual(
|
expect(notification.props.children).toEqual("Invalid Recovery Key");
|
||||||
["\uD83D\uDC4E ", "Unable to access secret storage. Please verify that you " +
|
|
||||||
"entered the correct recovery key."]);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -79,20 +79,8 @@ module.exports = async function signup(session, username, password, homeserver)
|
||||||
const acceptButton = await session.query('.mx_InteractiveAuthEntryComponents_termsSubmit');
|
const acceptButton = await session.query('.mx_InteractiveAuthEntryComponents_termsSubmit');
|
||||||
await acceptButton.click();
|
await acceptButton.click();
|
||||||
|
|
||||||
//plow through cross-signing setup by entering arbitrary details
|
// Continue with the default (generate a security key)
|
||||||
//TODO: It's probably important for the tests to know the passphrase
|
const xsignContButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary');
|
||||||
const xsigningPassphrase = 'a7eaXcjpa9!Yl7#V^h$B^%dovHUVX'; // https://xkcd.com/221/
|
|
||||||
let passphraseField = await session.query('.mx_CreateSecretStorageDialog_passPhraseField input');
|
|
||||||
await session.replaceInputText(passphraseField, xsigningPassphrase);
|
|
||||||
await session.delay(1000); // give it a second to analyze our passphrase for security
|
|
||||||
let xsignContButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary');
|
|
||||||
await xsignContButton.click();
|
|
||||||
|
|
||||||
//repeat passphrase entry
|
|
||||||
passphraseField = await session.query('.mx_CreateSecretStorageDialog_passPhraseField input');
|
|
||||||
await session.replaceInputText(passphraseField, xsigningPassphrase);
|
|
||||||
await session.delay(1000); // give it a second to analyze our passphrase for security
|
|
||||||
xsignContButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary');
|
|
||||||
await xsignContButton.click();
|
await xsignContButton.click();
|
||||||
|
|
||||||
//ignore the recovery key
|
//ignore the recovery key
|
||||||
|
@ -101,13 +89,11 @@ module.exports = async function signup(session, username, password, homeserver)
|
||||||
await copyButton.click();
|
await copyButton.click();
|
||||||
|
|
||||||
//acknowledge that we copied the recovery key to a safe place
|
//acknowledge that we copied the recovery key to a safe place
|
||||||
const copyContinueButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_primary');
|
const copyContinueButton = await session.query(
|
||||||
|
'.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary',
|
||||||
|
);
|
||||||
await copyContinueButton.click();
|
await copyContinueButton.click();
|
||||||
|
|
||||||
//acknowledge that we're done cross-signing setup and our keys are safe
|
|
||||||
const doneOkButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_primary');
|
|
||||||
await doneOkButton.click();
|
|
||||||
|
|
||||||
//wait for registration to finish so the hash gets set
|
//wait for registration to finish so the hash gets set
|
||||||
//onhashchange better?
|
//onhashchange better?
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue