diff --git a/CHANGELOG.md b/CHANGELOG.md index 0706e20085..7901062b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,275 @@ +Changes in [2.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.0) (2020-05-19) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.6.0-rc.1...v2.6.0) + + * Upgrade to JS SDK 6.1.0 + * Revert "ImageView make clicking off it easier" + [\#4602](https://github.com/matrix-org/matrix-react-sdk/pull/4602) + * Remove debugging that causes email addresses to load forever (to release) + [\#4598](https://github.com/matrix-org/matrix-react-sdk/pull/4598) + +Changes in [2.6.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.0-rc.1) (2020-05-14) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.5.0...v2.6.0-rc.1) + + * Upgrade to JS SDK 6.1.0-rc.1 + * Update from Weblate + [\#4596](https://github.com/matrix-org/matrix-react-sdk/pull/4596) + * Fix message edits dialog being wrong and sometimes crashing + [\#4595](https://github.com/matrix-org/matrix-react-sdk/pull/4595) + * Acquire a new session before enacting deactivation + [\#4584](https://github.com/matrix-org/matrix-react-sdk/pull/4584) + * Remove UI for upgrading 4S to symmetric encryption + [\#4581](https://github.com/matrix-org/matrix-react-sdk/pull/4581) + * Add copy to SSO prompts during cross-signing setup + [\#4555](https://github.com/matrix-org/matrix-react-sdk/pull/4555) + * Re-fix OpenID requests from widgets + [\#4592](https://github.com/matrix-org/matrix-react-sdk/pull/4592) + * Fix persistent widgets on desktop / http + [\#4591](https://github.com/matrix-org/matrix-react-sdk/pull/4591) + * Updated link and added:Yarn two is not yet used. + [\#4589](https://github.com/matrix-org/matrix-react-sdk/pull/4589) + * Fix topic dialog not supporting escape as it didn't have a "Close" + [\#4578](https://github.com/matrix-org/matrix-react-sdk/pull/4578) + * Default to public room when creating room from room directory + [\#4579](https://github.com/matrix-org/matrix-react-sdk/pull/4579) + * Replace png flags and add Kosovo to country code dropdown + [\#4576](https://github.com/matrix-org/matrix-react-sdk/pull/4576) + * Rename `trash (custom).svg` as electron doesn't like paths with spaces + [\#4583](https://github.com/matrix-org/matrix-react-sdk/pull/4583) + * Fix sign in / up links on previewed rooms + [\#4582](https://github.com/matrix-org/matrix-react-sdk/pull/4582) + * Avoid soft crash if unknown device in verification + [\#4580](https://github.com/matrix-org/matrix-react-sdk/pull/4580) + * Add slash commands /query and /msg to match IRC + [\#4568](https://github.com/matrix-org/matrix-react-sdk/pull/4568) + * Send cross-signing debug booleans over rageshake + [\#4570](https://github.com/matrix-org/matrix-react-sdk/pull/4570) + * Prompt user to specify an alternate server if theirs has registration off + [\#4575](https://github.com/matrix-org/matrix-react-sdk/pull/4575) + * Don't try and redact redactions for "Remove recent messages" + [\#4573](https://github.com/matrix-org/matrix-react-sdk/pull/4573) + * View Source should target the replacing event rather than the root one + [\#4571](https://github.com/matrix-org/matrix-react-sdk/pull/4571) + * Fix passphrase reset in key backup restore dialog + [\#4569](https://github.com/matrix-org/matrix-react-sdk/pull/4569) + * Ensure key backup gets dealt with correctly during secret storage reset + [\#4556](https://github.com/matrix-org/matrix-react-sdk/pull/4556) + * Fix crash for broken invites + [\#4565](https://github.com/matrix-org/matrix-react-sdk/pull/4565) + * Fix rageshake with no matrix client + [\#4572](https://github.com/matrix-org/matrix-react-sdk/pull/4572) + * Update from Weblate + [\#4567](https://github.com/matrix-org/matrix-react-sdk/pull/4567) + * Bring back UnknownBody for UISIs + [\#4564](https://github.com/matrix-org/matrix-react-sdk/pull/4564) + * clear tag panel selection if the community selected is left + [\#4559](https://github.com/matrix-org/matrix-react-sdk/pull/4559) + * Close ImageView when redacting + [\#4560](https://github.com/matrix-org/matrix-react-sdk/pull/4560) + * Redesign redactions + [\#4484](https://github.com/matrix-org/matrix-react-sdk/pull/4484) + * Don't try to reload profile information when closing the user panel + [\#4547](https://github.com/matrix-org/matrix-react-sdk/pull/4547) + * Fix right panel hiding when viewing room member + [\#4558](https://github.com/matrix-org/matrix-react-sdk/pull/4558) + * Don't erase password confirm on registration error + [\#4540](https://github.com/matrix-org/matrix-react-sdk/pull/4540) + * Add a loading state for email addresses/phone numbers in settings + [\#4557](https://github.com/matrix-org/matrix-react-sdk/pull/4557) + * set the meta tag for theme-color to the same theme css background + [\#4554](https://github.com/matrix-org/matrix-react-sdk/pull/4554) + * Update Invite Dialog copy to include email addresses + [\#4497](https://github.com/matrix-org/matrix-react-sdk/pull/4497) + * Fix slider toggle regression. + [\#4546](https://github.com/matrix-org/matrix-react-sdk/pull/4546) + * Fix a crash where a name could unexpectedly be an empty list + [\#4552](https://github.com/matrix-org/matrix-react-sdk/pull/4552) + * Solves communities can be dragged from context menu + [\#4492](https://github.com/matrix-org/matrix-react-sdk/pull/4492) + * Remove prefixes for composer avatar urls + [\#4553](https://github.com/matrix-org/matrix-react-sdk/pull/4553) + * Fix reply RR spacing getting doubled + [\#4541](https://github.com/matrix-org/matrix-react-sdk/pull/4541) + * Differentiate copy for own untrusted device dialog + [\#4549](https://github.com/matrix-org/matrix-react-sdk/pull/4549) + * EventIndex: Reduce the logging the event index is producing. + [\#4548](https://github.com/matrix-org/matrix-react-sdk/pull/4548) + * Increase rageshake size limit to 5mb + [\#4543](https://github.com/matrix-org/matrix-react-sdk/pull/4543) + * Update from Weblate + [\#4542](https://github.com/matrix-org/matrix-react-sdk/pull/4542) + * Guard against race when waiting for cross-signing to be ready + [\#4539](https://github.com/matrix-org/matrix-react-sdk/pull/4539) + * Wait for user to be verified in e2e setup + [\#4537](https://github.com/matrix-org/matrix-react-sdk/pull/4537) + * Convert MatrixChat to a TypeScript class + [\#4462](https://github.com/matrix-org/matrix-react-sdk/pull/4462) + * Mark room as read when escape is pressed + [\#4271](https://github.com/matrix-org/matrix-react-sdk/pull/4271) + * Only show key backup reminder when confirmed by server to be missing + [\#4534](https://github.com/matrix-org/matrix-react-sdk/pull/4534) + * Add device name to unverified session toast + [\#4535](https://github.com/matrix-org/matrix-react-sdk/pull/4535) + * Show progress when loading keys + [\#4507](https://github.com/matrix-org/matrix-react-sdk/pull/4507) + * Fix device verification toasts not disappearing + [\#4532](https://github.com/matrix-org/matrix-react-sdk/pull/4532) + * Update toast copy again + [\#4529](https://github.com/matrix-org/matrix-react-sdk/pull/4529) + * Re-apply theme after login + [\#4518](https://github.com/matrix-org/matrix-react-sdk/pull/4518) + * Reduce maximum width of toasts & allow multiple lines + [\#4525](https://github.com/matrix-org/matrix-react-sdk/pull/4525) + * Treat sessions that are there when we log in as old + [\#4524](https://github.com/matrix-org/matrix-react-sdk/pull/4524) + * Allow resetting storage from the access dialog + [\#4521](https://github.com/matrix-org/matrix-react-sdk/pull/4521) + * Update (bulk) unverified device toast copy + [\#4522](https://github.com/matrix-org/matrix-react-sdk/pull/4522) + * Make new device toasts appear above review toasts + [\#4519](https://github.com/matrix-org/matrix-react-sdk/pull/4519) + * Separate toasts for existing & new device verification + [\#4511](https://github.com/matrix-org/matrix-react-sdk/pull/4511) + * Slightly darker toggle off bg color + [\#4477](https://github.com/matrix-org/matrix-react-sdk/pull/4477) + * Fix pill vertical align + [\#4514](https://github.com/matrix-org/matrix-react-sdk/pull/4514) + * Fix set up encryption toast to use "set up" as action + [\#4502](https://github.com/matrix-org/matrix-react-sdk/pull/4502) + * Don't enable e2ee when inviting a 3pid + [\#4509](https://github.com/matrix-org/matrix-react-sdk/pull/4509) + * Fix internal link styling in Security Settings + [\#4510](https://github.com/matrix-org/matrix-react-sdk/pull/4510) + * Small custom theming fixes + [\#4508](https://github.com/matrix-org/matrix-react-sdk/pull/4508) + * Fix scaling issues + [\#4355](https://github.com/matrix-org/matrix-react-sdk/pull/4355) + * Aggregate device verify toasts + [\#4506](https://github.com/matrix-org/matrix-react-sdk/pull/4506) + * Support setting username and avatar colors in custom themes + [\#4503](https://github.com/matrix-org/matrix-react-sdk/pull/4503) + * only clear on continuations where the clear isn't done by SenderProfile + [\#4501](https://github.com/matrix-org/matrix-react-sdk/pull/4501) + * cap width of editable item list item to leave space for its X button + [\#4495](https://github.com/matrix-org/matrix-react-sdk/pull/4495) + * Add a link from settings / devices to your user profile + [\#4498](https://github.com/matrix-org/matrix-react-sdk/pull/4498) + * Update from Weblate + [\#4496](https://github.com/matrix-org/matrix-react-sdk/pull/4496) + * Make icon change in SetupEncryptionDialog + [\#4485](https://github.com/matrix-org/matrix-react-sdk/pull/4485) + * Remove invite only padlocks feature flag + [\#4487](https://github.com/matrix-org/matrix-react-sdk/pull/4487) + * Fix incorrect toast if security setup skipped + [\#4486](https://github.com/matrix-org/matrix-react-sdk/pull/4486) + * Revert "Update emojibase for fixed emoji codepoints and Emoji 13 support" + [\#4482](https://github.com/matrix-org/matrix-react-sdk/pull/4482) + * Fix widget URL templating (again) + [\#4481](https://github.com/matrix-org/matrix-react-sdk/pull/4481) + * Fix recovery link on login verification flow + [\#4479](https://github.com/matrix-org/matrix-react-sdk/pull/4479) + * Make avatars in pills occupy the entire space using cropping + [\#4476](https://github.com/matrix-org/matrix-react-sdk/pull/4476) + * Use WidgetType more often to avoid breaking new sticker pickers + [\#4458](https://github.com/matrix-org/matrix-react-sdk/pull/4458) + * Update logging for unmanaged widgets, and add TODO comments for other areas + [\#4460](https://github.com/matrix-org/matrix-react-sdk/pull/4460) + * Fix OpenID requests from widgets + [\#4459](https://github.com/matrix-org/matrix-react-sdk/pull/4459) + * Take encrypted message search out of labs + [\#4467](https://github.com/matrix-org/matrix-react-sdk/pull/4467) + * Fix BigEmoji for replies + [\#4475](https://github.com/matrix-org/matrix-react-sdk/pull/4475) + * Update login security copy and design to match Figma + [\#4472](https://github.com/matrix-org/matrix-react-sdk/pull/4472) + * Fix i18n of SSO UIA copy in Deactivate Account Dialog + [\#4471](https://github.com/matrix-org/matrix-react-sdk/pull/4471) + * Assert type of domNode as HTMLElement to fix build + [\#4470](https://github.com/matrix-org/matrix-react-sdk/pull/4470) + * Unignored in settings + [\#4466](https://github.com/matrix-org/matrix-react-sdk/pull/4466) + * Skip auth flow test for signing upload when password present + [\#4464](https://github.com/matrix-org/matrix-react-sdk/pull/4464) + * If user cannot set email during registration don't tell them to + [\#4461](https://github.com/matrix-org/matrix-react-sdk/pull/4461) + * Fix post-ts autocomplete, it is not null + [\#4463](https://github.com/matrix-org/matrix-react-sdk/pull/4463) + * Convert autocomplete stuff to TypeScript + [\#4452](https://github.com/matrix-org/matrix-react-sdk/pull/4452) + * Add a back button to the devtools verifications panel + [\#4455](https://github.com/matrix-org/matrix-react-sdk/pull/4455) + * Fix: wait until cross-signing keys are fetched to show verify button + [\#4456](https://github.com/matrix-org/matrix-react-sdk/pull/4456) + * Handle load error in create secret storage dialog + [\#4451](https://github.com/matrix-org/matrix-react-sdk/pull/4451) + * Allow iframes and Jitsi URLs in /addwidget + [\#4382](https://github.com/matrix-org/matrix-react-sdk/pull/4382) + * Support m.jitsi-typed widgets as Jitsi widgets + [\#4379](https://github.com/matrix-org/matrix-react-sdk/pull/4379) + * Don't recheck DeviceListener until after initial sync is finished + [\#4450](https://github.com/matrix-org/matrix-react-sdk/pull/4450) + * Fix CSS class in ButtonPlaceholder + [\#4449](https://github.com/matrix-org/matrix-react-sdk/pull/4449) + * Password Login make sure tab takes user to password field + [\#4441](https://github.com/matrix-org/matrix-react-sdk/pull/4441) + * Network Dropdown fix things not scrolling properly + [\#4439](https://github.com/matrix-org/matrix-react-sdk/pull/4439) + * ImageView make clicking off it easier + [\#4448](https://github.com/matrix-org/matrix-react-sdk/pull/4448) + * Add slash command to send a rageshake + [\#4443](https://github.com/matrix-org/matrix-react-sdk/pull/4443) + * EventIndex: Filter out events that don't have a propper content value. + [\#4446](https://github.com/matrix-org/matrix-react-sdk/pull/4446) + * Revert "Fix Filepanel scroll position state lost when room is changed" + [\#4445](https://github.com/matrix-org/matrix-react-sdk/pull/4445) + * Update seshat copy to remove trailing full stop + [\#4442](https://github.com/matrix-org/matrix-react-sdk/pull/4442) + * Fix Filepanel scroll position state lost when room is changed + [\#4388](https://github.com/matrix-org/matrix-react-sdk/pull/4388) + * Fix end-to-end tests for end-to-end encryption verification + [\#4436](https://github.com/matrix-org/matrix-react-sdk/pull/4436) + * Don't explode if the e2e test directory exists when crashing + [\#4437](https://github.com/matrix-org/matrix-react-sdk/pull/4437) + * Bump https-proxy-agent from 2.2.1 to 2.2.4 in /test/end-to-end-tests + [\#4430](https://github.com/matrix-org/matrix-react-sdk/pull/4430) + * Minor updates to e2e test instructions on Windows + [\#4432](https://github.com/matrix-org/matrix-react-sdk/pull/4432) + * Fix typo + [\#4435](https://github.com/matrix-org/matrix-react-sdk/pull/4435) + * Catch errors sooner so users can recover more easily + [\#4122](https://github.com/matrix-org/matrix-react-sdk/pull/4122) + * Rageshake: remind user of unsupported browser and send modernizr report + [\#4381](https://github.com/matrix-org/matrix-react-sdk/pull/4381) + * Design tweaks for DM Room Tiles + [\#4338](https://github.com/matrix-org/matrix-react-sdk/pull/4338) + * Don't break spills over multiple lines, ellipsis them at max-1-line + [\#4434](https://github.com/matrix-org/matrix-react-sdk/pull/4434) + * Turn the end-to-end tests back on and fix the lazy-loading tests + [\#4433](https://github.com/matrix-org/matrix-react-sdk/pull/4433) + * Fix key backup debug panel + [\#4431](https://github.com/matrix-org/matrix-react-sdk/pull/4431) + * Convert cross-signing feature flag to setting + [\#4416](https://github.com/matrix-org/matrix-react-sdk/pull/4416) + * Make RoomPublishSetting import-skinnable + [\#4428](https://github.com/matrix-org/matrix-react-sdk/pull/4428) + * Iterate cross-signing copy + [\#4425](https://github.com/matrix-org/matrix-react-sdk/pull/4425) + * Fix: ensure twemoji font is loaded when showing SAS emojis + [\#4422](https://github.com/matrix-org/matrix-react-sdk/pull/4422) + * Revert "Fix: load Twemoji before login so complete security gets the right + emojis during SAS" + [\#4421](https://github.com/matrix-org/matrix-react-sdk/pull/4421) + * Fix: load Twemoji before login so complete security gets the right emojis + during SAS + [\#4419](https://github.com/matrix-org/matrix-react-sdk/pull/4419) + * consolidate and fix copy to clipboard + [\#4410](https://github.com/matrix-org/matrix-react-sdk/pull/4410) + * Fix Message Context Menu options not displaying: block + [\#4418](https://github.com/matrix-org/matrix-react-sdk/pull/4418) + * Fix pills being broken by unescaped characters + [\#4411](https://github.com/matrix-org/matrix-react-sdk/pull/4411) + Changes in [2.5.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.5.0) (2020-05-05) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.5.0-rc.6...v2.5.0) diff --git a/package.json b/package.json index 92d228a812..7c008d5ccc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.5.0", + "version": "2.6.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -87,7 +87,6 @@ "project-name-generator": "^2.1.7", "prop-types": "^15.5.8", "qrcode": "^1.4.4", - "qrcode-react": "^0.1.16", "qs": "^6.6.0", "react": "^16.9.0", "react-beautiful-dnd": "^4.0.1", @@ -118,8 +117,11 @@ "@babel/register": "^7.7.4", "@peculiar/webcrypto": "^1.0.22", "@types/classnames": "^2.2.10", + "@types/flux": "^3.1.9", "@types/modernizr": "^3.5.3", + "@types/qrcode": "^1.3.4", "@types/react": "16.9", + "@types/zxcvbn": "^4.4.0", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", "chokidar": "^3.3.1", diff --git a/res/css/_components.scss b/res/css/_components.scss index 744e940a57..5cbcd32e9e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -41,6 +41,7 @@ @import "./views/auth/_CountryDropdown.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_LanguageSelector.scss"; +@import "./views/auth/_PassphraseField.scss"; @import "./views/auth/_ServerConfig.scss"; @import "./views/auth/_ServerTypeSelector.scss"; @import "./views/auth/_Welcome.scss"; @@ -108,6 +109,7 @@ @import "./views/elements/_ManageIntegsButton.scss"; @import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; +@import "./views/elements/_QRCode.scss"; @import "./views/elements/_ReplyThread.scss"; @import "./views/elements/_ResizeHandle.scss"; @import "./views/elements/_RichText.scss"; diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 4b2d6b1bf1..120da4c4f1 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -146,27 +146,3 @@ limitations under the License. .mx_AuthBody_spinner { margin: 1em 0; } - -.mx_AuthBody_passwordScore { - width: 100%; - appearance: none; - height: 4px; - border: 0; - border-radius: 2px; - position: absolute; - top: -12px; - - &::-moz-progress-bar { - border-radius: 2px; - background-color: $accent-color; - } - - &::-webkit-progress-bar, - &::-webkit-progress-value { - border-radius: 2px; - } - - &::-webkit-progress-value { - background-color: $accent-color; - } -} diff --git a/res/css/views/auth/_PassphraseField.scss b/res/css/views/auth/_PassphraseField.scss new file mode 100644 index 0000000000..d1b8c47d00 --- /dev/null +++ b/res/css/views/auth/_PassphraseField.scss @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +$PassphraseStrengthHigh: $accent-color; +$PassphraseStrengthMedium: $username-variant5-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 { + appearance: none; + width: 100%; + border: 0; + height: 4px; + position: absolute; + top: -12px; + + border-radius: 2px; + &::-moz-progress-bar { + border-radius: 2px; + } + &::-webkit-progress-bar, + &::-webkit-progress-value { + border-radius: 2px; + } + + @mixin ProgressBarColour $PassphraseStrengthLow; + &[value="2"], &[value="3"] { + @mixin ProgressBarColour $PassphraseStrengthMedium; + } + &[value="4"] { + @mixin ProgressBarColour $PassphraseStrengthHigh; + } +} diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index 9a2f67dea3..e08469ec6d 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -64,9 +64,6 @@ limitations under the License. .mx_ShareDialog_qrcode_container { float: left; - background-color: #ffffff; - padding: 5px; // makes qr code more readable in dark theme - border-radius: 5px; height: 256px; width: 256px; margin-right: 64px; diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index b9babd05f5..9be98e25b2 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -35,17 +35,6 @@ limitations under the License. align-items: flex-start; } -.mx_CreateKeyBackupDialog_passPhraseHelp { - flex: 1; - height: 85px; - margin-left: 20px; - font-size: 80%; -} - -.mx_CreateKeyBackupDialog_passPhraseHelp progress { - width: 100%; -} - .mx_CreateKeyBackupDialog_passPhraseInput { flex: none; width: 250px; diff --git a/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss index a9ebd54b31..63e5a3de09 100644 --- a/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss +++ b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss @@ -68,17 +68,6 @@ limitations under the License. margin-top: 0px; } -.mx_CreateSecretStorageDialog_passPhraseHelp { - flex: 1; - height: 64px; - margin-left: 20px; - font-size: 80%; -} - -.mx_CreateSecretStorageDialog_passPhraseHelp progress { - width: 100%; -} - .mx_CreateSecretStorageDialog_passPhraseMatch { width: 200px; margin-left: 20px; diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 983ef074f2..0a4ed2a194 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -37,7 +37,7 @@ limitations under the License. order: 2; /* min-width hack needed for FF */ min-width: 0px; - max-height: 90%; + height: 90%; flex: 15 15 0; display: flex; align-items: center; diff --git a/res/css/views/elements/_QRCode.scss b/res/css/views/elements/_QRCode.scss new file mode 100644 index 0000000000..96d9114b54 --- /dev/null +++ b/res/css/views/elements/_QRCode.scss @@ -0,0 +1,21 @@ +/* +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. +*/ + +.mx_QRCode { + img { + border-radius: 8px; + } +} diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 7214031586..e3cbc4dcf0 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -20,7 +20,7 @@ limitations under the License. */ import {MatrixClient} from "matrix-js-sdk"; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; /** @@ -167,13 +167,9 @@ export default class BasePlatform { setLanguage(preferredLangs: string[]) {} - getSSOCallbackUrl(hsUrl: string, isUrl: string): URL { + getSSOCallbackUrl(hsUrl: string, isUrl: string, fragmentAfterLogin: string): URL { const url = new URL(window.location.href); - // XXX: at this point, the fragment will always be #/login, which is no - // use to anyone. Ideally, we would get the intended fragment from - // MatrixChat.screenAfterLogin so that you could follow #/room links etc - // through an SSO login. - url.hash = ""; + url.hash = fragmentAfterLogin || ""; url.searchParams.set("homeserver", hsUrl); url.searchParams.set("identityServer", isUrl); return url; @@ -183,9 +179,11 @@ export default class BasePlatform { * Begin Single Sign On flows. * @param {MatrixClient} mxClient the matrix client using which we should start the flow * @param {"sso"|"cas"} loginType the type of SSO it is, CAS/SSO. + * @param {string} fragmentAfterLogin the hash to pass to the app during sso callback. */ - startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") { - const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl()); + startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { + const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl(), + fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } diff --git a/src/CallHandler.js b/src/CallHandler.js index 2bfe10850a..c95ed16eb3 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -59,7 +59,7 @@ import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; import Matrix from 'matrix-js-sdk'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 34379c029b..4f5a1a1220 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -18,7 +18,7 @@ limitations under the License. 'use strict'; import extend from './extend'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import {MatrixClientPeg} from './MatrixClientPeg'; import * as sdk from './index'; import { _t } from './languageHandler'; diff --git a/src/DeviceListener.js b/src/DeviceListener.js index d2ba8219db..27caba971e 100644 --- a/src/DeviceListener.js +++ b/src/DeviceListener.js @@ -225,14 +225,6 @@ export default class DeviceListener { }); } } - } else if (await cli.secretStorageKeyNeedsUpgrade()) { - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Encryption upgrade available"), - icon: "verification_warning", - props: {kind: 'upgrade_ssss'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - }); } else { // cross-signing is ready, and we don't need to upgrade encryption ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js index c9793d40f7..102afa6bf1 100644 --- a/src/FromWidgetPostMessageApi.js +++ b/src/FromWidgetPostMessageApi.js @@ -17,7 +17,7 @@ limitations under the License. */ import URL from 'url'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import WidgetMessagingEndpoint from './WidgetMessagingEndpoint'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import {MatrixClientPeg} from "./MatrixClientPeg"; diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1baa6c8e0c..22c5d48317 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -26,7 +26,7 @@ import Analytics from './Analytics'; import Notifier from './Notifier'; import UserActivity from './UserActivity'; import Presence from './Presence'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import DMRoomMap from './utils/DMRoomMap'; import Modal from './Modal'; import * as sdk from './index'; diff --git a/src/Modal.js b/src/Modal.js index de441740f1..9b9f190d58 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import ReactDOM from 'react-dom'; import Analytics from './Analytics'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import {defer} from './utils/promise'; import AsyncWrapper from './AsyncWrapper'; diff --git a/src/Notifier.js b/src/Notifier.js index ec92840998..2ffa92452b 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -21,7 +21,7 @@ import PlatformPeg from './PlatformPeg'; import * as TextForEvent from './TextForEvent'; import Analytics from './Analytics'; import * as Avatar from './Avatar'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; diff --git a/src/Presence.js b/src/Presence.js index 2fc13a090b..42bca35f96 100644 --- a/src/Presence.js +++ b/src/Presence.js @@ -17,7 +17,7 @@ limitations under the License. */ import {MatrixClientPeg} from "./MatrixClientPeg"; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import Timer from './utils/Timer'; // Time in ms after that a user is considered as unavailable/away diff --git a/src/Registration.js b/src/Registration.js index ca162bac03..32c3d9cc35 100644 --- a/src/Registration.js +++ b/src/Registration.js @@ -20,7 +20,7 @@ limitations under the License. * registration code. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import Modal from './Modal'; import { _t } from './languageHandler'; diff --git a/src/Resend.js b/src/Resend.js index 6d6c18cf27..f5f24bffa5 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -16,7 +16,7 @@ limitations under the License. */ import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import { EventStatus } from 'matrix-js-sdk'; export default class Resend { diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 9731e42825..315c2d86f4 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -238,7 +238,7 @@ Example: import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixEvent } from 'matrix-js-sdk'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import RoomViewStore from './stores/RoomViewStore'; import { _t } from './languageHandler'; diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index fbb9e2eb0e..d81da80e8d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -21,7 +21,7 @@ limitations under the License. import * as React from 'react'; import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import {_t, _td} from './languageHandler'; import Modal from './Modal'; @@ -41,6 +41,8 @@ import { parseFragment as parseHtml } from "parse5"; import sendBugReport from "./rageshake/submit-rageshake"; import SdkConfig from "./SdkConfig"; import { ensureDMExists } from "./createRoom"; +import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; +import { Action } from "./dispatcher/actions"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -943,8 +945,10 @@ export const Commands = [ } const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId); - dis.dispatch({ - action: 'view_user', + dis.dispatch({ + action: Action.ViewUser, + // XXX: We should be using a real member object and not assuming what the + // receiver wants. member: member || {userId}, }); return success(); diff --git a/src/UserActivity.js b/src/UserActivity.js index 0d1b4d0cc0..0174aebaf5 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import Timer from './utils/Timer'; // important these are larger than the timeouts of timers diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index bcbf3d6810..f527ab4a14 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -34,6 +34,7 @@ export enum Categories { CALLS = "Calls", COMPOSER = "Composer", ROOM_LIST = "Room List", + ROOM = "Room", AUTOCOMPLETE = "Autocomplete", } @@ -142,6 +143,34 @@ const shortcuts: Record = { }, ], + [Categories.ROOM]: [ + { + keybinds: [{ + key: Key.PAGE_UP, + }, { + key: Key.PAGE_DOWN, + }], + description: _td("Scroll up/down in the timeline"), + }, { + keybinds: [{ + key: Key.ESCAPE, + }], + description: _td("Dismiss read marker and jump to bottom"), + }, { + keybinds: [{ + modifiers: [Modifiers.SHIFT], + key: Key.PAGE_UP, + }], + description: _td("Jump to oldest unread message"), + }, { + keybinds: [{ + modifiers: [CMD_OR_CTRL, Modifiers.SHIFT], + key: Key.U, + }], + description: _td("Upload a file"), + } + ], + [Categories.ROOM_LIST]: [ { keybinds: [{ @@ -181,13 +210,6 @@ const shortcuts: Record = { [Categories.NAVIGATION]: [ { - keybinds: [{ - key: Key.PAGE_UP, - }, { - key: Key.PAGE_DOWN, - }], - description: _td("Scroll up/down in the timeline"), - }, { keybinds: [{ modifiers: [Modifiers.ALT, Modifiers.SHIFT], key: Key.ARROW_UP, @@ -257,10 +279,11 @@ const shortcuts: Record = { const categoryOrder = [ Categories.COMPOSER, - Categories.CALLS, - Categories.ROOM_LIST, Categories.AUTOCOMPLETE, + Categories.ROOM, + Categories.ROOM_LIST, Categories.NAVIGATION, + Categories.CALLS, ]; interface IModal { diff --git a/src/actions/GroupActions.js b/src/actions/GroupActions.js deleted file mode 100644 index 006c2da5b8..0000000000 --- a/src/actions/GroupActions.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2017 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { asyncAction } from './actionCreators'; - -const GroupActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to fetch - * the groups to which a user is joined. - * - * @param {MatrixClient} matrixClient the matrix client to query. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -GroupActions.fetchJoinedGroups = function(matrixClient) { - return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups()); -}; - -export default GroupActions; diff --git a/src/actions/GroupActions.ts b/src/actions/GroupActions.ts new file mode 100644 index 0000000000..81470d1221 --- /dev/null +++ b/src/actions/GroupActions.ts @@ -0,0 +1,34 @@ +/* +Copyright 2017 New Vector Ltd +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 { asyncAction } from './actionCreators'; +import { AsyncActionPayload } from "../dispatcher/payloads"; +import { MatrixClient } from "matrix-js-sdk/src/client"; + +export default class GroupActions { + /** + * Creates an action thunk that will do an asynchronous request to fetch + * the groups to which a user is joined. + * + * @param {MatrixClient} matrixClient the matrix client to query. + * @returns {AsyncActionPayload} An async action payload. + * @see asyncAction + */ + public static fetchJoinedGroups(matrixClient: MatrixClient): AsyncActionPayload { + return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups(), null); + } +} diff --git a/src/actions/MatrixActionCreators.js b/src/actions/MatrixActionCreators.js index c89ec44435..93a4fcf07c 100644 --- a/src/actions/MatrixActionCreators.js +++ b/src/actions/MatrixActionCreators.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; // TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events // become dispatches in the same place. diff --git a/src/actions/RoomListActions.js b/src/actions/RoomListActions.js deleted file mode 100644 index 10a3848dda..0000000000 --- a/src/actions/RoomListActions.js +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { asyncAction } from './actionCreators'; -import RoomListStore, {TAG_DM} from '../stores/RoomListStore'; -import Modal from '../Modal'; -import * as Rooms from '../Rooms'; -import { _t } from '../languageHandler'; -import * as sdk from '../index'; - -const RoomListActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to - * tag room. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {Room} room the room to tag. - * @param {string} oldTag the tag to remove (unless oldTag ==== newTag) - * @param {string} newTag the tag with which to tag the room. - * @param {?number} oldIndex the previous position of the room in the - * list of rooms. - * @param {?number} newIndex the new position of the room in the list - * of rooms. - * @returns {function} an action thunk. - * @see asyncAction - */ -RoomListActions.tagRoom = function(matrixClient, room, oldTag, newTag, oldIndex, newIndex) { - let metaData = null; - - // Is the tag ordered manually? - if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - const lists = RoomListStore.getRoomLists(); - const newList = [...lists[newTag]]; - - newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); - - // If the room was moved "down" (increasing index) in the same list we - // need to use the orders of the tiles with indices shifted by +1 - const offset = ( - newTag === oldTag && oldIndex < newIndex - ) ? 1 : 0; - - const indexBefore = offset + newIndex - 1; - const indexAfter = offset + newIndex; - - const prevOrder = indexBefore <= 0 ? - 0 : newList[indexBefore].tags[newTag].order; - const nextOrder = indexAfter >= newList.length ? - 1 : newList[indexAfter].tags[newTag].order; - - metaData = { - order: (prevOrder + nextOrder) / 2.0, - }; - } - - return asyncAction('RoomListActions.tagRoom', () => { - const promises = []; - const roomId = room.roomId; - - // Evil hack to get DMs behaving - if ((oldTag === undefined && newTag === TAG_DM) || - (oldTag === TAG_DM && newTag === undefined) - ) { - return Rooms.guessAndSetDMRoom( - room, newTag === TAG_DM, - ).catch((err) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to set direct chat tag " + err); - Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { - title: _t('Failed to set direct chat tag'), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - } - - const hasChangedSubLists = oldTag !== newTag; - - // More evilness: We will still be dealing with moving to favourites/low prio, - // but we avoid ever doing a request with TAG_DM. - // - // if we moved lists, remove the old tag - if (oldTag && oldTag !== TAG_DM && - hasChangedSubLists - ) { - const promiseToDelete = matrixClient.deleteRoomTag( - roomId, oldTag, - ).catch(function(err) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to remove tag " + oldTag + " from room: " + err); - Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { - title: _t('Failed to remove tag %(tagName)s from room', {tagName: oldTag}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - - promises.push(promiseToDelete); - } - - // if we moved lists or the ordering changed, add the new tag - if (newTag && newTag !== TAG_DM && - (hasChangedSubLists || metaData) - ) { - // metaData is the body of the PUT to set the tag, so it must - // at least be an empty object. - metaData = metaData || {}; - - const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to add tag " + newTag + " to room: " + err); - Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { - title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - - throw err; - }); - - promises.push(promiseToAdd); - } - - return Promise.all(promises); - }, () => { - // For an optimistic update - return { - room, oldTag, newTag, metaData, - }; - }); -}; - -export default RoomListActions; diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts new file mode 100644 index 0000000000..eb9831ec47 --- /dev/null +++ b/src/actions/RoomListActions.ts @@ -0,0 +1,151 @@ +/* +Copyright 2018 New Vector Ltd +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 { asyncAction } from './actionCreators'; +import RoomListStore, { TAG_DM } from '../stores/RoomListStore'; +import Modal from '../Modal'; +import * as Rooms from '../Rooms'; +import { _t } from '../languageHandler'; +import * as sdk from '../index'; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { AsyncActionPayload } from "../dispatcher/payloads"; + +export default class RoomListActions { + /** + * Creates an action thunk that will do an asynchronous request to + * tag room. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {Room} room the room to tag. + * @param {string} oldTag the tag to remove (unless oldTag ==== newTag) + * @param {string} newTag the tag with which to tag the room. + * @param {?number} oldIndex the previous position of the room in the + * list of rooms. + * @param {?number} newIndex the new position of the room in the list + * of rooms. + * @returns {AsyncActionPayload} an async action payload + * @see asyncAction + */ + public static tagRoom( + matrixClient: MatrixClient, room: Room, + oldTag: string, newTag: string, + oldIndex: number | null, newIndex: number | null, + ): AsyncActionPayload { + let metaData = null; + + // Is the tag ordered manually? + if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { + const lists = RoomListStore.getRoomLists(); + const newList = [...lists[newTag]]; + + newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); + + // If the room was moved "down" (increasing index) in the same list we + // need to use the orders of the tiles with indices shifted by +1 + const offset = ( + newTag === oldTag && oldIndex < newIndex + ) ? 1 : 0; + + const indexBefore = offset + newIndex - 1; + const indexAfter = offset + newIndex; + + const prevOrder = indexBefore <= 0 ? + 0 : newList[indexBefore].tags[newTag].order; + const nextOrder = indexAfter >= newList.length ? + 1 : newList[indexAfter].tags[newTag].order; + + metaData = { + order: (prevOrder + nextOrder) / 2.0, + }; + } + + return asyncAction('RoomListActions.tagRoom', () => { + const promises = []; + const roomId = room.roomId; + + // Evil hack to get DMs behaving + if ((oldTag === undefined && newTag === TAG_DM) || + (oldTag === TAG_DM && newTag === undefined) + ) { + return Rooms.guessAndSetDMRoom( + room, newTag === TAG_DM, + ).catch((err) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to set direct chat tag " + err); + Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { + title: _t('Failed to set direct chat tag'), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + }); + } + + const hasChangedSubLists = oldTag !== newTag; + + // More evilness: We will still be dealing with moving to favourites/low prio, + // but we avoid ever doing a request with TAG_DM. + // + // if we moved lists, remove the old tag + if (oldTag && oldTag !== TAG_DM && + hasChangedSubLists + ) { + const promiseToDelete = matrixClient.deleteRoomTag( + roomId, oldTag, + ).catch(function (err) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to remove tag " + oldTag + " from room: " + err); + Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { + title: _t('Failed to remove tag %(tagName)s from room', {tagName: oldTag}), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + }); + + promises.push(promiseToDelete); + } + + // if we moved lists or the ordering changed, add the new tag + if (newTag && newTag !== TAG_DM && + (hasChangedSubLists || metaData) + ) { + // metaData is the body of the PUT to set the tag, so it must + // at least be an empty object. + metaData = metaData || {}; + + const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function (err) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to add tag " + newTag + " to room: " + err); + Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { + title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + + throw err; + }); + + promises.push(promiseToAdd); + } + + return Promise.all(promises); + }, () => { + // For an optimistic update + return { + room, oldTag, newTag, metaData, + }; + }); + } +} diff --git a/src/actions/TagOrderActions.js b/src/actions/TagOrderActions.js deleted file mode 100644 index a257ff16d8..0000000000 --- a/src/actions/TagOrderActions.js +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2017 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import Analytics from '../Analytics'; -import { asyncAction } from './actionCreators'; -import TagOrderStore from '../stores/TagOrderStore'; - -const TagOrderActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to - * move a tag in TagOrderStore to destinationIx. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {string} tag the tag to move. - * @param {number} destinationIx the new position of the tag. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -TagOrderActions.moveTag = function(matrixClient, tag, destinationIx) { - // Only commit tags if the state is ready, i.e. not null - let tags = TagOrderStore.getOrderedTags(); - let removedTags = TagOrderStore.getRemovedTagsAccountData() || []; - if (!tags) { - return; - } - - tags = tags.filter((t) => t !== tag); - tags = [...tags.slice(0, destinationIx), tag, ...tags.slice(destinationIx)]; - - removedTags = removedTags.filter((t) => t !== tag); - - const storeId = TagOrderStore.getStoreId(); - - return asyncAction('TagOrderActions.moveTag', () => { - Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); - return matrixClient.setAccountData( - 'im.vector.web.tag_ordering', - {tags, removedTags, _storeId: storeId}, - ); - }, () => { - // For an optimistic update - return {tags, removedTags}; - }); -}; - -/** - * Creates an action thunk that will do an asynchronous request to - * label a tag as removed in im.vector.web.tag_ordering account data. - * - * The reason this is implemented with new state `removedTags` is that - * we incrementally and initially populate `tags` with groups that - * have been joined. If we remove a group from `tags`, it will just - * get added (as it looks like a group we've recently joined). - * - * NB: If we ever support adding of tags (which is planned), we should - * take special care to remove the tag from `removedTags` when we add - * it. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {string} tag the tag to remove. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -TagOrderActions.removeTag = function(matrixClient, tag) { - // Don't change tags, just removedTags - const tags = TagOrderStore.getOrderedTags(); - const removedTags = TagOrderStore.getRemovedTagsAccountData() || []; - - if (removedTags.includes(tag)) { - // Return a thunk that doesn't do anything, we don't even need - // an asynchronous action here, the tag is already removed. - return () => {}; - } - - removedTags.push(tag); - - const storeId = TagOrderStore.getStoreId(); - - return asyncAction('TagOrderActions.removeTag', () => { - Analytics.trackEvent('TagOrderActions', 'removeTag'); - return matrixClient.setAccountData( - 'im.vector.web.tag_ordering', - {tags, removedTags, _storeId: storeId}, - ); - }, () => { - // For an optimistic update - return {removedTags}; - }); -}; - -export default TagOrderActions; diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts new file mode 100644 index 0000000000..bf1820d5d1 --- /dev/null +++ b/src/actions/TagOrderActions.ts @@ -0,0 +1,111 @@ +/* +Copyright 2017 New Vector Ltd +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 Analytics from '../Analytics'; +import { asyncAction } from './actionCreators'; +import TagOrderStore from '../stores/TagOrderStore'; +import { AsyncActionPayload } from "../dispatcher/payloads"; +import { MatrixClient } from "matrix-js-sdk/src/client"; + +export default class TagOrderActions { + + /** + * Creates an action thunk that will do an asynchronous request to + * move a tag in TagOrderStore to destinationIx. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {string} tag the tag to move. + * @param {number} destinationIx the new position of the tag. + * @returns {AsyncActionPayload} an async action payload that will + * dispatch actions indicating the status of the request. + * @see asyncAction + */ + public static moveTag(matrixClient: MatrixClient, tag: string, destinationIx: number): AsyncActionPayload { + // Only commit tags if the state is ready, i.e. not null + let tags = TagOrderStore.getOrderedTags(); + let removedTags = TagOrderStore.getRemovedTagsAccountData() || []; + if (!tags) { + return; + } + + tags = tags.filter((t) => t !== tag); + tags = [...tags.slice(0, destinationIx), tag, ...tags.slice(destinationIx)]; + + removedTags = removedTags.filter((t) => t !== tag); + + const storeId = TagOrderStore.getStoreId(); + + return asyncAction('TagOrderActions.moveTag', () => { + Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); + return matrixClient.setAccountData( + 'im.vector.web.tag_ordering', + {tags, removedTags, _storeId: storeId}, + ); + }, () => { + // For an optimistic update + return {tags, removedTags}; + }); + }; + + /** + * Creates an action thunk that will do an asynchronous request to + * label a tag as removed in im.vector.web.tag_ordering account data. + * + * The reason this is implemented with new state `removedTags` is that + * we incrementally and initially populate `tags` with groups that + * have been joined. If we remove a group from `tags`, it will just + * get added (as it looks like a group we've recently joined). + * + * NB: If we ever support adding of tags (which is planned), we should + * take special care to remove the tag from `removedTags` when we add + * it. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {string} tag the tag to remove. + * @returns {function} an async action payload that will dispatch + * actions indicating the status of the request. + * @see asyncAction + */ + public static removeTag(matrixClient: MatrixClient, tag: string): AsyncActionPayload { + // Don't change tags, just removedTags + const tags = TagOrderStore.getOrderedTags(); + const removedTags = TagOrderStore.getRemovedTagsAccountData() || []; + + if (removedTags.includes(tag)) { + // Return a thunk that doesn't do anything, we don't even need + // an asynchronous action here, the tag is already removed. + return new AsyncActionPayload(() => {}); + } + + removedTags.push(tag); + + const storeId = TagOrderStore.getStoreId(); + + return asyncAction('TagOrderActions.removeTag', () => { + Analytics.trackEvent('TagOrderActions', 'removeTag'); + return matrixClient.setAccountData( + 'im.vector.web.tag_ordering', + {tags, removedTags, _storeId: storeId}, + ); + }, () => { + // For an optimistic update + return {removedTags}; + }); + } +} diff --git a/src/actions/actionCreators.js b/src/actions/actionCreators.ts similarity index 76% rename from src/actions/actionCreators.js rename to src/actions/actionCreators.ts index 967ce609e7..c789e3cd07 100644 --- a/src/actions/actionCreators.js +++ b/src/actions/actionCreators.ts @@ -1,5 +1,6 @@ /* Copyright 2017 New Vector Ltd +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. @@ -14,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { AsyncActionPayload } from "../dispatcher/payloads"; + /** * Create an action thunk that will dispatch actions indicating the current * status of the Promise returned by fn. @@ -25,9 +28,9 @@ limitations under the License. * @param {function?} pendingFn a function that returns an object to assign * to the `request` key of the ${id}.pending * payload. - * @returns {function} an action thunk - a function that uses its single - * argument as a dispatch function to dispatch the - * following actions: + * @returns {AsyncActionPayload} an async action payload. Includes a function + * that uses its single argument as a dispatch function + * to dispatch the following actions: * `${id}.pending` and either * `${id}.success` or * `${id}.failure`. @@ -41,12 +44,11 @@ limitations under the License. * result is the result of the promise returned by * `fn`. */ -export function asyncAction(id, fn, pendingFn) { - return (dispatch) => { +export function asyncAction(id: string, fn: () => Promise, pendingFn: () => any | null): AsyncActionPayload { + const helper = (dispatch) => { dispatch({ action: id + '.pending', - request: - typeof pendingFn === 'function' ? pendingFn() : undefined, + request: typeof pendingFn === 'function' ? pendingFn() : undefined, }); fn().then((result) => { dispatch({action: id + '.success', result}); @@ -54,4 +56,5 @@ export function asyncAction(id, fn, pendingFn) { dispatch({action: id + '.failure', err}); }); }; + return new AsyncActionPayload(helper); } diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 120b086ef6..ec4b88f759 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -17,11 +17,12 @@ limitations under the License. import React from 'react'; import * as sdk from '../../../../index'; import PropTypes from 'prop-types'; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; +import {Action} from "../../../../dispatcher/actions"; /* * Allows the user to disable the Event Index. @@ -47,7 +48,7 @@ export default class DisableEventIndexDialog extends React.Component { await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } render() { diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index e4e39400f6..532b2f960f 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -15,17 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import FileSaver from 'file-saver'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import PropTypes from 'prop-types'; -import { scorePassword } from '../../../../utils/PasswordScorer'; -import { _t } from '../../../../languageHandler'; +import {_t, _td} from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; import SettingsStore from '../../../../settings/SettingsStore'; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import {copyNode} from "../../../../utils/strings"; +import PassphraseField from "../../../../components/views/auth/PassphraseField"; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -36,7 +36,6 @@ const PHASE_DONE = 5; const PHASE_OPTOUT_CONFIRM = 6; const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms. /* * Walks the user through the process of creating an e2e key backup @@ -52,17 +51,18 @@ export default class CreateKeyBackupDialog extends React.PureComponent { this._recoveryKeyNode = null; this._keyBackupInfo = null; - this._setZxcvbnResultTimeout = null; this.state = { secureSecretStorage: null, phase: PHASE_PASSPHRASE, passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', copied: false, downloaded: false, - zxcvbnResult: null, }; + + this._passphraseField = createRef(); } async componentDidMount() { @@ -81,12 +81,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent { } } - componentWillUnmount() { - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - } - _collectRecoveryKeyNode = (n) => { this._recoveryKeyNode = n; } @@ -180,22 +174,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onPassPhraseNextClick = async (e) => { e.preventDefault(); + if (!this._passphraseField.current) return; // unmounting - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + await this._passphraseField.current.validate({ allowEmpty: false }); + if (!this._passphraseField.current.state.valid) { + this._passphraseField.current.focus(); + this._passphraseField.current.validate({ allowEmpty: false, focused: true }); + return; } + + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); }; _onPassPhraseConfirmNextClick = async (e) => { @@ -214,9 +202,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onSetAgainClick = () => { this.setState({ passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', phase: PHASE_PASSPHRASE, - zxcvbnResult: null, }); } @@ -226,23 +214,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } + _onPassPhraseValidate = (result) => { + this.setState({ + passPhraseValid: result.valid, + }); + }; + _onPassPhraseChange = (e) => { this.setState({ passPhrase: e.target.value, }); - - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - this._setZxcvbnResultTimeout = setTimeout(() => { - this._setZxcvbnResultTimeout = null; - this.setState({ - // precompute this and keep it in state: zxcvbn is fast but - // we use it in a couple of different places so no point recomputing - // it unnecessarily. - zxcvbnResult: scorePassword(this.state.passPhrase), - }); - }, PASSPHRASE_FEEDBACK_DELAY); } _onPassPhraseConfirmChange = (e) => { @@ -251,35 +232,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } - _passPhraseIsValid() { - return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE; - } - _renderPhasePassPhrase() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - let strengthMeter; - let helpText; - if (this.state.zxcvbnResult) { - if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) { - helpText = _t("Great! This recovery passphrase looks strong enough."); - } else { - const suggestions = []; - for (let i = 0; i < this.state.zxcvbnResult.feedback.suggestions.length; ++i) { - suggestions.push(
{this.state.zxcvbnResult.feedback.suggestions[i]}
); - } - const suggestionBlock =
{suggestions.length > 0 ? suggestions : _t("Keep going...")}
; - - helpText =
- {this.state.zxcvbnResult.feedback.warning} - {suggestionBlock} -
; - } - strengthMeter =
- -
; - } - return

{_t( "Warning: You should only set up key backup from a trusted computer.", {}, @@ -293,17 +248,19 @@ export default class CreateKeyBackupDialog extends React.PureComponent {

- -
- {strengthMeter} - {helpText} -
@@ -311,7 +268,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { primaryButton={_t('Next')} onPrimaryButtonClick={this._onPassPhraseNextClick} hasCancel={false} - disabled={!this._passPhraseIsValid()} + disabled={!this.state.passPhraseValid} />
diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js index 9e2264a960..74552a5c08 100644 --- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js +++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js @@ -19,9 +19,10 @@ import React from "react"; import PropTypes from "prop-types"; import * as sdk from "../../../../index"; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; +import {Action} from "../../../../dispatcher/actions"; export default class NewRecoveryMethodDialog extends React.PureComponent { static propTypes = { @@ -36,7 +37,7 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { onGoToSettingsClick = () => { this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } onSetupClick = async () => { diff --git a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js index c5222dafd5..cda353e717 100644 --- a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js +++ b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js @@ -18,9 +18,10 @@ limitations under the License. import React from "react"; import PropTypes from "prop-types"; import * as sdk from "../../../../index"; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; +import {Action} from "../../../../dispatcher/actions"; export default class RecoveryMethodRemovedDialog extends React.PureComponent { static propTypes = { @@ -29,7 +30,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent { onGoToSettingsClick = () => { this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } onSetupClick = () => { diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index c24623e30e..12b71206d0 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -15,17 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; -import { scorePassword } from '../../../../utils/PasswordScorer'; import FileSaver from 'file-saver'; -import { _t } from '../../../../languageHandler'; +import {_t, _td} from '../../../../languageHandler'; import Modal from '../../../../Modal'; import { promptForBackupPassphrase } from '../../../../CrossSigningManager'; import {copyNode} from "../../../../utils/strings"; import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents"; +import PassphraseField from "../../../../components/views/auth/PassphraseField"; const PHASE_LOADING = 0; const PHASE_LOADERROR = 1; @@ -39,7 +39,6 @@ const PHASE_DONE = 8; const PHASE_CONFIRM_SKIP = 9; const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms. /* * Walks the user through the process of creating a passphrase to guard Secure @@ -62,16 +61,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._recoveryKey = null; this._recoveryKeyNode = null; - this._setZxcvbnResultTimeout = null; this._backupKey = null; this.state = { phase: PHASE_LOADING, passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', copied: false, downloaded: false, - zxcvbnResult: null, backupInfo: null, backupSigStatus: null, // does the server offer a UI auth flow with just m.login.password @@ -83,6 +81,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { useKeyBackup: true, }; + this._passphraseField = createRef(); + this._fetchBackupInfo(); if (this.state.accountPassword) { // If we have an account password in memory, let's simplify and @@ -99,9 +99,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { componentWillUnmount() { MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatusChange); - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } } async _fetchBackupInfo() { @@ -364,22 +361,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _onPassPhraseNextClick = async (e) => { e.preventDefault(); + if (!this._passphraseField.current) return; // unmounting - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + await this._passphraseField.current.validate({ allowEmpty: false }); + if (!this._passphraseField.current.state.valid) { + this._passphraseField.current.focus(); + this._passphraseField.current.validate({ allowEmpty: false, focused: true }); + return; } + + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); }; _onPassPhraseConfirmNextClick = async (e) => { @@ -399,9 +390,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _onSetAgainClick = () => { this.setState({ passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', phase: PHASE_PASSPHRASE, - zxcvbnResult: null, }); } @@ -411,23 +402,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } + _onPassPhraseValidate = (result) => { + this.setState({ + passPhraseValid: result.valid, + }); + }; + _onPassPhraseChange = (e) => { this.setState({ passPhrase: e.target.value, }); - - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - this._setZxcvbnResultTimeout = setTimeout(() => { - this._setZxcvbnResultTimeout = null; - this.setState({ - // precompute this and keep it in state: zxcvbn is fast but - // we use it in a couple of different places so no point recomputing - // it unnecessarily. - zxcvbnResult: scorePassword(this.state.passPhrase), - }); - }, PASSPHRASE_FEEDBACK_DELAY); } _onPassPhraseConfirmChange = (e) => { @@ -436,10 +420,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } - _passPhraseIsValid() { - return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE; - } - _onAccountPasswordChange = (e) => { this.setState({ accountPassword: e.target.value, @@ -502,37 +482,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _renderPhasePassPhrase() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const Field = sdk.getComponent('views.elements.Field'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const LabelledToggleSwitch = sdk.getComponent('views.elements.LabelledToggleSwitch'); - let strengthMeter; - let helpText; - if (this.state.zxcvbnResult) { - if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) { - helpText = _t("Great! This recovery passphrase looks strong enough."); - } else { - // We take the warning from zxcvbn or failing that, the first - // suggestion. In practice The first is generally the most relevant - // and it's probably better to present the user with one thing to - // improve about their password than a whole collection - it can - // spit out a warning and multiple suggestions which starts getting - // very information-dense. - const suggestion = ( - this.state.zxcvbnResult.feedback.warning || - this.state.zxcvbnResult.feedback.suggestions[0] - ); - const suggestionBlock =
{suggestion || _t("Keep going...")}
; - - helpText =
- {suggestionBlock} -
; - } - strengthMeter =
- -
; - } - return

{_t( "Set a recovery passphrase to secure encrypted information and recover it if you log out. " + @@ -540,19 +492,19 @@ export default class CreateSecretStorageDialog extends React.PureComponent { )}

- -
- {strengthMeter} - {helpText} -
{errorSection} diff --git a/src/components/views/settings/EnableNotificationsButton.js b/src/components/views/settings/EnableNotificationsButton.js index 9ca591f30e..e4b348dfbd 100644 --- a/src/components/views/settings/EnableNotificationsButton.js +++ b/src/components/views/settings/EnableNotificationsButton.js @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import createReactClass from 'create-react-class'; import Notifier from "../../../Notifier"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { _t } from '../../../languageHandler'; export default createReactClass({ diff --git a/src/components/views/settings/IntegrationManager.js b/src/components/views/settings/IntegrationManager.js index a5150e3777..fd6a62d73a 100644 --- a/src/components/views/settings/IntegrationManager.js +++ b/src/components/views/settings/IntegrationManager.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {Key} from "../../../Keyboard"; export default class IntegrationManager extends React.Component { diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js index cb37271452..23e72e2352 100644 --- a/src/components/views/settings/SetIdServer.js +++ b/src/components/views/settings/SetIdServer.js @@ -21,7 +21,7 @@ import {_t} from "../../../languageHandler"; import * as sdk from '../../../index'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import Modal from '../../../Modal'; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { getThreepidsWithBindStatus } from '../../../boundThreepids'; import IdentityAuthClient from "../../../IdentityAuthClient"; import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils"; diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js index 9ee9c8d130..f57d5d3798 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js @@ -21,7 +21,7 @@ import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; import Modal from "../../../../../Modal"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; export default class AdvancedRoomSettingsTab extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js index 99882ae400..1f12396413 100644 --- a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js @@ -20,7 +20,7 @@ import {_t} from "../../../../../languageHandler"; import RoomProfileSettings from "../../../room_settings/RoomProfileSettings"; import * as sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; export default class GeneralRoomSettingsTab extends React.Component { diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 867982ad2b..ddaa054c5e 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -32,7 +32,7 @@ import PlatformPeg from "../../../../../PlatformPeg"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; import Modal from "../../../../../Modal"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import {Service, startTermsFlow} from "../../../../../Terms"; import {SERVICE_TYPES} from "matrix-js-sdk"; import IdentityAuthClient from "../../../../../IdentityAuthClient"; @@ -348,7 +348,7 @@ export default class GeneralUserSettingsTab extends React.Component { // For newer homeservers with separate 3PID add and bind methods (MSC2290), // there is no such concern, so we can always show the HS account 3PIDs. if (this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true) { - const emails = this.state.loading3pids || true + const emails = this.state.loading3pids ? : { - const recheck = async () => { - const needsUpgrade = await MatrixClientPeg.get().secretStorageKeyNeedsUpgrade(); - if (!needsUpgrade) { - MatrixClientPeg.get().removeListener('accountData', recheck); - resolve(); - } - }; - MatrixClientPeg.get().on('accountData', recheck); - recheck(); - }); - } else { - return; - } - } - _onSetupClick = async () => { if (this.props.kind === "verify_this_session") { Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog, @@ -68,7 +48,6 @@ export default class SetupEncryptionToast extends React.PureComponent { ); try { await accessSecretStorage(); - await this._waitForCompletion(); } finally { modal.close(); } @@ -82,8 +61,6 @@ export default class SetupEncryptionToast extends React.PureComponent { return _t('Verify yourself & others to keep your chats safe'); case 'verify_this_session': return _t('Other users may not trust it'); - case 'upgrade_ssss': - return _t('Update your secure storage'); } } @@ -92,7 +69,6 @@ export default class SetupEncryptionToast extends React.PureComponent { case 'set_up_encryption': return _t('Set up'); case 'upgrade_encryption': - case 'upgrade_ssss': return _t('Upgrade'); case 'verify_this_session': return _t('Verify'); diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index 6447e87627..421dd7bea1 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import ToastStore from "../../../stores/ToastStore"; import Modal from "../../../Modal"; diff --git a/src/components/views/voip/CallPreview.js b/src/components/views/voip/CallPreview.js index 049dd8a3c6..c465170950 100644 --- a/src/components/views/voip/CallPreview.js +++ b/src/components/views/voip/CallPreview.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import RoomViewStore from '../../../stores/RoomViewStore'; import CallHandler from '../../../CallHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; export default createReactClass({ diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js index 4a5f3923e2..a0a566dfac 100644 --- a/src/components/views/voip/CallView.js +++ b/src/components/views/voip/CallView.js @@ -17,7 +17,7 @@ limitations under the License. import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import CallHandler from '../../../CallHandler'; import * as sdk from '../../../index'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js index 53e829b784..bf28fa0157 100644 --- a/src/components/views/voip/IncomingCallBox.js +++ b/src/components/views/voip/IncomingCallBox.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; diff --git a/src/components/views/voip/VideoView.js b/src/components/views/voip/VideoView.js index 51be6db81d..a51ab70da9 100644 --- a/src/components/views/voip/VideoView.js +++ b/src/components/views/voip/VideoView.js @@ -22,7 +22,7 @@ import createReactClass from 'create-react-class'; import classNames from 'classnames'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; diff --git a/src/createRoom.js b/src/createRoom.js index a39d2c2216..18fc787e1c 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -19,7 +19,7 @@ import {MatrixClientPeg} from './MatrixClientPeg'; import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import * as Rooms from "./Rooms"; import DMRoomMap from "./utils/DMRoomMap"; import {getAddressType} from "./UserAddress"; diff --git a/src/cryptodevices.js b/src/cryptodevices.js index f56a80e1e4..86b97364f9 100644 --- a/src/cryptodevices.js +++ b/src/cryptodevices.js @@ -16,7 +16,7 @@ limitations under the License. import Resend from './Resend'; import * as sdk from './index'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import Modal from './Modal'; import { _t } from './languageHandler'; diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts new file mode 100644 index 0000000000..a2f9c3efe3 --- /dev/null +++ b/src/dispatcher/actions.ts @@ -0,0 +1,42 @@ +/* +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. +*/ + +// Dispatcher actions also extend into any arbitrary string, so support that. +export type DispatcherAction = Action | string; + +export enum Action { + // TODO: Populate with actual actions + // This is lazily generated as it also includes fixing a bunch of references. Work + // that we don't really want to take on in a giant chunk. We should always define + // new actions here, and ideally when we touch existing ones we take some time to + // define them correctly. + + // When defining a new action, please use lower_scored_case with an optional class + // name prefix. For example, `RoomListStore.view_room` or `view_user_settings`. + // New definitions should also receive an accompanying interface in the payloads + // directory. + + /** + * View a user's profile. Should be used with a ViewUserPayload. + */ + ViewUser = "view_user", + + /** + * Open the user settings. No additional payload information required. + */ + ViewUserSettings = "view_user_settings", +} + diff --git a/src/dispatcher.js b/src/dispatcher/dispatcher.ts similarity index 50% rename from src/dispatcher.js rename to src/dispatcher/dispatcher.ts index 5dfaa11345..8330e5cd19 100644 --- a/src/dispatcher.js +++ b/src/dispatcher/dispatcher.ts @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 New Vector Ltd +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. @@ -15,25 +16,25 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; +import { Dispatcher } from "flux"; +import { Action } from "./actions"; +import { ActionPayload, AsyncActionPayload } from "./payloads"; -import flux from "flux"; - -class MatrixDispatcher extends flux.Dispatcher { +/** + * A dispatcher for ActionPayloads (the default within the SDK). + */ +export class MatrixDispatcher extends Dispatcher { /** - * @param {Object|function} payload Required. The payload to dispatch. - * If an Object, must contain at least an 'action' key. - * If a function, must have the signature (dispatch) => {...}. - * @param {boolean=} sync Optional. Pass true to dispatch + * Dispatches an event on the dispatcher's event bus. + * @param {ActionPayload} payload Required. The payload to dispatch. + * @param {boolean=false} sync Optional. Pass true to dispatch * synchronously. This is useful for anything triggering * an operation that the browser requires user interaction - * for. + * for. Default false (async). */ - dispatch(payload, sync) { - // Allow for asynchronous dispatching by accepting payloads that have the - // type `function (dispatch) {...}` - if (typeof payload === 'function') { - payload((action) => { + dispatch(payload: T, sync = false) { + if (payload instanceof AsyncActionPayload) { + payload.fn((action: ActionPayload) => { this.dispatch(action, sync); }); return; @@ -50,9 +51,24 @@ class MatrixDispatcher extends flux.Dispatcher { setTimeout(super.dispatch.bind(this, payload), 0); } } + + /** + * Shorthand for dispatch({action: Action.WHATEVER}, sync). No additional + * properties can be included with this version. + * @param {Action} action The action to dispatch. + * @param {boolean=false} sync Whether the dispatch should be sync or not. + * @see dispatch(action: ActionPayload, sync: boolean) + */ + fire(action: Action, sync = false) { + this.dispatch({action}, sync); + } } -if (global.mxDispatcher === undefined) { - global.mxDispatcher = new MatrixDispatcher(); +export const defaultDispatcher = new MatrixDispatcher(); + +const anyGlobal = global; +if (!anyGlobal.mxDispatcher) { + anyGlobal.mxDispatcher = defaultDispatcher; } -export default global.mxDispatcher; + +export default defaultDispatcher; diff --git a/src/dispatcher/payloads.ts b/src/dispatcher/payloads.ts new file mode 100644 index 0000000000..fa45b30623 --- /dev/null +++ b/src/dispatcher/payloads.ts @@ -0,0 +1,59 @@ +/* +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 { DispatcherAction } from "./actions"; + +/** + * The base dispatch type exposed by our dispatcher. + */ +export interface ActionPayload { + [property: string]: any; // effectively makes this 'extends Object' + action: DispatcherAction; +} + +/** + * The function the dispatcher calls when ready for an AsyncActionPayload. The + * single argument is used to start a dispatch. First the dispatcher calls the + * outer function, then when the called function is ready it calls the cb + * function to issue the dispatch. It may call the callback repeatedly if needed. + */ +export type AsyncActionFn = (cb: (action: ActionPayload) => void) => void; + +/** + * An async version of ActionPayload + */ +export class AsyncActionPayload implements ActionPayload { + /** + * The function the dispatcher should call. + */ + public readonly fn: AsyncActionFn; + + /** + * @deprecated Not used on AsyncActionPayload. + */ + public get action(): DispatcherAction { + return "NOT_USED"; + } + + /** + * Create a new AsyncActionPayload with the given ready function. + * @param {AsyncActionFn} readyFn The function to be called when the + * dispatcher is ready. + */ + public constructor(readyFn: AsyncActionFn) { + this.fn = readyFn; + } +} diff --git a/src/dispatcher/payloads/ViewUserPayload.ts b/src/dispatcher/payloads/ViewUserPayload.ts new file mode 100644 index 0000000000..ed602d4e24 --- /dev/null +++ b/src/dispatcher/payloads/ViewUserPayload.ts @@ -0,0 +1,29 @@ +/* +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 { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +export interface ViewUserPayload extends ActionPayload { + action: Action.ViewUser, + + /** + * The member to view. May be null or falsy to indicate that no member + * should be shown (hide whichever relevant components). + */ + member?: RoomMember; +} diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 4de5084106..ddd5e5521b 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -2237,5 +2237,62 @@ "Waiting for you to accept on your other session…": "Čekáme na vaše přijetí v druhé relaci…", "Almost there! Is your other session showing the same shield?": "Téměř hotovo! Je vaše druhá relace také ověřená?", "Almost there! Is %(displayName)s showing the same shield?": "Téměř hotovo! Je relace %(displayName)s také ověřená?", - "You've successfully verified %(deviceName)s (%(deviceId)s)!": "Ověřili jste %(deviceName)s (%(deviceId)s)!" + "You've successfully verified %(deviceName)s (%(deviceId)s)!": "Ověřili jste %(deviceName)s (%(deviceId)s)!", + "If you cancel now, you won't complete your operation.": "Pokud teď akci stornujete, nebudete jí moci dokončit.", + "Review where you’re logged in": "Zobrazit kde jste přihlášení", + "New login. Was this you?": "Nové přihlášní. Jste to vy?", + "%(name)s is requesting verification": "%(name)s žádá o ověření", + "Failed to set topic": "Nepovedlo se nastavit téma", + "Command failed": "Příkaz selhal", + "Could not find user in room": "Nepovedlo se najít uživatele v místnosti", + "Please supply a widget URL or embed code": "Zadejte prosím URL widgetu nebo jeho kód", + "Send a bug report with logs": "Zaslat hlášení o chybě", + "You signed in to a new session without verifying it:": "Přihlásili jste se do nové relace, ale neoveřili jste ji:", + "Verify your other session using one of the options below.": "Ověřte ostatní relací jedním z následujících způsobů.", + "Enable cross-signing to verify per-user instead of per-session": "Povolit cross-signing - ověřování uživatelů místo oveřování jednotlivých relací", + "Keep recovery passphrase in memory for this session": "V této relaci si heslo pro obnovení zálohy zapamatovat", + "Click the button below to confirm deleting these sessions.|other": "Zmáčknutím tlačítka potvrdíte smazání těchto relací.", + "Click the button below to confirm deleting these sessions.|one": "Zmáčknutím tlačítka potvrdíte smazání této relace.", + "Delete sessions|other": "Smazat relace", + "Delete sessions|one": "Smazat relaci", + "Where you’re logged in": "Kde jste přihlášení", + "You've successfully verified your device!": "Úspěšně jste ověřili vaše zařízení!", + "Start verification again from the notification.": "Začít proces ověření znovu pomocí notifikace.", + "Start verification again from their profile.": "Začít proces ověření znovu z jejich profilu.", + "Verification timed out.": "Ověření vypršelo.", + "You cancelled verification on your other session.": "Na druhé relace jste proces ověření zrušili.", + "%(displayName)s cancelled verification.": "%(displayName)s zrušil/a proces ověření.", + "You cancelled verification.": "Zrušili jste proces ověření.", + "Message deleted": "Zpráva smazána", + "Message deleted by %(name)s": "Zpráva smazána uživatelem %(name)s", + "Can't load this message": "Tuto zprávu nelze načíst", + "Submit logs": "Odeslat záznamy o chybě", + "Looks good": "To vypadá dobře", + "Can't find this server or its room list": "Server nebo jeho seznam místností se nepovedlo nalézt", + "All rooms": "Všechny místnosti", + "Your server": "Váš server", + "Are you sure you want to remove %(serverName)s": "Opravdu chcete odstranit %(serverName)s", + "Remove server": "Odstranit server", + "Matrix": "Matrix", + "Add a new server": "Přidat nový server", + "Enter the name of a new server you want to explore.": "Zadejte jméno serveru, který si chcete prohlédnout.", + "Server name": "Jméno serveru", + "Add a new server...": "Přidat nový server...", + "%(networkName)s rooms": "místnosti v %(networkName)s", + "Matrix rooms": "místnosti na Matrixu", + "Reminder: Your browser is unsupported, so your experience may be unpredictable.": "Připomínka: Váš prohlížeč není oficiálně podporován, tak se může Riot chovat nepředvídatelně.", + "Enable end-to-end encryption": "Povolit E2E šifrování", + "You can’t disable this later. Bridges & most bots won’t work yet.": "Už to v budoucnu nepůjde vypnout. Většina botů a propojení zatím nefunguje.", + "Server did not require any authentication": "Server nevyžadoval žádné ověření", + "Server did not return valid authentication information.": "Server neposkytl platné informace o ověření.", + "Confirm your account deactivation by using Single Sign On to prove your identity.": "Potvrďte deaktivaci účtu použtím Jednotného přihlášení.", + "Are you sure you want to deactivate your account? This is irreversible.": "Opravdu chcete deaktivovat účet? Je to nevratné.", + "Confirm account deactivation": "Potvrďte deaktivaci účtu", + "There was a problem communicating with the server. Please try again.": "Došlo k potížím při komunikaci se serverem. Zkuste to prosím znovu.", + "Start a conversation with someone using their name, username (like ) or email address.": "Začněte s někým konverzovat za pomocí jména, přihlašovacího jména (jako ) nebo emailu.", + "Opens chat with the given user": "Otevře konverzaci s tímto uživatelem", + "Sends a message to the given user": "Pošle zprávu danému uživateli", + "Waiting for your other session to verify…": "Čekáme na ověření od vaší druhé relace…", + "Verify all your sessions to ensure your account & messages are safe": "Ověřte všechny své relace, abyste zaručili, že jsou vaše zprávy a účet bezpečné", + "Verify the new login accessing your account: %(name)s": "Ověřte nové přihlášení na váš účet: %(name)s" } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 7fdaf20e40..1166e7710a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1794,7 +1794,7 @@ "Confirm adding this email address by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser E-Mail-Adresse mit „Single Sign-On“, um deine Identität nachzuweisen.", "Single Sign On": "Single Sign-On", "Confirm adding email": "Bestätige das Hinzfugen der Email-Addresse", - "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser Telefonnumer, indem du deine Identität mittels Single Sign-On nachweist.", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser Telefonnummer, indem du deine Identität mittels „Single Sign-On“ nachweist.", "Click the button below to confirm adding this phone number.": "Betätige unten die Schaltfläche um das Hinzufügen dieser Telefonnummer zu bestätigen.", "If you cancel now, you won't complete your operation.": "Wenn du jetzt abbrichst, wirst du deinen Vorgang nicht fertigstellen.", "%(name)s is requesting verification": "%(name)s fordert eine Verifizierung an", @@ -2365,5 +2365,15 @@ "Activate selected button": "Ausgewählten Button aktivieren", "Toggle right panel": "Rechtes Panel ein-/ausblenden", "Toggle this dialog": "Diesen Dialog ein-/ausblenden", - "Move autocomplete selection up/down": "Auto-Vervollständigung nach oben/unten verschieben" + "Move autocomplete selection up/down": "Auto-Vervollständigung nach oben/unten verschieben", + "Opens chat with the given user": "Öffnet einen Chat mit diesem Benutzer", + "Sends a message to the given user": "Sendet diesem Benutzer eine Nachricht", + "Waiting for your other session to verify…": "Warte auf die Verifikation deiner anderen Sitzungen…", + "You've successfully verified your device!": "Du hast dein Gerät erfolgreich verifiziert!", + "QR Code": "QR-Code", + "To continue, use Single Sign On to prove your identity.": "Zum Fortfahren, nutze Single Sign On um deine Identität zu bestätigen.", + "Confirm to continue": "Bestätige um fortzufahren", + "Click the button below to confirm your identity.": "Klicke den Button unten um deine Identität zu bestätigen.", + "Confirm encryption setup": "Bestätige die Einrichtung der Verschlüsselung", + "Click the button below to confirm setting up encryption.": "Klick die Schaltfläche unten um die Einstellungen der Verschlüsselung zu bestätigen." } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 32733bb023..dcb6326dab 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -571,7 +571,6 @@ "Review": "Review", "Verify yourself & others to keep your chats safe": "Verify yourself & others to keep your chats safe", "Other users may not trust it": "Other users may not trust it", - "Update your secure storage": "Update your secure storage", "Set up": "Set up", "Upgrade": "Upgrade", "Verify": "Verify", @@ -623,9 +622,6 @@ "in account data": "in account data", "Homeserver feature support:": "Homeserver feature support:", "exists": "exists", - "Secret Storage key format:": "Secret Storage key format:", - "outdated": "outdated", - "up to date": "up to date", "Your homeserver does not support session management.": "Your homeserver does not support session management.", "Unable to load session list": "Unable to load session list", "Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Confirm deleting these sessions by using Single Sign On to prove your identity.", @@ -1119,6 +1115,7 @@ "Direct Messages": "Direct Messages", "Start chat": "Start chat", "Rooms": "Rooms", + "Create room": "Create room", "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", @@ -1500,6 +1497,7 @@ "%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes", "Power level": "Power level", "Custom level": "Custom level", + "QR Code": "QR Code", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.", "In reply to ": "In reply to ", "Room alias": "Room alias", @@ -1591,13 +1589,13 @@ "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ", "Incompatible Database": "Incompatible Database", "Continue With Encryption Disabled": "Continue With Encryption Disabled", - "Server did not require any authentication": "Server did not require any authentication", - "Server did not return valid authentication information.": "Server did not return valid authentication information.", "Confirm your account deactivation by using Single Sign On to prove your identity.": "Confirm your account deactivation by using Single Sign On to prove your identity.", "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:", "There was a problem communicating with the server. Please try again.": "There was a problem communicating with the server. Please try again.", + "Server did not require any authentication": "Server did not require any authentication", + "Server did not return valid authentication information.": "Server did not return valid authentication information.", "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. This action is irreversible.": "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. This action is irreversible.", "Deactivating your account does not by default cause us to forget messages you have sent. If you would like us to forget your messages, please tick the box below.": "Deactivating your account does not by default cause us to forget messages you have sent. If you would like us to forget your messages, please tick the box below.", "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.": "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.", @@ -1894,6 +1892,10 @@ "Your Modular server": "Your Modular server", "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.", "Server Name": "Server Name", + "Enter password": "Enter password", + "Nice, strong password!": "Nice, strong password!", + "Password is allowed, but unsafe": "Password is allowed, but unsafe", + "Keep going...": "Keep going...", "The email field must not be blank.": "The email field must not be blank.", "The username field must not be blank.": "The username field must not be blank.", "The phone number field must not be blank.": "The phone number field must not be blank.", @@ -1908,10 +1910,6 @@ "Use an email address to recover your account": "Use an email address to recover your account", "Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)", "Doesn't look like a valid email address": "Doesn't look like a valid email address", - "Enter password": "Enter password", - "Password is allowed, but unsafe": "Password is allowed, but unsafe", - "Nice, strong password!": "Nice, strong password!", - "Keep going...": "Keep going...", "Passwords don't match": "Passwords don't match", "Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details", "Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)", @@ -2205,9 +2203,9 @@ "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.", "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.", - "Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.", "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 recovery passphrase": "Enter a recovery passphrase", + "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!", @@ -2235,7 +2233,6 @@ "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.", - "Enter a recovery passphrase...": "Enter a recovery passphrase...", "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).", @@ -2291,13 +2288,16 @@ "Cancel replying to a message": "Cancel replying to a message", "Toggle microphone mute": "Toggle microphone mute", "Toggle video on/off": "Toggle video on/off", + "Scroll up/down in the timeline": "Scroll up/down in the timeline", + "Dismiss read marker and jump to bottom": "Dismiss read marker and jump to bottom", + "Jump to oldest unread message": "Jump to oldest unread message", + "Upload a file": "Upload a file", "Jump to room search": "Jump to room search", "Navigate up/down in the room list": "Navigate up/down in the room list", "Select room from the room list": "Select room from the room list", "Collapse room list section": "Collapse room list section", "Expand room list section": "Expand room list section", "Clear room list filter field": "Clear room list filter field", - "Scroll up/down in the timeline": "Scroll up/down in the timeline", "Previous/next unread room or DM": "Previous/next unread room or DM", "Previous/next room or DM": "Previous/next room or DM", "Toggle the top left menu": "Toggle the top left menu", diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 5e63974d12..3f749ebe0f 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -70,7 +70,7 @@ "Unable to create widget.": "Ne povas krei fenestraĵon.", "Failed to send request.": "Malsukcesis sendi peton.", "This room is not recognised.": "Ĉi tiu ĉambro ne estas rekonita.", - "Power level must be positive integer.": "Nivelo de potenco devas esti entjero pozitiva.", + "Power level must be positive integer.": "Povnivelo devas esti entjero pozitiva.", "You are not in this room.": "Vi ne estas en tiu ĉi ĉambro.", "You do not have permission to do that in this room.": "Vi ne havas permeson fari tion en tiu ĉambro.", "Missing room_id in request": "En peto mankas room_id", @@ -122,7 +122,7 @@ "%(senderName)s made future room history visible to anyone.": "%(senderName)s videbligis estontan historion de la ĉambro al ĉiuj.", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s videbligis estontan historion de la ĉambro al nekonata (%(visibility)s).", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s al %(toPowerLevel)s", - "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ŝanĝis la potencan nivelon de %(powerLevelDiffText)s.", + "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ŝanĝis la povnivelon de %(powerLevelDiffText)s.", "%(senderName)s changed the pinned messages for the room.": "%(senderName)s ŝanĝis la fiksitajn mesaĝojn de la ĉambro.", "%(widgetName)s widget modified by %(senderName)s": "Fenestraĵon %(widgetName)s ŝanĝis %(senderName)s", "%(widgetName)s widget added by %(senderName)s": "Fenestraĵon %(widgetName)s aldonis %(senderName)s", @@ -172,12 +172,12 @@ "New passwords don't match": "Novaj pasvortoj ne akordas", "Passwords can't be empty": "Pasvortoj ne povas esti malplenaj", "Continue": "Daŭrigi", - "Export E2E room keys": "Elporti ĝiscele ĉifrajn ŝlosilojn de la ĉambro", + "Export E2E room keys": "Elporti tutvoje ĉifrajn ŝlosilojn de la ĉambro", "Do you want to set an email address?": "Ĉu vi volas agordi retpoŝtadreson?", "Current password": "Nuna pasvorto", "Password": "Pasvorto", "New Password": "Nova pasvorto", - "Confirm password": "Konfirmi pasvorton", + "Confirm password": "Konfirmu pasvorton", "Change Password": "Ŝanĝi pasvorton", "Authentication": "Aŭtentikigo", "Device ID": "Aparata identigilo", @@ -212,8 +212,8 @@ "Failed to ban user": "Malsukcesis forbari uzanton", "Failed to mute user": "Malsukcesis silentigi uzanton", "Failed to toggle moderator status": "Malsukcesis baskuligi estrecon", - "Failed to change power level": "Malsukcesis ŝanĝi nivelon de potenco", - "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Tiun ĉi ŝanĝon vi ne povos fareblos, ĉar vi donas al la uzanto la saman nivelon de potenco, kiun havas vi mem.", + "Failed to change power level": "Malsukcesis ŝanĝi povnivelon", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Tiun ĉi ŝanĝon vi ne povos malfari, ĉar vi donas al la uzanto la saman povnivelon, kiun havas vi mem.", "Are you sure?": "Ĉu vi certas?", "Unignore": "Reatenti", "Ignore": "Malatenti", @@ -233,7 +233,7 @@ "and %(count)s others...|one": "kaj unu alia…", "Invited": "Invititaj", "Filter room members": "Filtri ĉambranojn", - "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (potenco je %(powerLevelNumber)s)", + "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (povnivelo je %(powerLevelNumber)s)", "Attachment": "Aldonaĵo", "Hangup": "Fini vokon", "Voice call": "Voĉvoko", @@ -275,7 +275,7 @@ "Search": "Serĉi", "Community Invites": "Komunumaj invitoj", "Invites": "Invitoj", - "Favourites": "Ŝatataj", + "Favourites": "Elstarigitaj", "Rooms": "Ĉambroj", "Low priority": "Malpli gravaj", "Historical": "Estintaj", @@ -291,14 +291,14 @@ "Banned users": "Forbaritaj uzantoj", "This room is not accessible by remote Matrix servers": "Ĉi tiu ĉambro ne atingeblas por foraj serviloj de Matrix", "Leave room": "Eliri ĉambron", - "Favourite": "Ŝatata", + "Favourite": "Elstarigi", "Guests cannot join this room even if explicitly invited.": "Gastoj ne povas aliĝi ĉi tiun ĉambron eĉ kun malimplica invito.", "Click here to fix": "Klaku ĉi tie por riparo", "Who can access this room?": "Kiu povas aliri ĉi tiun ĉambron?", "Only people who have been invited": "Nur invititaj uzantoj", "Anyone who knows the room's link, apart from guests": "Iu ajn kun la ligilo, krom gastoj", "Anyone who knows the room's link, including guests": "Iu ajn kun la ligilo, inkluzive gastojn", - "Publish this room to the public in %(domain)s's room directory?": "Ĉu publikigi ĉi tiun ĉambron al la publika ĉambrujo de %(domain)s?", + "Publish this room to the public in %(domain)s's room directory?": "Ĉu publikigi ĉi tiun ĉambron al la publika listo de ĉambroj de %(domain)s?", "Who can read history?": "Kiu povas legi la historion?", "Anyone": "Iu ajn", "Members only (since the point in time of selecting this option)": "Nur ĉambranoj (ekde ĉi tiu elekto)", @@ -441,7 +441,7 @@ "collapse": "maletendi", "expand": "etendi", "Custom level": "Propra nivelo", - "Room directory": "Ĉambra dosierujo", + "Room directory": "Listo de ĉambroj", "Username not available": "Uzantonomo ne disponeblas", "Username invalid: %(errMessage)s": "Uzantonomo ne validas: %(errMessage)s", "Username available": "Uzantonomo disponeblas", @@ -489,7 +489,7 @@ "Skip": "Preterpasi", "An error occurred: %(error_string)s": "Okazis eraro: %(error_string)s", "This will be your account name on the homeserver, or you can pick a different server.": "Tio ĉi estos la nomo de via konto sur la hejmservilo , aŭ vi povas elekti alian servilon.", - "Blacklist": "Malpermesi legadon de ĉifritaj mesaĝoj", + "Blacklist": "Malpermesi malĉifradon", "Unverify": "Malkontroli", "If you already have a Matrix account you can log in instead.": "Se vi jam havas Matrix-konton, vi povas saluti anstataŭe.", "Private Chat": "Privata babilo", @@ -509,7 +509,7 @@ "Failed to remove the room from the summary of %(groupId)s": "Malsukcesis forigi la ĉambron de la superrigardo de %(groupId)s", "The room '%(roomName)s' could not be removed from the summary.": "Ĉambro ;%(roomName)s' ne forigeblas de la superrigardo.", "Add users to the community summary": "Aldoni uzantojn al la komunuma superrigardo", - "Who would you like to add to this summary?": "Kiun vi ŝatus aldoni al tiu ĉi superrigardo?", + "Who would you like to add to this summary?": "Kiun vi volas aldoni al tiu ĉi superrigardo?", "Failed to add the following users to the summary of %(groupId)s:": "Malsukcesis aldoni la jenajn uzantojn al la superrigardo de %(groupId)s:", "Add a User": "Aldoni uzanton", "Failed to remove a user from the summary of %(groupId)s": "Malsukcesis forigi uzanton de la superrigardo de %(groupId)s", @@ -538,8 +538,8 @@ "Are you sure you want to leave the room '%(roomName)s'?": "Ĉu vi certe volas forlasi la ĉambron '%(roomName)s'?", "Failed to leave room": "Malsukcesis forlasi la ĉambron", "Signed Out": "Adiaŭinta", - "Old cryptography data detected": "Malnovaj kriptografiaj datumoj troviĝis", - "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Datumoj el malnova versio de Riot troviĝis. Ĉi tio malfunkciigos ĝiscelan ĉifradon en la malnova versio. Ĝiscele ĉifritaj mesaĝoj interŝanĝitaj freŝtempe per la malnova versio eble ne malĉifreblos. Tio povas kaŭzi malsukceson ankaŭ al mesaĝoj interŝanĝitaj kun tiu ĉi versio. Se vin trafos problemoj, adiaŭu kaj resalutu. Por reteni mesaĝan historion, elportu kaj reenportu viajn ŝlosilojn.", + "Old cryptography data detected": "Malnovaj datumoj de ĉifroteĥnikaro troviĝis", + "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Datumoj el malnova versio de Riot troviĝis. Ĉi tio malfunkciigos tutvojan ĉifradon en la malnova versio. Tutvoje ĉifritaj mesaĝoj interŝanĝitaj freŝtempe per la malnova versio eble ne malĉifreblos. Tio povas kaŭzi malsukceson ankaŭ al mesaĝoj interŝanĝitaj kun tiu ĉi versio. Se vin trafos problemoj, adiaŭu kaj resalutu. Por reteni mesaĝan historion, elportu kaj reenportu viajn ŝlosilojn.", "Logout": "Adiaŭi", "Your Communities": "Viaj komunumoj", "Error whilst fetching joined communities": "Akirado de viaj komunumoj eraris", @@ -550,7 +550,7 @@ "Connectivity to the server has been lost.": "Konekto al la servilo perdiĝis.", "Sent messages will be stored until your connection has returned.": "Senditaj mesaĝoj konserviĝos ĝis via konekto refunkcios.", "Active call": "Aktiva voko", - "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Neniu alia ĉeestas! Ĉu vi ŝatus inviti aliajnĉesigi avertadon pri la malplena ĉambro?", + "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Neniu alia ĉeestas! Ĉu vi volas inviti aliajnĉesigi avertadon pri la malplena ĉambro?", "You seem to be uploading files, are you sure you want to quit?": "Ŝajne vi alŝutas dosierojn nun; ĉu vi tamen volas foriri?", "You seem to be in a call, are you sure you want to quit?": "Ŝajne vi vokas nun; ĉu vi tamen volas foriri?", "Search failed": "Serĉo malsukcesis", @@ -576,8 +576,8 @@ "Success": "Sukceso", "Unable to remove contact information": "Ne povas forigi kontaktajn informojn", "": "", - "Import E2E room keys": "Enporti ĝiscele ĉifrajn ĉambrajn ŝlosilojn", - "Cryptography": "Kriptografio", + "Import E2E room keys": "Enporti tutvoje ĉifrajn ĉambrajn ŝlosilojn", + "Cryptography": "Ĉifroteĥnikaro", "Analytics": "Analizo", "Riot collects anonymous analytics to allow us to improve the application.": "Riot kolektas sennomaj analizajn datumojn por helpi plibonigadon de la programo.", "Labs": "Eksperimentaj funkcioj", @@ -621,7 +621,7 @@ "This server does not support authentication with a phone number.": "Ĉi tiu servilo ne subtenas aŭtentikigon per telefona numero.", "Displays action": "Montras agon", "Bans user with given id": "Forbaras uzanton kun la donita identigilo", - "Define the power level of a user": "Difini la potencan nivelon de uzanto", + "Define the power level of a user": "Difini la povnivelon de uzanto", "Deops user with given id": "Senestrigas uzanton kun donita identigilo", "Invites user with given id to current room": "Invitas uzanton per identigilo al la nuna ĉambro", "Joins room with given alias": "Aliĝas al ĉambro per kromnomo", @@ -664,7 +664,7 @@ "File to import": "Enportota dosiero", "Import": "Enporti", "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Forigo de fenestraĵo efektiviĝos por ĉiuj uzantoj en ĉi tiu ĉambro. Ĉu vi certe volas ĝin forigi?", - "Unblacklist": "Repermesi legadon de ĉifritaj mesaĝoj", + "Unblacklist": "Repermesi malĉifradon", "none": "neniu", "The version of Riot.im": "Tiu ĉi versio de Riot.im", "Your language of choice": "Via preferata lingvo", @@ -732,7 +732,7 @@ "No update available.": "Neniuj ĝisdatigoj haveblas.", "Resend": "Resendi", "Collecting app version information": "Kolektante informon pri versio de la aplikaĵo", - "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Ĉu forigi la ĉambran kromnomon %(alias)s kaj forigi %(name)s de la ujo?", + "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Ĉu forigi la ĉambran kromnomon %(alias)s kaj forigi %(name)s de la listo de ĉambroj?", "Enable notifications for this account": "Ŝalti sciigojn por tiu ĉi konto", "Invite to this community": "Inviti al tiu ĉi komunumo", "Messages containing keywords": "Mesaĝoj enhavantaj ŝlosilovortojn", @@ -883,7 +883,7 @@ "Language and region": "Lingvo kaj regiono", "Theme": "Haŭto", "General": "Ĝenerala", - "In reply to ": "Respondante al ", + "In reply to ": "Responde al ", "Share Message": "Diskonigi", "Whether or not you're logged in (we don't record your username)": "Ĉu vi salutis aŭ ne (ni ne registras vian uzantonomon)", "You do not have permission to start a conference call in this room": "Vi ne havas permeson komenci grupvokon en ĉi tiu ĉambro", @@ -981,7 +981,7 @@ "Room version": "Ĉambra versio", "Room version:": "Ĉambra versio:", "Developer options": "Programistaj elektebloj", - "Room Addresses": "Ĉambra adresoj", + "Room Addresses": "Adresoj de ĉambro", "Change room avatar": "Ŝanĝi profilbildon de ĉambro", "Change room name": "Ŝanĝi nomon de ĉambro", "Change main address for the room": "Ŝanĝi ĉefan adreson de la ĉambro", @@ -1477,7 +1477,7 @@ "Demote yourself?": "Ĉu malrangaltigi vin mem?", "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Vi ne povos malfari tiun ŝanĝon, ĉar vi malrangaltigas vin mem; se vi estas la lasta povohava uzanto en la ĉambro, estos neeble vian povon rehavi.", "Demote": "Malrangaltigi", - "Power level": "Povonivelo", + "Power level": "Povnivelo", "Use two-way text verification": "Uzi duflankan tekstan kontrolon", "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:": "Gradaltigo de ĉi tiu ĉambro bezonas fermi ĝin, kaj krei novan por anstataŭi ĝin. Por plejbonigi sperton de la ĉambranoj, ni:", "Invalid homeserver discovery response": "Nevalida eltrova respondo de hejmservilo", @@ -1485,7 +1485,7 @@ "Homeserver URL does not appear to be a valid Matrix homeserver": "URL por hejmservilo ŝajne ne ligas al valida hejmservilo de Matrix", "Invalid identity server discovery response": "Nevalida eltrova respondo de identiga servilo", "Identity server URL does not appear to be a valid identity server": "URL por identiga servilo ŝajne ne ligas al valida identiga servilo", - "Sign in with single sign-on": "Salutu per ununura saluto", + "Sign in with single sign-on": "Saluti per ununura saluto", "Failed to re-authenticate due to a homeserver problem": "Malsukcesis reaŭtentikigi pro hejmservila problemo", "Failed to re-authenticate": "Malsukcesis reaŭtentikigi", "Enter your password to sign in and regain access to your account.": "Enigu vian pasvorton por saluti kaj rehavi aliron al via konto.", @@ -1571,10 +1571,10 @@ "Always show the window menu bar": "Ĉiam montri la fenestran menubreton", "Upgrade the room": "Gradaltigi la ĉambron", "Enable room encryption": "Ŝalti ĉifradon de la ĉambro", - "Error changing power level requirement": "Eraris ŝanĝo de postulo de potenconivelo", - "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de la postuloj de la ĉambro pri potenconivelo. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", - "Error changing power level": "Eraris ŝanĝo de potenconivelo", - "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de potenconivelo de la uzanto. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", + "Error changing power level requirement": "Eraris ŝanĝo de postulo de povnivelo", + "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de la postuloj de la ĉambro pri povnivelo. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", + "Error changing power level": "Eraris ŝanĝo de povnivelo", + "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de povnivelo de la uzanto. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", "Remove %(email)s?": "Ĉu forigi %(email)s?", "Remove %(phone)s?": "Ĉu forigi %(phone)s?", "No recent messages by %(user)s found": "Neniuj freŝaj mesaĝoj de %(user)s troviĝis", @@ -1965,7 +1965,7 @@ "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Uzu kunigilon (%(serverName)s) por administrado de robotoj, fenestraĵoj, kaj glumarkaroj.", "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Uzu kunigilon por administrado de robotoj, fenestraĵoj, kaj glumarkaroj.", "Manage integrations": "Administri kunigojn", - "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Kunigiloj ricevas agordajn datumojn, kaj povas modifi fenestraĵojn, sendi invitojn al ĉambroj, kaj vianome agordi nivelojn de potenco.", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Kunigiloj ricevas agordajn datumojn, kaj povas modifi fenestraĵojn, sendi invitojn al ĉambroj, kaj vianome agordi povnivelojn.", "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Via pasvorto sukcese ŝanĝiĝis. Vi ne ricevados pasivajn sciigojn en aliaj salutaĵoj, ĝis vi ilin resalutos", "Error downloading theme information.": "Eraris elŝuto de informoj pri haŭto.", "Theme added!": "Haŭto aldoniĝis!", @@ -2396,5 +2396,18 @@ "Cancel replying to a message": "Nuligi respondon al mesaĝo", "Invite someone using their name, username (like ), email address or share this room.": "Invitu iun per ĝia nomo, uzantonomo (kiel ), retpoŝtadreso, aŭ kunhavigu la ĉambron.", "Message deleted": "Mesaĝo foriĝis", - "Message deleted by %(name)s": "Mesaĝon forigis %(name)s" + "Message deleted by %(name)s": "Mesaĝon forigis %(name)s", + "Opens chat with the given user": "Malfermas babilon kun la uzanto", + "Sends a message to the given user": "Sendas mesaĝon al la uzanto", + "Waiting for your other session to verify…": "Atendante kontrolon de via alia salutaĵo…", + "You've successfully verified your device!": "Vi sukcese kontrolis vian aparaton!", + "To continue, use Single Sign On to prove your identity.": "Por daŭrigi, pruvu vian identecon per ununura saluto.", + "Confirm to continue": "Konfirmu por daŭrigi", + "Click the button below to confirm your identity.": "Klaku sube la butonon por konfirmi vian identecon.", + "Confirm encryption setup": "Konfirmi agordon de ĉifrado", + "Click the button below to confirm setting up encryption.": "Klaku sube la butonon por konfirmi agordon de ĉifrado.", + "QR Code": "Rapidresponda kodo", + "Dismiss read marker and jump to bottom": "Forigi legomarkon kaj iri al fundo", + "Jump to oldest unread message": "Iri al plej malnova nelegita mesaĝo", + "Upload a file": "Alŝuti dosieron" } diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index e2d4e93e43..3fb069055c 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -6,7 +6,7 @@ "Failed to verify email address: make sure you clicked the link in the email": "E-posti aadressi kontrollimine ei õnnestunud: palun vaata, et sa kindlasti klõpsisid saabunud kirjas olnud viidet", "The platform you're on": "Sinu kasutatav arvutisüsteem", "The version of Riot": "Riot'i versioon", - "Whether or not you're logged in (we don't record your username)": "Kas sa oled sisseloginud või mitte (me ei salvesta sinu kasutajanime)", + "Whether or not you're logged in (we don't record your username)": "Kas sa oled sisseloginud või mitte (me ei salvesta sinu kasutajanime)", "Your language of choice": "Sinu keelevalik", "Your homeserver's URL": "Sinu koduserveri aadress", "e.g. ": "näiteks ", @@ -148,7 +148,7 @@ "Remove": "Eemalda", "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Sa peaksid enne ühenduse katkestamisst eemaldama isiklikud andmed id-serverist . Kahjuks id-server ei ole hetkel võrgus või pole kättesaadav.", "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Me soovitame, et eemaldad enne ühenduse katkestamist oma e-posti aadressi ja telefoninumbrid id-serverist.", - "Remove messages": "Kustuta sõnumid", + "Remove messages": "Kustuta sõnumeid", "Unable to remove contact information": "Kontaktiinfo eemaldamine ebaõnnestus", "Remove %(email)s?": "Eemalda %(email)s?", "Remove %(phone)s?": "Eemalda %(phone)s?", @@ -238,7 +238,7 @@ "Remove %(name)s from the directory?": "Eemalda %(name)s kataloogist?", "Remove from Directory": "Eemalda kataloogist", "remove %(name)s from the directory.": "eemalda %(name)s kataloogist.", - "You seem to be uploading files, are you sure you want to quit?": "Tundub, et sa parasjagu laed faile üles, kas sa kindlasti soovid väljuda?", + "You seem to be uploading files, are you sure you want to quit?": "Tundub, et sa parasjagu laed faile üles. Kas sa kindlasti soovid väljuda?", "Failed to set direct chat tag": "Otsevestluse sildi lisamine ei õnnestunud", "Failed to remove tag %(tagName)s from room": "Sildi %(tagName)s eemaldamine jututoast ebaõnnestus", "Calls": "Kõned", @@ -262,7 +262,7 @@ "Expand room list section": "Laienda jututubade loendi valikut", "Create Account": "Loo konto", "Sign In": "Logi sisse", - "Send a bug report with logs": "Saada veakirjeldus koos logidega", + "Send a bug report with logs": "Saada veakirjeldus koos logikirjetega", "Group & filter rooms by custom tags (refresh to apply changes)": "Rühmita ja filtreeri jututubasid kohandatud siltide alusel (muudatuste rakendamiseks värskenda vaade)", "Try out new ways to ignore people (experimental)": "Proovi uusi kasutajate eiramise viise (katseline)", "Uploading report": "Laen üles veakirjeldust", @@ -334,7 +334,7 @@ "Once enabled, encryption cannot be disabled.": "Kui krüptimine on juba kasutusele võetud, siis ei saa seda enam eemaldada.", "Encrypted": "Krüptitud", "Who can access this room?": "Kes pääsevad ligi siia jututuppa?", - "Who can read history?": "Kes võib lugeda ajalugu?", + "Who can read history?": "Kes võivad lugeda ajalugu?", "Encrypted by an unverified session": "Krüptitud verifitseerimata sessiooni poolt", "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Sõnumid selles jututoas on läbivalt krüptitud. Uuri lisaks ja verifitseeri see kasutaja tema kasutajaprofiilis.", "Encryption not enabled": "Krüptimine ei ole kasutusel", @@ -543,7 +543,7 @@ "Start a conversation with someone using their name, username (like ) or email address.": "Alusta vestlust kasutades teise osapoole nime, kasutajanime (näiteks ) või e-posti aadressi.", "Go": "Mine", "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Sinu sõnumit ei saadetud, kuna see koduserver on saavutanud igakuise aktiivsete kasutajate piiri. Teenuse kasutamiseks palun võta ühendust serveri haldajaga.", - "Add room": "LIsa jututuba", + "Add room": "Lisa jututuba", "%(senderName)s invited %(targetName)s.": "%(senderName)s kutsus vestlema kasutajat %(targetName)s.", "%(targetName)s joined the room.": "%(targetName)s liitus jututoaga.", "%(senderName)s answered the call.": "%(senderName)s vastas kõnele.", @@ -685,7 +685,7 @@ "Room version:": "Jututoa versioon:", "Developer options": "Valikud arendajale", "Open Devtools": "Ava arendusvahendid", - "Change room name": "Muuda jututoa nimi", + "Change room name": "Muuda jututoa nime", "Roles & Permissions": "Rollid ja õigused", "Room Name": "Jututoa nimi", "Room Topic": "Jututoa teema", @@ -961,5 +961,280 @@ "Previous/next room or DM": "Eelmine/järgmine otsevestlus või jututuba", "Toggle the top left menu": "Lülita ülemine vasak menüü sisse/välja", "Activate selected button": "Aktiveeri valitud nupp", - "Toggle right panel": "Lülita parem paan sisse/välja" + "Toggle right panel": "Lülita parem paan sisse/välja", + "%(count)s of your messages have not been sent.|other": "Mõned sinu sõnumid on saatmata.", + "%(count)s of your messages have not been sent.|one": "Sinu sõnum on saatmata.", + "You seem to be in a call, are you sure you want to quit?": "Tundub, et sul parasjagu on kõne pooleli. Kas sa kindlasti soovid väljuda?", + "Unknown room %(roomId)s": "Tundmatu jututuba %(roomId)s", + "Room": "Jututuba", + "Failed to reject invite": "Kutse tagasilükkamine ei õnnestunud", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Üritasin laadida teatud hetke selle jututoa ajajoonelt, kuid sul ei ole õigusi selle sõnumi nägemiseks.", + "Failed to load timeline position": "Asukoha laadimine ajajoonel ei õnnestunud", + " (1/%(totalCount)s)": " (1/%(totalCount)s)", + "Guest": "Külaline", + "Uploading %(filename)s and %(count)s others|other": "Laen üles %(filename)s ning %(count)s muud faili", + "Uploading %(filename)s and %(count)s others|zero": "Laen üles %(filename)s", + "Uploading %(filename)s and %(count)s others|one": "Laen üles %(filename)s ning veel %(count)s faili", + "Verify this login": "Verifitseeri see sisselogimissessioon", + "Session verified": "Sessioon on verifitseeritud", + "Failed to send email": "E-kirja saatmine ebaõnnestus", + "The email address linked to your account must be entered.": "Sa pead sisestama oma kontoga seotud e-posti aadressi.", + "A new password must be entered.": "Palun sisesta uus salasõna.", + "New passwords must match each other.": "Uued salasõnad peavad omavahel klappima.", + "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.": "Salasõna muutmine tühistab kõik läbiva krüptimise võtmed sinu kõikides sessioonides ning seega muutub kogu sinu vestluste ajalugu loetamatuks. Palun kindlasti kas sea üles võtmete varundamine või ekspordi mõnest muust sessioonist jututubade võtmed enne senise salasõna tühistamist.", + "Your Matrix account on %(serverName)s": "Sinu Matrix'i konto serveris %(serverName)s", + "Your Matrix account on ": "Sinu Matrix'i kasutajakonto serveris ", + "This homeserver does not support login using email address.": "See koduserver ei võimalda e-posti aadressi kasutamist sisselogimisel.", + "Please contact your service administrator to continue using this service.": "Jätkamaks selle teenuse kasutamist palun võta ühendust oma teenuse haldajaga.", + "This account has been deactivated.": "See kasutajakonto on deaktiveeritud.", + "Incorrect username and/or password.": "Vigane kasutajanimi ja/või salasõna.", + "Please note you are logging into the %(hs)s server, not matrix.org.": "Sa kasutad sisselogimiseks serverit %(hs)s, mitte aga matrix.org'i.", + "Failed to perform homeserver discovery": "Koduserveri leidmine ebaõnnestus", + "The phone number entered looks invalid": "Sisestatud telefoninumber tundub vigane", + "This homeserver doesn't offer any login flows which are supported by this client.": "See koduserver ei paku ühtegi sisselogimislahendust, mida see klient toetab.", + "Error: Problem communicating with the given homeserver.": "Viga: Suhtlusel koduserveriga tekkis probleem.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kui aadressiribal on HTTPS-aadress, siis HTTP-protokolli kasutades ei saa ühendust koduserveriga. Palun pruugi HTTPS-protokolli või luba brauseris ebaturvaliste skriptide kasutamine.", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Ei sa ühendust koduserveriga. Palun kontrolli, et sinu koduserveri SSL sertifikaat oleks usaldusväärne ning mõni brauseri lisamoodul ei blokeeri päringuid.", + "Syncing...": "Sünkroniseerin...", + "Signing In...": "Login sisse...", + "Create account": "Loo kasutajakonto", + "Failed to fetch avatar URL": "Ei õnnestunud laadida profiilipildi ehk avatari aadressi", + "Upload an avatar:": "Lae üles profiilipilt ehk avatar:", + "Unable to query for supported registration methods.": "Ei õnnestunud pärida toetatud registreerimismeetodite loendit.", + "Registration has been disabled on this homeserver.": "Väline registreerimine ei ole selles koduserveris kasutusel.", + "This server does not support authentication with a phone number.": "See server ei toeta autentimist telefoninumbri alusel.", + "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Sinu uus kasutajakonto (%(newAccountId)s) on registreeritud, kuid sa jube oled sisse loginud teise kasutajakontoga (%(loggedInUserId)s).", + "Continue with previous account": "Jätka senise konto kasutamist", + "Log in to your new account.": "Logi sisse oma uuele kasutajakontole.", + "Registration Successful": "Registreerimine õnnestus", + "Create your account": "Loo endale konto", + "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Kinnita oma isikusamasust verifitseerides seda sisselogimissessiooni mõnest oma muust sessioonist. Sellega tagad ka ligipääsu krüptitud sõnumitele.", + "This requires the latest Riot on your other devices:": "Selleks on sul vaja muudes seadmetes kõige uuemat Riot'i versiooni:", + "You're signed out": "Sa oled loginud välja", + "Clear personal data": "Kustuta privaatsed andmed", + "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.": "Hoiatus: Sinu privaatsed andmed (sealhulgas krüptimisvõtmed) on jätkuvalt salvestatud selles sessioonis. Eemalda nad, kui oled lõpetanud selle sessiooni kasutamise või soovid sisse logida muu kasutajakontoga.", + "Commands": "Käsud", + "Results from DuckDuckGo": "Otsingutulemused DuckDuckGo saidist", + "DuckDuckGo Results": "DuckDuckGo otsingutulemused", + "Emoji": "Emoji", + "Notify the whole room": "Teavita kogu jututuba", + "Users": "Kasutajad", + "unknown device": "tundmatu seade", + "NOT verified": "EI OLE verifitseeritud", + "verified": "verifitseeritud", + "Verification": "Verifitseerimine", + "Ed25519 fingerprint": "Ed25519 sõrmejälg", + "User ID": "Kasutajatunnus", + "Algorithm": "Algoritm", + "unencrypted": "krüptimata", + "Terms and Conditions": "Kasutustingimused", + "Logout": "Logi välja", + "Your Communities": "Sinu kogukonnad", + "Error whilst fetching joined communities": "Viga nende kogukondade laadimisel, millega sa oled liitunud", + "You have no visible notifications": "Sul ei ole nähtavaid teavitusi", + "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot'il ei õnnestunud koduserverist laadida toetatud protokollide loendit. Toetamaks kolmandate osapoolte võrke võib koduserver olla liiga vana.", + "Riot failed to get the public room list.": "Riot'il ei õnnestunud laadida avalike jututubade loendit.", + "The homeserver may be unavailable or overloaded.": "Koduserver pole kas saadaval või on üle koormatud.", + "Room not found": "Jututuba ei leidunud", + "Preview": "Eelvaade", + "View": "Näita", + "Message not sent due to unknown sessions being present": "Sõnum on saatmata, sest jutuoas on tundmatud sessioonid", + "You can't send any messages until you review and agree to our terms and conditions.": "Sa ei saa saata ühtego sõnumit enne, kui oled läbi lugenud ja nõustunud meie kasutustingimustega.", + "Couldn't load page": "Lehe laadimine ei õnnestunud", + "You must register to use this functionality": "Selle funktsionaalsuse kasutamiseks pead sa registreeruma", + "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even use 'img' tags\n

\n": "

Sinu kogukonna lehe HTML'i näidis - see on pealkiri

\n

\n Tutvustamaks uutele liikmetele kogukonda, kasuta seda pikka kirjeldust\n või jaga olulist teavet viidetena\n

\n

\n Pildite lisaminseks võid sa isegi kasutada img-märgendit\n

\n", + "Add to summary": "Lisa kokkuvõtte lehele", + "Add a Room": "Lisa jututuba", + "The room '%(roomName)s' could not be removed from the summary.": "Valitud %(roomName)s jututoa eemaldamine koondinfost ei õnnestunud.", + "Add users to the community summary": "Lisa kasutajad kogukonna kokkuvõtte lehele", + "Who would you like to add to this summary?": "Keda sa sooviksid lisada siia kokkuvõtte lehele?", + "Failed to add the following users to the summary of %(groupId)s:": "Järgnevate kasutajate lisamine %(groupId)s kogukonna koondinfo lehele ei õnnestunud:", + "Add a User": "Lisa kasutaja", + "The user '%(displayName)s' could not be removed from the summary.": "Kasutaja %(displayName)s eemaldamine koondinfo lehelt ei õnnestunud.", + "Failed to upload image": "Pildi üleslaadimine ei õnnestunud", + "Failed to update community": "Kogukonna uuendamine ei õnnestunud", + "Unable to accept invite": "Ei saanud kutset vastu võtta", + "Unable to join community": "Ei õnnetunud liituda kogukonnaga", + "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Sa oled selle kogukonna haldur. Sa ei saa uuesti liituda ilma kutseta mõnelt teiselt administraatori õigustes liikmelt.", + "Leave Community": "Lahku kogukonnast", + "Leave %(groupName)s?": "Lahku %(groupName)s kogukonnast?", + "Unable to leave community": "Kogukonnast lahkumine ei õnnestunud", + "Community Settings": "Kogukonna seadistused", + "Want more than a community? Get your own server": "Soovid enamat kui kogukond? Pane üles oma server", + "Changes made to your community name and avatar might not be seen by other users for up to 30 minutes.": "Oma kogukonnas tehtud nime ja avatari muudatused ei pruugi teistele näha olla kuni 30 minuti jooksul.", + "Featured Users:": "Esiletõstetud kasutajad:", + "%(inviter)s has invited you to join this community": "%(inviter)s on kutsunud sind kogukonna liikmeks", + "Join this community": "Liitu selle kogukonnaga", + "Leave this community": "Lahku sellest kogukonnast", + "You are an administrator of this community": "Sa oled selle kogukonna haldur", + "You are a member of this community": "Sa oled kogukonna liige", + "Who can join this community?": "Kes võib liituda selle kogukonnaga?", + "Everyone": "Kes iganes soovib", + "Your community hasn't got a Long Description, a HTML page to show to community members.
Click here to open settings and give it one!": "Sinu kogukonnal on puudu pikk kirjeldus, mis pole muud kui lihtne HTML-leht, mida kuvatakse liikmetele.
Klõpsi siia ja loo selline leht!", + "Long Description (HTML)": "Pikk kirjeldus (HTML)", + "Upload avatar": "Lae üles profiilipilt ehk avatar", + "Description": "Kirjeldus", + "Community %(groupId)s not found": "%(groupId)s kogukonda ei leidunud", + "This homeserver does not support communities": "See koduserver ei toeta kogukondade funktsionaalsust", + "Failed to load %(groupId)s": "%(groupId)s kogukonna laadimine ei õnnestunud", + "Welcome to %(appName)s": "Tere tulemast %(appName)s kasutajaks", + "Liberate your communication": "Vabasta oma suhtlus", + "Send a Direct Message": "Saada otsesõnum", + "Are you sure you want to leave the room '%(roomName)s'?": "Kas oled kindel, et soovid lahkuda jututoast '%(roomName)s'?", + "Unknown error": "Teadmata viga", + "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleka et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutamistingimustega.", + "Permissions": "Õigused", + "Select the roles required to change various parts of the room": "Vali rollid, mis on vajalikud jututoa eri osade muutmiseks", + "Enable encryption?": "Kas võtame krüptimise kasutusele?", + "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "Kui kord juba kasutusele võetud, siis krüptimist enam hiljem ära lõpetada ei saa. Krüptitud sõnumeid ei saa lugeda ei vaheapealses veebiliikluses ega serveris ja vaid jututoa liikmed saavad neid lugeda. Krüptimise kasutusele võtmine takistada nii robotite kui sõnumisildade tööd. Lisateave krüptimise kohta.", + "Guests cannot join this room even if explicitly invited.": "Külalised ei saa selle jututoaga liituda ka siis, kui neid on otseselt kutsutud.", + "Click here to fix": "Parandamiseks klõpsi siia", + "Server error": "Serveri viga", + "Command error": "Käsu viga", + "Server unavailable, overloaded, or something else went wrong.": "Kas server pole saadaval, on üle koormatud või midagi muud läks viltu.", + "Unknown Command": "Tundmatu käsk", + "Unrecognised command: %(commandText)s": "Tundmatu käsk: %(commandText)s", + "Send as message": "Saada sõnumina", + "Failed to connect to integration manager": "Ühendus integratsioonihalduriga ei õnnestunud", + "You don't currently have any stickerpacks enabled": "Sul pole ühtegi kleepsupakki kasutusel", + "Add some now": "Lisa nüüd mõned", + "Stickerpack": "Kleepsupakk", + "Hide Stickers": "Peida kleepsud", + "Show Stickers": "Näita kleepse", + "Failed to revoke invite": "Kutse tühistamine ei õnnestunud", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Kutse tühistamine ei õnnestunud. Serveri töös võib olla ajutine tõrge või sul pole piisavalt õigusi kutse tühistamiseks.", + "Revoke invite": "Tühista kutse", + "Invited by %(sender)s": "Kutsutud %(sender)s poolt", + "Jump to first unread message.": "Mine esimese lugemata sõnumi juurde.", + "Mark all as read": "Märgi kõik loetuks", + "Error updating main address": "Viga põhiaadressi uuendamisel", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Jututoa põhiaadressi uuendamisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Jututoa lisaaadressi uuendamisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "Error creating alias": "Viga aliase loomisel", + "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "Aliase loomisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "You don't have permission to delete the alias.": "Sul pole õigusi aliase kustutamiseks.", + "There was an error removing that alias. It may no longer exist or a temporary error occurred.": "Selle aliase eemaldamisel tekkis viga. Teda kas pole enam olemas või tekkis mingi ajutine viga.", + "Error removing alias": "Viga aliase eemaldamisel", + "Main address": "Põhiaadress", + "not specified": "määratlemata", + "Rejecting invite …": "Hülgan kutset …", + "Join the conversation with an account": "Liitu vestlusega kasutades oma kontot", + "Sign Up": "Registreeru", + "Loading room preview": "Laen jututoa eelvaadet", + "You were kicked from %(roomName)s by %(memberName)s": "%(memberName)s müksas sind välja jututoast %(roomName)s", + "Reason: %(reason)s": "Põhjus: %(reason)s", + "Forget this room": "Unusta see jututuba", + "Re-join": "Liitu uuesti", + "You were banned from %(roomName)s by %(memberName)s": "%(memberName)s keelas sulle ligipääsu jututuppa %(roomName)s", + "Something went wrong with your invite to %(roomName)s": "Midagi läks viltu sinu kutsega %(roomName)s jututuppa", + "An error (%(errcode)s) was returned while trying to validate your invite. You could try to pass this information on to a room admin.": "Sinu kutse kontrollimisel tekkis viga (%(errcode)s). Kui võimalik, siis proovi see info edastada jututoa haldurile.", + "unknown error code": "tundmatu veakood", + "You can only join it with a working invite.": "Sa võid liituda vaid toimiva kutse alusel.", + "Try to join anyway": "Proovi siiski liituda", + "You can still join it because this is a public room.": "Kuna tegemist on avaliku jututoaga, siis võid ikkagi liituda.", + "Join the discussion": "Liitu vestlusega", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "See kutse jututuppa %(roomName)s saadeti e-posti aadressile %(email)s, mis ei ole seotud sinu kontoga", + "Link this email with your account in Settings to receive invites directly in Riot.": "Selleks et saada kutseid otse Riot'isse, seosta see e-posti aadress seadete all oma kontoga.", + "This invite to %(roomName)s was sent to %(email)s": "Kutse %(roomName)s jututuppa saadeti %(email)s e-posti aadressile", + "Use an identity server in Settings to receive invites directly in Riot.": "Selleks et saada kutseid otse Riot'isse peab seadistustes olema määratud isikutuvastusserver.", + "Share this email in Settings to receive invites directly in Riot.": "Selleks, et saada kutseid otse Riot'isse, jaga oma seadetes seda e-posti aadressi.", + "Start chatting": "Alusta vestlust", + "Do you want to join %(roomName)s?": "Kas sa soovid liitud jututoaga %(roomName)s?", + " invited you": " kutsus sind", + "Reject": "Hülga", + "You're previewing %(roomName)s. Want to join it?": "Sa vaatad jututoa %(roomName)s eelvaadet. Kas soovid sellega liituda?", + "%(roomName)s can't be previewed. Do you want to join it?": "Jututoal %(roomName)s puudub eelvaate võimalus. Kas sa soovid sellega liituda?", + "%(roomName)s does not exist.": "Jututuba %(roomName)s ei ole olemas.", + "This room doesn't exist. Are you sure you're at the right place?": "Seda jututuba ei ole olemas. Kas sa oled kindlasti õiges kohas?", + "%(roomName)s is not accessible at this time.": "Jututuba %(roomName)s ei ole parasjagu kättesaadav.", + "Try again later, or ask a room admin to check if you have access.": "Proovi hiljem uuesti või küsi jututoa haldurilt, kas sul on ligipääs olemas.", + "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "Astumisel jututuppa tekkis viga %(errcode)s. Kui sa arvad, et sellise põhjusega viga ei tohiks tekkida, siis palun koosta veateade.", + "Never lose encrypted messages": "Ära kunagi kaota krüptitud sõnumeid", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Sõnumid siis jututoas kasutavad läbivat krüptimist. Ainult sinul ja saaja(te)l on võtmed selliste sõnumite lugemiseks.", + "Securely back up your keys to avoid losing them. Learn more.": "Vältimaks nende kaotamist, varunda turvaliselt oma võtmed. Loe lisateavet.", + "Not now": "Mitte praegu", + "Don't ask me again": "Ära küsi minult uuesti", + "%(count)s unread messages including mentions.|other": "%(count)s lugemata sõnumit kaasa arvatud mainimised.", + "%(count)s unread messages.|other": "%(count)s lugemata teadet.", + "%(count)s unread messages.|one": "1 lugemata teade.", + "Unread mentions.": "Lugemata mainimised.", + "Unread messages.": "Lugemata sõnumid.", + "Add a topic": "Lisa teema", + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Selle jututoa versiooni uuendamine sulgeb tema praeguse instantsi ja loob sama nimega uuendatud jututoa.", + "This room has already been upgraded.": "See jututuba on juba uuendatud.", + "This room is running room version , which this homeserver has marked as unstable.": "Selle jututoa versioon on ning see koduserver on tema märkinud ebastabiilseks.", + "Only room administrators will see this warning": "Vaid administraatorid näevad seda hoiatust", + "You can use /help to list available commands. Did you mean to send this as a message?": "Kirjutades /help saad vaadata käskude loendit. Või soovisid seda saata sõnumina?", + "Hint: Begin your message with // to start it with a slash.": "Vihje: kui soovid alustada sõnumit kaldkriipsuga, siis kirjuta //.", + "To link to this room, please add an alias.": "Viitamaks sellele jututoale, palun määra talle alias.", + "Only people who have been invited": "Vaid kutsutud kasutajad", + "Anyone who knows the room's link, apart from guests": "Kõik, kes teavad jututoa viidet, välja arvatud külalised", + "Anyone who knows the room's link, including guests": "Kõik, kes teavad jututoa viidet, kaasa arvatud külalised", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Kui muudad seda, kes saavad selle jututoa ajalugu lugeda, siis kehtib see vaid tulevaste sõnumite kohta. Senise ajaloo nähtavus sellega ei muutu.", + "Unable to revoke sharing for email address": "Ei õnnestu tagasi võtta otsust e-posti aadressi jagamise kohta", + "Unable to share email address": "Ei õnnestu jagada e-posti aadressi", + "Your email address hasn't been verified yet": "Sinu e-posti aadress pole veel verifitseeritud", + "Click the link in the email you received to verify and then click continue again.": "Klõpsi saabunud e-kirjas olevat verifitseerimisviidet ning seejärel klõpsi siin uuesti nuppu \"Jätka\".", + "Unable to verify email address.": "E-posti aadressi verifitseerimine ei õnnestunud.", + "Verify the link in your inbox": "Verifitseeri klõpsides viidet saabunud e-kirjas", + "Complete": "Valmis", + "Revoke": "Tühista", + "Share": "Jaga", + "Session already verified!": "Sessioon on juba verifitseeritud!", + "WARNING: Session already verified, but keys do NOT MATCH!": "HOIATUS: Sessioon on juba verifitseeritud, aga võtmed ei klapi!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "HOIATUS: VÕTMETE VERIFITSEERIMINE EI ÕNNESTUNUD! Kasutaja %(userId)s ja sessiooni %(deviceId)s allkirjastamise võti on \"%(fprint)s\", aga see ei vasta antud sõrmejäljele \"%(fingerprint)s\". See võib tähendada, et sinu kasutatavad ühendused võivad olla kolmanda osapoole poolt vahelt lõigatud!", + "Verified key": "Verifitseeritud võti", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Sinu antud allkirjavõti vastab allkirjavõtmele, mille sa said kasutaja %(userId)s sessioonist %(deviceId)s. Sessioon on märgitud verifitseerituks.", + "Sends the given message coloured as a rainbow": "Saadab selle sõnumi vikerkaarevärvilisena", + "Sends the given emote coloured as a rainbow": "Saadab antud emote vikerkaarevärvides", + "Displays list of commands with usages and descriptions": "Näitab käskude loendit koos kirjeldustega", + "Logs sent": "Logikirjed saadetud", + "Thank you!": "Suur tänu!", + "Opens chat with the given user": "Avab vestluse näidatud kasutajaga", + "Sends a message to the given user": "Saadab sõnumi näidatud kasutajale", + "Short keyboard patterns are easy to guess": "Lühikesi klahvijärjestusi on lihtne ära arvata", + "Keyboard Shortcuts": "Kiirklahvid", + "This room is bridging messages to the following platforms. Learn more.": "See jututuba kasutab sõnumisildasid liidestamiseks järgmiste süsteemidega. Lisateave.", + "This room isn’t bridging messages to any platforms. Learn more.": "See jututuba ei kasuta sõnumisildasid liidestamiseks muude süsteemidega. Lisateave.", + "Bridges": "Sõnumisillad", + "Room Addresses": "Jututubade aadressid", + "Browse": "Sirvi", + "Change room avatar": "Muuda jututoa profiilipilti ehk avatari", + "Change main address for the room": "Muuda jututoa põhiaadressi", + "Change history visibility": "Muuda vestlusajaloo nähtavust", + "Change permissions": "Muuda õigusi", + "Change topic": "Muuda teemat", + "Upgrade the room": "Uuenda jututuba uue versioonini", + "Enable room encryption": "Võta jututoas kasutusele krüptimine", + "Modify widgets": "Muuda vidinaid", + "Default role": "Vaikimisi roll", + "Send messages": "Saada sõnumeid", + "Invite users": "Kutsu kasutajaid", + "Change settings": "Muuda seadistusi", + "Kick users": "Müksa kasutajaid välja", + "Ban users": "Määra kasutajatele suhtluskeeld", + "Notify everyone": "Teavita kõiki", + "No users have specific privileges in this room": "Mitte ühelgi kasutajal pole siin jututoas eelisõigusi", + "Privileged Users": "Eelisõigustega kasutajad", + "Banned users": "Suhtluskeelu saanud kasutajad", + "Send %(eventType)s events": "Saada %(eventType)s-sündmusi", + "Unable to revoke sharing for phone number": "Telefoninumbri jagamist ei õnnestunud tühistada", + "Unable to share phone number": "Telefoninumbri jagamine ei õnnestunud", + "Unable to verify phone number.": "Telefoninumbri verifitseerimine ei õnnestunud.", + "Incorrect verification code": "Vigane verifikatsioonikood", + "Please enter verification code sent via text.": "Palun sisesta verifikatsioonikood, mille said telefoni tekstisõnumina.", + "Verification code": "Verifikatsioonikood", + "Invalid Email Address": "Vigane e-posti aadress", + "This doesn't appear to be a valid email address": "See ei tundu olema e-posti aadressi moodi", + "Preparing to send logs": "Valmistun logikirjete saatmiseks", + "Failed to send logs: ": "Logikirjete saatmine ei õnnestunud: ", + "Verify session": "Verifitseeri sessioon", + "Use Legacy Verification (for older clients)": "Kasuta vanematüübilist verifitseerimist (vähem-moodsa klient-tarkvara jaoks)", + "Verify by comparing a short text string.": "Verifitseeri kasutades lühikeste sõnede võrdlemist.", + "Begin Verifying": "Alusta verifitseerimist", + "Waiting for partner to accept...": "Ootan, et teine osapool nõustuks…", + "Nothing appearing? Not all clients support interactive verification yet. .": "Mitte midagi ei kuvata? Kõik Matrix'i kliendid ei toeta veel interaktiivset verifitseerimist. .", + "Waiting for %(userId)s to confirm...": "Ootan kinnitust kasutajalt %(userId)s…", + "Skip": "Jäta vahele", + "Token incorrect": "Vigane tunnusluba" } diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 8135462d60..ae08572eb2 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1282,7 +1282,7 @@ "Premium hosting for organisations Learn more": "Premium-ylläpitoa organisaatioille. Lue lisää", "Other": "Muut", "Find other public servers or use a custom server": "Etsi muita julkisia palvelimia tai käytä mukautettua palvelinta", - "Please install Chrome, Firefox, or Safari for the best experience.": "Parhaan käyttökokemuksen saa Chromella, Firefoxilla tai Safarilla.", + "Please install Chrome, Firefox, or Safari for the best experience.": "Asenna Chrome, Firefox tai Safari, jotta kaikki toimii parhaiten.", "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even use 'img' tags\n

\n": "

HTML-kuvaus yhteisösi etusivulle

\n

\n Käytä pitkää kuvausta esitelläksesi yhteisöäsi uusille jäsenille tai jakaaksesi tärkeitä\n linkkejä\n

\n

\n Voit jopa käyttää 'img'-tageja\n

\n", "Unable to join community": "Yhteisöön liittyminen epäonnistui", "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Olet tämän yhteisön ylläpitäjä. Et voi liittyä uudelleen ilman kutsua toiselta ylläpitäjältä.", @@ -2157,5 +2157,37 @@ "Syncing...": "Synkronoidaan...", "Signing In...": "Kirjaudutaan sisään...", "If you've joined lots of rooms, this might take a while": "Jos olet liittynyt moniin huoneisiin, tässä voi kestää hetken", - "Use your other device to continue…": "Jatka toisella laitteellasi…" + "Use your other device to continue…": "Jatka toisella laitteellasi…", + "Click the button below to confirm adding this email address.": "Klikkaa alapuolella olevaa painiketta lisätäksesi tämän sähköpostiosoitteen.", + "Click the button below to confirm adding this phone number.": "Klikkaa alapuolella olevaa painiketta lisätäksesi tämän puhelinnumeron.", + "If you cancel now, you won't complete your operation.": "Jos peruutat, toimintoa ei suoriteta loppuun.", + "Review where you’re logged in": "Tarkasta missä olet sisäänkirjautuneena", + "New login. Was this you?": "Uusi sisäänkirjautuminen. Olitko se sinä?", + "%(name)s is requesting verification": "%(name)s pyytää varmennusta", + "Sends a message as html, without interpreting it as markdown": "Lähettää viestin HTML-muodossa, tulkitsematta sitä Markdowniksi", + "Please supply a widget URL or embed code": "Anna sovelman osoite tai upotettava koodinpätkä", + "You signed in to a new session without verifying it:": "Olet kirjautunut uuteen istuntoon varmentamatta sitä:", + "Verify your other session using one of the options below.": "Varmenna toinen istuntosi käyttämällä yhtä seuraavista tavoista.", + "Click the button below to confirm deleting these sessions.|other": "Napsauta alla olevaa painiketta vahvistaaksesi näiden istuntojen poistamisen.", + "Click the button below to confirm deleting these sessions.|one": "Napsauta alla olevaa painiketta vahvistaaksesi tämän istunnon poistamisen.", + "Backup has a signature from unknown session with ID %(deviceId)s": "Varmuuskopiossa on allekirjoitus tuntemattomasta istunnosta tunnuksella %(deviceId)s", + "Error downloading theme information.": "Virhe ladattaessa teematietoa.", + "Waiting for you to accept on your other session…": "Odotetaan, että hyväksyt toisen istunnon…", + "Almost there! Is your other session showing the same shield?": "Melkein valmista! Näyttääkö toinen istuntosi saman kilven?", + "Almost there! Is %(displayName)s showing the same shield?": "Melkein valmista! Näyttääkö %(displayName)s saman kilven?", + "Message deleted": "Viesti poistettu", + "Message deleted by %(name)s": "%(name)s poisti viestin", + "QR Code": "QR-koodi", + "To continue, use Single Sign On to prove your identity.": "Todista henkilöllisyytesi kertakirjautumisen avulla jatkaaksesi.", + "If they don't match, the security of your communication may be compromised.": "Jos ne eivät täsmää, viestinnän turvallisuus saattaa olla vaarantunut.", + "This session, or the other session": "Tämä tai toinen istunto", + "We recommend you change your password and recovery key in Settings immediately": "Suosittelemme, että vaihdat salasanasi ja palautusavaimesi asetuksissa välittömästi", + "Restoring keys from backup": "Palautetaan avaimia varmuuskopiosta", + "Fetching keys from server...": "Noudetaan avaimia palvelimelta...", + "%(completed)s of %(total)s keys restored": "%(completed)s / %(total)s avainta palautettu", + "Keys restored": "Avaimet palautettu", + "Successfully restored %(sessionCount)s keys": "%(sessionCount)s avaimen palautus onnistui", + "This requires the latest Riot on your other devices:": "Tämä vaatii uusimman Riotin muilla laitteillasi:", + "Currently indexing: %(currentRoom)s": "Indeksoidaan huonetta: %(currentRoom)s", + "Jump to oldest unread message": "Siirry vanhimpaan lukemattomaan viestiin" } diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 1756ff57ad..c231769f27 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2422,5 +2422,18 @@ "Verify your other session using one of the options below.": "Vérifiez votre autre session en utilisant une des options ci-dessous.", "Invite someone using their name, username (like ), email address or share this room.": "Invitez quelqu’un en utilisant leur nom, leur nom d’utilisateur (comme ), leur adresse e-mail ou partagez ce salon.", "Message deleted": "Message supprimé", - "Message deleted by %(name)s": "Message supprimé par %(name)s" + "Message deleted by %(name)s": "Message supprimé par %(name)s", + "Opens chat with the given user": "Ouvre une discussion avec l’utilisateur fourni", + "Sends a message to the given user": "Envoie un message à l’utilisateur fourni", + "Waiting for your other session to verify…": "En attente de la vérification depuis votre autre session…", + "You've successfully verified your device!": "Vous avez bien vérifié votre appareil !", + "To continue, use Single Sign On to prove your identity.": "Pour continuer, utilisez l’authentification unique pour prouver votre identité.", + "Confirm to continue": "Confirmer pour continuer", + "Click the button below to confirm your identity.": "Cliquez sur le bouton ci-dessous pour confirmer votre identité.", + "Confirm encryption setup": "Confirmer la configuration du chiffrement", + "Click the button below to confirm setting up encryption.": "Cliquez sur le bouton ci-dessous pour confirmer la configuration du chiffrement.", + "QR Code": "Code QR", + "Dismiss read marker and jump to bottom": "Ignorer le signet de lecture et aller en bas", + "Jump to oldest unread message": "Aller au plus vieux message non lu", + "Upload a file": "Envoyer un fichier" } diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 346dda09f2..3328292be0 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -698,9 +698,9 @@ "Failed to remove tag %(tagName)s from room": "Fallo ao eliminar a etiqueta %(tagName)s da sala", "Failed to add tag %(tagName)s to room": "Fallo ao engadir a etiqueta %(tagName)s a sala", "Key request sent.": "Petición de chave enviada.", - "Flair": "Aura", - "Showing flair for these communities:": "Mostrar a aura para estas comunidades:", - "Display your community flair in rooms configured to show it.": "Mostrar a aura da súa comunidade nas salas configuradas para que a mostren.", + "Flair": "Popularidade", + "Showing flair for these communities:": "Mostrar a popularidade destas comunidades:", + "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ía que pode utilizar as comunidades para mellorar a súa experiencia con 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, arrastre un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Pode pulsar 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", @@ -822,7 +822,7 @@ "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.", "Unhide Preview": "Desagochar a vista previa", "Unable to join network": "Non se puido conectar a 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 configurase nun cliente diferente de Riot. Non pode 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", "Sorry, your browser is not able to run Riot.": "Desculpe, o seu navegador non pode executar Riot.", "Uploaded on %(date)s by %(user)s": "Subido a %(date)s por %(user)s", "Messages in group chats": "Mensaxes en grupos de chat", @@ -919,5 +919,23 @@ "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "A súa mensaxe non foi enviada porque este servidor acadou o Límite Mensual de Usuaria Activa. Por favor contacte coa administración do servizo para continuar utilizando o servizo.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "A súa mensaxe non foi enviada porque o servidor superou o límite de recursos. Por favor contacte coa administración do servizo para continuar utilizando o servizo.", "Legal": "Legal", - "Please contact your service administrator to continue using this service.": "Por favor contacte coa administración do servizo para continuar utilizando o servizo." + "Please contact your service administrator to continue using this service.": "Por favor contacte coa administración do servizo para continuar utilizando o servizo.", + "Use Single Sign On to continue": "Usar Single Sign On para continuar", + "Confirm adding this email address by using Single Sign On to prove your identity.": "Confirma que queres engadir este email usando Single Sign On como proba de identidade.", + "Single Sign On": "Single Sign On", + "Confirm adding email": "Confirma novo email", + "Click the button below to confirm adding this email address.": "Preme no botón inferior para confirmar que queres engadir o email.", + "Confirm": "Confirmar", + "Add Email Address": "Engadir email", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Confirma que queres engadir este teléfono usando Single Sign On como proba de identidade.", + "Confirm adding phone number": "Confirma a adición do teléfono", + "Click the button below to confirm adding this phone number.": "Preme no botón inferior para confirmar que engades este número.", + "Add Phone Number": "Engadir novo Número", + "The version of Riot": "A versión de Riot", + "Whether or not you're logged in (we don't record your username)": "Se estás conectada ou non (non rexistramos o teu nome de usuaria)", + "Whether you're using Riot on a device where touch is the primary input mechanism": "Se estás conectada utilizando Riot nun dispositivo maiormente táctil", + "Whether you're using Riot as an installed Progressive Web App": "Se estás a usar Riot como unha Progressive Web App instalada", + "Your user agent": "User Agent do navegador", + "The information being sent to us to help make Riot better includes:": "Información que nos envías para mellorar Riot inclúe:", + "Please install Chrome, Firefox, or Safari for the best experience.": "Instala Chrome, Firefox, ou Safari para ter unha mellor experiencia." } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index afad390192..17254a77db 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2409,5 +2409,20 @@ "Successfully restored %(sessionCount)s keys": "Kulcsok (%(sessionCount)s) sikeresen visszaállítva", "You signed in to a new session without verifying it:": "Ellenőrzés nélkül jelentkeztél be egy új munkamenetbe:", "Verify your other session using one of the options below.": "Ellenőrizd a másik munkameneted a lenti lehetőségek egyikével.", - "Invite someone using their name, username (like ), email address or share this room.": "Hívj meg valakit a nevével, felhasználónevével (mint ), e-mail címével vagy oszd meg ezt a szobát." + "Invite someone using their name, username (like ), email address or share this room.": "Hívj meg valakit a nevével, felhasználónevével (mint ), e-mail címével vagy oszd meg ezt a szobát.", + "Opens chat with the given user": "Beszélgetés megnyitása a megadott felhasználóval", + "Sends a message to the given user": "Üzenet küldése a megadott felhasználónak", + "Waiting for your other session to verify…": "A másik munkameneted ellenőrzésére várunk…", + "You've successfully verified your device!": "Sikeresen ellenőrizted az eszközödet!", + "Message deleted": "Üzenet törölve", + "Message deleted by %(name)s": "Üzenetet törölte: %(name)s", + "QR Code": "QR kód", + "To continue, use Single Sign On to prove your identity.": "A folytatáshoz a személyazonosságod megerősítéséhez használd az egyszeri bejelentkezést.", + "Confirm to continue": "Erősítsd meg a továbblépéshez", + "Click the button below to confirm your identity.": "A személyazonosságod megerősítéséhez kattints az alábbi gombra.", + "Confirm encryption setup": "Erősítsd meg a titkosítási beállításokat", + "Click the button below to confirm setting up encryption.": "Az alábbi gomb megnyomásával erősítsd meg, hogy megadod a titkosítási beállításokat.", + "Dismiss read marker and jump to bottom": "Az olvasottak jel eltűntetése és ugrás a végére", + "Jump to oldest unread message": "A legrégebbi olvasatlan üzenetre ugrás", + "Upload a file": "Fájl feltöltése" } diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index d891a06687..014d6015b3 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2417,5 +2417,18 @@ "Verify your other session using one of the options below.": "Verifica la tua altra sessione usando una delle opzioni sotto.", "Invite someone using their name, username (like ), email address or share this room.": "Invita qualcuno usando il suo nome, nome utente (come ), indirizzo email o condividi questa stanza.", "Message deleted": "Messaggio eliminato", - "Message deleted by %(name)s": "Messaggio eliminato da %(name)s" + "Message deleted by %(name)s": "Messaggio eliminato da %(name)s", + "Opens chat with the given user": "Apre una chat con l'utente specificato", + "Sends a message to the given user": "Invia un messaggio all'utente specificato", + "Waiting for your other session to verify…": "In attesa che la tua altra sessione verifichi…", + "You've successfully verified your device!": "Hai verificato correttamente il tuo dispositivo!", + "To continue, use Single Sign On to prove your identity.": "Per continuare, usa Single Sign On per provare la tua identità.", + "Confirm to continue": "Conferma per continuare", + "Click the button below to confirm your identity.": "Clicca il pulsante sotto per confermare la tua identità.", + "Confirm encryption setup": "Conferma impostazione cifratura", + "Click the button below to confirm setting up encryption.": "Clicca il pulsante sotto per confermare l'impostazione della cifratura.", + "QR Code": "Codice QR", + "Dismiss read marker and jump to bottom": "Scarta il segno di lettura e salta alla fine", + "Jump to oldest unread message": "Salta al messaggio non letto più vecchio", + "Upload a file": "Invia un file" } diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 4271864216..cb8fb17da3 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -523,7 +523,7 @@ "Invite to this room": "이 방에 초대", "You cannot delete this message. (%(code)s)": "이 메시지를 삭제할 수 없습니다. (%(code)s)", "Thursday": "목요일", - "I understand the risks and wish to continue": "위험을 인지하고 계속하고 싶습니다", + "I understand the risks and wish to continue": "위험하다는 것을 이해했으며 계속하고 싶습니다", "Back": "돌아가기", "Failed to change settings": "설정 변경 실패", "Show message in desktop notification": "컴퓨터 알림에서 내용 보이기", @@ -1799,5 +1799,7 @@ "Session already verified!": "이미 검증된 세션입니다!", "WARNING: Session already verified, but keys do NOT MATCH!": "경고: 이미 검증된 세션이지만 키가 일치하지 않습니다!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "경고: 키 검증 실패! 제공된 키인 \"%(fingerprint)s\"가 사용자 %(userId)s와 %(deviceId)s 세션의 서명 키인 \"%(fprint)s\"와 일치하지 않습니다. 이는 통신이 탈취되고 있는 중일 수도 있다는 뜻입니다!", - "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "사용자 %(userId)s의 세션 %(deviceId)s에서 받은 서명 키와 당신이 제공한 서명 키가 일치합니다. 세션이 검증되었습니다." + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "사용자 %(userId)s의 세션 %(deviceId)s에서 받은 서명 키와 당신이 제공한 서명 키가 일치합니다. 세션이 검증되었습니다.", + "Show more": "더 보기", + "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "비밀번호를 변경한다면 방의 암호화 키를 내보낸 후 다시 가져오지 않는 이상 모든 종단간 암호화 키는 초기화 될 것이고, 암호화된 대화 내역은 읽을 수 없게 될 것입니다. 이 문제는 추후에 개선될 것입니다." } diff --git a/src/i18n/strings/nb_NO.json b/src/i18n/strings/nb_NO.json index 147584ed00..59182a0249 100644 --- a/src/i18n/strings/nb_NO.json +++ b/src/i18n/strings/nb_NO.json @@ -1341,5 +1341,6 @@ "You're done!": "Du har gjort alt klart!", "Enter a recovery passphrase...": "Skriv inn en gjenopprettingspassfrase …", "Please enter your recovery passphrase a second time to confirm.": "Skriv inn gjenopprettingspassfrasen din en andre gang for å bekrefte.", - "Repeat your recovery passphrase...": "Gjenta gjenopprettingspassfrasen din …" + "Repeat your recovery passphrase...": "Gjenta gjenopprettingspassfrasen din …", + "Other users may not trust it": "Andre brukere kan kanskje mistro den" } diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index fbecaa4845..e85732ed82 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -628,7 +628,7 @@ "Export room keys": "Exportovať kľúče miestností", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Tento proces vás prevedie exportom kľúčov určených na dešifrovanie správ, ktoré ste dostali v šifrovaných miestnostiach do lokálneho súboru. Tieto kľúče zo súboru môžete neskôr importovať do iného Matrix klienta, aby ste v ňom mohli dešifrovať vaše šifrované správy.", "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Tento súbor umožní komukoľvek, k to má ku nemu prístup dešifrovať všetky vami viditeľné šifrované správy, mali by ste teda byť opatrní a tento súbor si bezpečne uchovať. Aby bolo toto pre vás jednoduchšie, nižšie zadajte heslo, ktorým budú údaje v súbore zašifrované. Importovať údaje zo súboru bude možné len po zadaní tohoto istého hesla.", - "Enter passphrase": "Zadajte heslo", + "Enter passphrase": "Zadajte (dlhé) heslo", "Confirm passphrase": "Potvrďte heslo", "Export": "Exportovať", "Import room keys": "Importovať kľúče miestností", @@ -1519,5 +1519,17 @@ "Whether you're using Riot on a device where touch is the primary input mechanism": "Či používate Riot na zariadení, ktorého hlavným vstupným mechanizmom je dotyk (mobil, tablet,...)", "Whether you're using Riot as an installed Progressive Web App": "Či používate Riot ako nainštalovanú Progresívnu Webovú Aplikáciu", "Your user agent": "Identifikátor vášho prehliadača", - "The information being sent to us to help make Riot better includes:": "Informácie, ktoré nám posielate, aby sme zlepšili Riot, zahŕňajú:" + "The information being sent to us to help make Riot better includes:": "Informácie, ktoré nám posielate, aby sme zlepšili Riot, zahŕňajú:", + "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "V miestnosti je neznáma relácia: pokiaľ budete pokračovať bez jej overenia, bude schopná odpočúvať váš hovor.", + "Review Sessions": "Overiť reláciu", + "If you cancel now, you won't complete verifying the other user.": "Pokiaľ teraz proces zrušíte, nedokončíte overenie druhého používateľa.", + "If you cancel now, you won't complete verifying your other session.": "Pokiaľ teraz proces zrušíte, nedokončíte overenie vašej druhej relácie.", + "If you cancel now, you won't complete your operation.": "Pokiaľ teraz proces zrušíte, nedokončíte ho.", + "Cancel entering passphrase?": "Zrušiť zadávanie (dlhého) hesla.", + "Setting up keys": "Príprava kľúčov", + "Verify this session": "Overiť túto reláciu", + "Keep recovery passphrase in memory for this session": "Ponechať (dlhé) heslo pre obnovu zálohy v pamäti pre túto reláciu", + "Enter recovery passphrase": "Zadajte (dlhé) heslo pre obnovu zálohy", + "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Nemožno sa dostať do tajného úložiska. Prosím, overte, že ste zadali správne (dlhé) heslo pre obnovu zálohy.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej zabezpečenej histórií správ a vašemu krížom-podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy." } diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index ee2781f445..7058ad67b0 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2408,5 +2408,21 @@ "or another cross-signing capable Matrix client": "ose një tjetër klient Matrix i aftë për cross-signing), email address or share this room.": "Ftoni dikë duke përdorur emrin e tij, emrin e përdoruesit (bie fjala, ), adresën email ose duke ndarë me të këtë dhomë.", + "Confirm encryption setup": "Ripohoni ujdisje fshehtëzimi", + "Click the button below to confirm setting up encryption.": "Klikoni mbi butonin më poshtë që të ripohoni ujdisjen e fshehtëzimit.", + "Dismiss read marker and jump to bottom": "Mos merr parasysh piketë leximi dhe hidhu te fundi", + "Jump to oldest unread message": "Hidhu te mesazhi më i vjetër i palexuar", + "Upload a file": "Ngarkoni një kartelë" } diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 4eff773d1c..481167a645 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -1443,5 +1443,56 @@ "Verifies a user, session, and pubkey tuple": "验证用户、会话和公钥元组", "Unknown (user, session) pair:": "未知(用户、会话)对:", "Session already verified!": "会话已验证!", - "WARNING: Session already verified, but keys do NOT MATCH!": "警告:会话已验证,但密钥不匹配!" + "WARNING: Session already verified, but keys do NOT MATCH!": "警告:会话已验证,但密钥不匹配!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "警告:密钥验证失败!%(userId)s 的会话 %(deviceId)s 的签名密钥为 %(fprint)s,与提供的密钥 %(fingerprint)s 不符。这可能表示您的通讯已被截获!", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "您提供的签名密钥与您从 %(userId)s 的会话 %(deviceId)s 获取的一致。该会话被标为已验证。", + "Sends the given message coloured as a rainbow": "以彩虹色发送给定消息", + "Sends the given emote coloured as a rainbow": "以彩虹色发送给定表情符号", + "Displays list of commands with usages and descriptions": "显示指令清单与其描述和用法", + "Displays information about a user": "显示关于用户的信息", + "Send a bug report with logs": "发送带日志的错误报告", + "Opens chat with the given user": "与指定用户发起聊天", + "Sends a message to the given user": "向指定用户发消息", + "%(senderName)s made no change.": "%(senderName)s 未做出变更。", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s 将聊天室名称从 %(oldRoomName)s 改为 %(newRoomName)s。", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s 为此聊天室添加备用地址 %(addresses)s。", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s 为此聊天室添加了备用地址 %(addresses)s。", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s 为此聊天室移除了备用地址 %(addresses)s。", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s 为此聊天室移除了备用地址 %(addresses)s。", + "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s 更改了此聊天室的备用地址。", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s 更改了此聊天室的主地址与备用地址。", + "%(senderName)s changed the addresses for this room.": "%(senderName)s 更改了此聊天室的地址。", + "%(senderName)s placed a voice call.": "%(senderName)s 发起了语音通话。", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s 发起了语音通话。(此浏览器不支持)", + "%(senderName)s placed a video call.": "%(senderName)s 发起了视频通话。", + "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s 发起了视频通话。(此浏览器不支持)", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的用户的规则", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的服务器的规则", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的规则", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s 删除了禁止聊天室匹配%(glob)s的规则", + "%(senderName)s updated an invalid ban rule": "%(senderName)s 更新了一个无效的禁止规则", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s 而禁止用户匹配%(glob)s的规则", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止聊天室匹配%(glob)s的规则", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止服务器匹配%(glob)s的规则", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止匹配%(glob)s的规则", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 创建了因为%(reason)s而禁止用户匹配%(glob)s的规则", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止聊天室匹配%(glob)s的规则", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止服务器匹配%(glob)s的规则", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止匹配%(glob)s的股则", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更改了一个由于%(reason)s而禁止用户%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s更改了一个由于%(reason)s而禁止聊天室%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更新了一个由于%(reason)s而禁止服务器%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更新了一个由于%(reason)s而禁止%(oldGlob)s跟%(newGlob)s匹配的规则", + "You signed in to a new session without verifying it:": "您登陆了未经过验证的新会话:", + "Verify your other session using one of the options below.": "使用以下选项之一验证您的其他会话。", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s(%(userId)s)登陆到未验证的新会话:", + "Ask this user to verify their session, or manually verify it below.": "要求该用户验证其会话,或在下面手动进行验证。", + "Not Trusted": "不可信任", + "Manually Verify by Text": "手动验证文字", + "Interactively verify by Emoji": "通过表情符号进行交互式验证", + "Done": "完成", + "Cannot reach homeserver": "不可连接到主服务器", + "Ensure you have a stable internet connection, or get in touch with the server admin": "确保您的网络连接稳定,或与服务器管理员联系", + "Ask your Riot admin to check your config for incorrect or duplicate entries.": "跟您的Riot管理员确认您的配置不正确或重复的条目。", + "Cannot reach identity server": "不可连接到身份服务器" } diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index ba8ffa0fcb..4995eeccb0 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2421,5 +2421,18 @@ "Verify your other session using one of the options below.": "使用下方的其中一個選項來驗證您其他的工作階段。", "Invite someone using their name, username (like ), email address or share this room.": "使用某人的名字、使用者名稱(如 )、電子郵件地址或分享此聊天室來邀請他們。", "Message deleted": "訊息已刪除", - "Message deleted by %(name)s": "訊息已被 %(name)s 刪除" + "Message deleted by %(name)s": "訊息已被 %(name)s 刪除", + "Opens chat with the given user": "開啟與指定使用者的聊天", + "Sends a message to the given user": "傳送訊息給指定的使用者", + "Waiting for your other session to verify…": "正在等待您的其他工作階段進行驗證……", + "You've successfully verified your device!": "您已成功驗證您的裝置!", + "To continue, use Single Sign On to prove your identity.": "要繼續,使用單一登入系統來證明您的身份。", + "Confirm to continue": "確認以繼續", + "Click the button below to confirm your identity.": "點擊下方按鈕以確認您的身份。", + "Confirm encryption setup": "確認加密設定", + "Click the button below to confirm setting up encryption.": "點擊下方按鈕以確認設定加密。", + "QR Code": "QR Code", + "Dismiss read marker and jump to bottom": "取消讀取標記並跳至底部", + "Jump to oldest unread message": "跳至最舊的未讀訊息", + "Upload a file": "上傳檔案" } diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index ee9f703136..77c62ce84d 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -44,7 +44,7 @@ function matrixLinkify(linkify) { const S_HASH = S_START.jump(TT.POUND); const S_HASH_NAME = new linkify.parser.State(); const S_HASH_NAME_COLON = new linkify.parser.State(); - const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State(ROOMALIAS); const S_HASH_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_ROOMALIAS = new linkify.parser.State(ROOMALIAS); const S_ROOMALIAS_COLON = new linkify.parser.State(); @@ -92,7 +92,7 @@ function matrixLinkify(linkify) { const S_AT = S_START.jump(TT.AT); const S_AT_NAME = new linkify.parser.State(); const S_AT_NAME_COLON = new linkify.parser.State(); - const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State(USERID); const S_AT_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_USERID = new linkify.parser.State(USERID); const S_USERID_COLON = new linkify.parser.State(); @@ -138,7 +138,7 @@ function matrixLinkify(linkify) { const S_PLUS = S_START.jump(TT.PLUS); const S_PLUS_NAME = new linkify.parser.State(); const S_PLUS_NAME_COLON = new linkify.parser.State(); - const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(GROUPID); const S_PLUS_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_GROUPID = new linkify.parser.State(GROUPID); const S_GROUPID_COLON = new linkify.parser.State(); diff --git a/src/mjolnir/Mjolnir.js b/src/mjolnir/Mjolnir.js index 5836ffd57a..9876cb1f7f 100644 --- a/src/mjolnir/Mjolnir.js +++ b/src/mjolnir/Mjolnir.js @@ -18,7 +18,7 @@ import {MatrixClientPeg} from "../MatrixClientPeg"; import {ALL_RULE_TYPES, BanList} from "./BanList"; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; import {_t} from "../languageHandler"; -import dis from "../dispatcher"; +import dis from "../dispatcher/dispatcher"; // TODO: Move this and related files to the js-sdk or something once finalized. diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index e5027e0d37..9f9d7898cb 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -133,7 +133,6 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp body.append("cross_signing_supported_by_hs", String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"))); body.append("cross_signing_ready", String(await client.isCrossSigningReady())); - body.append("ssss_key_needs_upgrade", String(await client.secretStorageKeyNeedsUpgrade())); } } diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 0122916bc3..36111dd46f 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -24,7 +24,7 @@ import RoomSettingsHandler from "./handlers/RoomSettingsHandler"; import ConfigSettingsHandler from "./handlers/ConfigSettingsHandler"; import {_t} from '../languageHandler'; import SdkConfig from "../SdkConfig"; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {SETTINGS} from "./Settings"; import LocalEchoWrapper from "./handlers/LocalEchoWrapper"; import {WatchManager} from "./WatchManager"; diff --git a/src/settings/controllers/CustomStatusController.js b/src/settings/controllers/CustomStatusController.js index 0fc6619d92..031387bb6a 100644 --- a/src/settings/controllers/CustomStatusController.js +++ b/src/settings/controllers/CustomStatusController.js @@ -15,7 +15,7 @@ limitations under the License. */ import SettingController from "./SettingController"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; export default class CustomStatusController extends SettingController { onChange(level, roomId, newValue) { diff --git a/src/stores/CustomRoomTagStore.js b/src/stores/CustomRoomTagStore.js index 909282c085..c67868e2c6 100644 --- a/src/stores/CustomRoomTagStore.js +++ b/src/stores/CustomRoomTagStore.js @@ -13,7 +13,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import * as RoomNotifs from '../RoomNotifs'; import RoomListStore from './RoomListStore'; import EventEmitter from 'events'; diff --git a/src/stores/GroupStore.js b/src/stores/GroupStore.js index 78a144f755..d4097184a1 100644 --- a/src/stores/GroupStore.js +++ b/src/stores/GroupStore.js @@ -18,7 +18,7 @@ import EventEmitter from 'events'; import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups'; import FlairStore from './FlairStore'; import {MatrixClientPeg} from '../MatrixClientPeg'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; function parseMembersResponse(response) { return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember)); diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 904f29f7b3..a12bac7dd6 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -15,7 +15,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; const INITIAL_STATE = { diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js index 3a5605ba3f..a73f3befbb 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 89edc9a8ef..d7b6759195 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {Store} from 'flux/utils'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import DMRoomMap from '../utils/DMRoomMap'; import * as Unread from '../Unread'; import SettingsStore from "../settings/SettingsStore"; diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 841734dfb7..3d82d086d7 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -15,7 +15,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; import {MatrixClientPeg} from '../MatrixClientPeg'; import * as sdk from '../index'; diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index f38bc046d0..096811940c 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -14,7 +14,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; const INITIAL_STATE = { diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js index c05728e497..2acf531d86 100644 --- a/src/stores/TagOrderStore.js +++ b/src/stores/TagOrderStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {Store} from 'flux/utils'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import GroupStore from './GroupStore'; import Analytics from '../Analytics'; import * as RoomNotifs from "../RoomNotifs"; diff --git a/src/theme.js b/src/theme.js index 2ccce81a8d..1da39d50fa 100644 --- a/src/theme.js +++ b/src/theme.js @@ -19,7 +19,7 @@ import {_t} from "./languageHandler"; export const DEFAULT_THEME = "light"; import Tinter from "./Tinter"; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; import ThemeController from "./settings/controllers/ThemeController"; diff --git a/src/utils/MessageDiffUtils.js b/src/utils/MessageDiffUtils.js index de0d8fdc89..7398173fdd 100644 --- a/src/utils/MessageDiffUtils.js +++ b/src/utils/MessageDiffUtils.js @@ -77,8 +77,6 @@ function findRefNodes(root, route, isAddition) { const end = isAddition ? route.length - 1 : route.length; for (let i = 0; i < end; ++i) { refParentNode = refNode; - // Lists don't have appropriate child nodes we can use. - if (!refNode.childNodes[route[i]]) continue; refNode = refNode.childNodes[route[i]]; } return {refNode, refParentNode}; @@ -190,10 +188,11 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { break; } case "addTextElement": { - if (diff.value !== "\n") { - const insNode = wrapInsertion(stringAsTextNode(diff.value)); - insertBefore(refParentNode, refNode, insNode); - } + // XXX: sometimes diffDOM says insert a newline when there shouldn't be one + // but we must insert the node anyway so that we don't break the route child IDs. + // See https://github.com/fiduswriter/diffDOM/issues/100 + const insNode = wrapInsertion(stringAsTextNode(diff.value !== "\n" ? diff.value : "")); + insertBefore(refParentNode, refNode, insNode); break; } // e.g. when changing a the href of a link, diff --git a/src/utils/PasswordScorer.js b/src/utils/PasswordScorer.ts similarity index 98% rename from src/utils/PasswordScorer.js rename to src/utils/PasswordScorer.ts index 9d89942bf5..d8f3b0fb96 100644 --- a/src/utils/PasswordScorer.js +++ b/src/utils/PasswordScorer.ts @@ -63,7 +63,7 @@ _td("Short keyboard patterns are easy to guess"); * @param {string} password Password to score * @returns {object} Score result with `score` and `feedback` properties */ -export function scorePassword(password) { +export function scorePassword(password: string) { if (password.length === 0) return null; const userInputs = ZXCVBN_USER_INPUTS.slice(); diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index ad4c02887e..35e23f0429 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -18,7 +18,7 @@ limitations under the License. import {MatrixClientPeg} from '../MatrixClientPeg'; import SdkConfig from "../SdkConfig"; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import * as url from "url"; import WidgetEchoStore from '../stores/WidgetEchoStore'; diff --git a/src/verification.js b/src/verification.js index f488b2ebeb..289ac9544b 100644 --- a/src/verification.js +++ b/src/verification.js @@ -15,7 +15,7 @@ limitations under the License. */ import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 8dc4647920..235ed61016 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -9,7 +9,7 @@ import {MatrixClientPeg} from '../../../../src/MatrixClientPeg'; import sdk from '../../../skinned-sdk'; import { DragDropContext } from 'react-beautiful-dnd'; -import dis from '../../../../src/dispatcher'; +import dis from '../../../../src/dispatcher/dispatcher'; import DMRoomMap from '../../../../src/utils/DMRoomMap.js'; import GroupStore from '../../../../src/stores/GroupStore.js'; diff --git a/test/test-utils.js b/test/test-utils.js index d7aa9d5de9..2d7c1bd62c 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -2,7 +2,7 @@ import React from 'react'; import {MatrixClientPeg as peg} from '../src/MatrixClientPeg'; -import dis from '../src/dispatcher'; +import dis from '../src/dispatcher/dispatcher'; import {makeType} from "../src/utils/TypeUtils"; import {ValidatedServerConfig} from "../src/utils/AutoDiscoveryUtils"; import ShallowRenderer from 'react-test-renderer/shallow'; diff --git a/tsconfig.json b/tsconfig.json index b87f640734..8a01ca335e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "jsx": "react", "types": [ "node", - "react" + "react", + "flux" ] }, "include": [ diff --git a/yarn.lock b/yarn.lock index 520e976b17..93118dab22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1218,6 +1218,19 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/fbemitter@*": + version "2.0.32" + resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c" + integrity sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw= + +"@types/flux@^3.1.9": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@types/flux/-/flux-3.1.9.tgz#ddfc9641ee2e2e6cb6cd730c6a48ef82e2076711" + integrity sha512-bSbDf4tTuN9wn3LTGPnH9wnSSLtR5rV7UPWFpM00NJ1pSTBwCzeZG07XsZ9lBkxwngrqjDtM97PLt5IuIdCQUA== + dependencies: + "@types/fbemitter" "*" + "@types/react" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -1272,6 +1285,21 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/qrcode@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" + integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== + dependencies: + "@types/node" "*" + +"@types/react@*": + version "16.9.35" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368" + integrity sha512-q0n0SsWcGc8nDqH2GJfWQWUOmZSJhXV64CjVN5SvcNti3TdEaA3AH0D8DwNmMdzjMAC/78tB8nAZIlV8yTz+zQ== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/react@16.9": version "16.9.32" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" @@ -1318,6 +1346,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/zxcvbn@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.0.tgz#fbc1d941cc6d9d37d18405c513ba6b294f89b609" + integrity sha512-GQLOT+SN20a+AI51y3fAimhyTF4Y0RG+YP3gf91OibIZ7CJmPFgoZi+ZR5a+vRbS01LbQosITWum4ATmJ1Z6Pg== + "@typescript-eslint/experimental-utils@^2.5.0": version "2.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a" @@ -5721,8 +5754,8 @@ mathml-tag-names@^2.0.1: integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "6.0.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f120533fadb309ac5dc8b2bcb6882e784ba93f18" + version "6.1.0" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e3c6a0e1a08a3812ba988e60eb5a2a013bb27404" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" @@ -6851,18 +6884,6 @@ pvutils@latest: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.0.17.tgz#ade3c74dfe7178944fe44806626bd2e249d996bf" integrity sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ== -qr.js@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" - integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8= - -qrcode-react@^0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/qrcode-react/-/qrcode-react-0.1.16.tgz#d064999d510ffc3e55a9ca3ffcf6c203c69f1517" - integrity sha512-FK+QCfFqCQMSxUE1byzglERJQkwKqXYvYMCS+/Ad2zACJOfoHkHHtRqsQQPji7lfb1y1qCXLvL+3eP1hAfg8Ng== - dependencies: - qr.js "0.0.0" - qrcode@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"