diff --git a/res/css/_common.scss b/res/css/_common.scss index aaefb859e4..4399fb224e 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -50,8 +50,54 @@ a:visited { color: $accent-color-alt; } +input[type=text], +input[type=search], +input[type=password] { + padding: 9px; + font-family: $font-family; + font-size: 14px; + font-weight: 600; + min-width: 0; +} + +input[type=text].mx_textinput_icon, +input[type=search].mx_textinput_icon { + padding-left: 36px; + background-repeat: no-repeat; + background-position: 10px center; +} + +// FIXME THEME - Tint by CSS rather than referencing a duplicate asset +input[type=text].mx_textinput_icon.mx_textinput_search, +input[type=search].mx_textinput_icon.mx_textinput_search { + background-image: url('$(res)/img/feather-icons/search-input.svg'); +} + +// dont search UI as not all browsers support it, +// we implement it ourselves where needed instead +input[type=search]::-webkit-search-decoration, +input[type=search]::-webkit-search-cancel-button, +input[type=search]::-webkit-search-results-button, +input[type=search]::-webkit-search-results-decoration { + display: none; +} + +.input[type=text]::-webkit-input-placeholder, +.input[type=text]::-moz-placeholder, +.input[type=search]::-webkit-input-placeholder, +.input[type=search]::-moz-placeholder { + color: #a5aab2; +} + +// Override Firefox's UA style so we get a consistent look across browsers +input::placeholder, +textarea::placeholder { + opacity: initial; +} + input[type=text], input[type=password], textarea { background-color: transparent; + color: $primary-fg-color; } input[type=text]:focus, input[type=password]:focus, textarea:focus { @@ -62,6 +108,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { /* Required by Firefox */ textarea { font-family: $font-family; + color: $primary-fg-color; } /* Prevent ugly dotted highlight around selected elements in Firefox */ @@ -242,7 +289,7 @@ textarea { font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; } .mx_Dialog button:hover, .mx_Dialog input[type="submit"]:hover { diff --git a/res/css/_components.scss b/res/css/_components.scss index 57a34023c0..18c3597db0 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,6 +7,7 @@ @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; @import "./structures/_GroupView.scss"; +@import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; @import "./structures/_MatrixChat.scss"; @@ -57,6 +58,7 @@ @import "./views/dialogs/_DevtoolsDialog.scss"; @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; +@import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_RestoreKeyBackupDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @@ -126,6 +128,7 @@ @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; +@import "./views/rooms/_RoomBreadcrumbs.scss"; @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss index cb0d6f7f09..0e1faf727d 100644 --- a/res/css/structures/_AutoHideScrollbar.scss +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -74,6 +74,7 @@ body.mx_scrollbar_nooverlay { // or fallback for webkit browsers ::-webkit-scrollbar { width: 6px; + height: 6px; background-color: $scrollbar-track-color; } diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index 001c405e15..3788929bf3 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -31,7 +31,7 @@ limitations under the License. .mx_ContextualMenu { border-radius: 4px; - box-shadow: 4px 4px 12px 0 rgba(118, 131, 156, 0.6);; + box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; background-color: $menu-bg-color; color: $primary-fg-color; position: absolute; diff --git a/res/css/structures/_GroupView.scss b/res/css/structures/_GroupView.scss index ace310ee5b..1c477a959a 100644 --- a/res/css/structures/_GroupView.scss +++ b/res/css/structures/_GroupView.scss @@ -44,13 +44,22 @@ limitations under the License. } .mx_GroupHeader_button { - margin-left: 12px; + margin-left: 5px; + margin-right: 5px; cursor: pointer; + height: 20px; + width: 20px; + background-color: $groupheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_GroupHeader_button object { - // prevents clicks from being swallowed by svg in 'object' tag - pointer-events: none; +.mx_GroupHeader_editButton { + mask-image: url('$(res)/img/icons-settings-room.svg'); +} + +.mx_GroupHeader_shareButton { + mask-image: url('$(res)/img/icons-share.svg'); } .mx_GroupView_editable { diff --git a/res/css/structures/_HeaderButtons.scss b/res/css/structures/_HeaderButtons.scss new file mode 100644 index 0000000000..eef7653b24 --- /dev/null +++ b/res/css/structures/_HeaderButtons.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 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. +*/ + +.mx_HeaderButtons { + display: flex; +} + +.mx_HeaderButtons::before { + content: ""; + background-color: $header-divider-color; + opacity: 0.5; + margin: 0 15px; + border-radius: 1px; + width: 1px; +} diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index 592eea067e..474040a626 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -25,9 +25,7 @@ limitations under the License. .mx_RightPanel_header { order: 1; - border-bottom: 1px solid $primary-hairline-color; - flex: 0 0 52px; } @@ -45,20 +43,55 @@ limitations under the License. cursor: pointer; flex: 0 0 auto; vertical-align: top; - margin-top: 4px; - padding-left: 5px; - padding-right: 5px; + margin-left: 5px; + margin-right: 5px; text-align: center; - position: relative; border-bottom: 2px solid transparent; + height: 20px; + width: 20px; + position: relative; } -.mx_RightPanel_headerButton object { - pointer-events: none; +.mx_RightPanel_headerButton::before { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: $rightpanel-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_RightPanel_headerButton_highlight { - border-color: $button-bg-color; +.mx_RightPanel_membersButton::before { + mask-image: url('$(res)/img/feather-icons/user.svg'); +} + +.mx_RightPanel_filesButton::before { + mask-image: url('$(res)/img/feather-icons/files.svg'); +} + +.mx_RightPanel_notifsButton::before { + mask-image: url('$(res)/img/feather-icons/notifications.svg'); +} + +.mx_RightPanel_groupMembersButton::before { + mask-image: url('$(res)/img/icons-people.svg'); +} + +.mx_RightPanel_roomsButton::before { + mask-image: url('$(res)/img/icons-room-nobg.svg'); +} + +.mx_RightPanel_headerButton_highlight::after { + content: ''; + position: absolute; + bottom: -6px; + left: 0; + right: 0; + height: 2px; + background-color: $button-bg-color; } .mx_RightPanel_headerButton_badge { diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 9791be902f..f76df1f683 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -152,7 +152,7 @@ limitations under the License. &.mx_IndicatorScrollbar_topOverflow::before { top: 0; transition: background-image 0.1s ease-in; - background: linear-gradient(to top, rgba(242,245,248,0), rgba(242,245,248,1)); + background: linear-gradient(to top, $panel-gradient); } /* diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index fb4df53d52..6e435b8e75 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -28,8 +28,8 @@ limitations under the License. } .mx_TabbedView_tabLabels { - width: 150px; - max-width: 150px; + width: 170px; + max-width: 170px; color: $tab-label-fg-color; position: fixed; } @@ -39,9 +39,8 @@ limitations under the License. cursor: pointer; display: block; border-radius: 3px; - font-size: 12px; - font-weight: 600; - min-height: 20px; // use min-height instead of height to allow the label to overflow a bit + font-size: 14px; + min-height: 24px; // use min-height instead of height to allow the label to overflow a bit margin-bottom: 6px; position: relative; } @@ -55,8 +54,8 @@ limitations under the License. margin-left: 6px; margin-right: 9px; margin-top: 1px; - width: 14px; - height: 14px; + width: 16px; + height: 16px; display: inline-block; } @@ -64,9 +63,9 @@ limitations under the License. display: inline-block; background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; - mask-size: 14px; + mask-size: 16px; width: 14px; - height: 18px; + height: 22px; mask-position: center; content: ''; vertical-align: middle; @@ -81,7 +80,7 @@ limitations under the License. } .mx_TabbedView_tabPanel { - margin-left: 220px; // 150px sidebar + 70px padding + margin-left: 240px; // 170px sidebar + 70px padding flex-grow: 1; display: flex; flex-direction: column; diff --git a/res/css/structures/_TagPanelButtons.scss b/res/css/structures/_TagPanelButtons.scss index f7cb189e25..323d77a405 100644 --- a/res/css/structures/_TagPanelButtons.scss +++ b/res/css/structures/_TagPanelButtons.scss @@ -38,7 +38,7 @@ limitations under the License. height: 40px; width: 40px; border-radius: 20px; - background-color: $roomheader-addroom-color; + background-color: $tagpanel-button-color; position: relative; /* overwrite mx_RoleButton inline-block */ display: block !important; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index b48021c27a..d4b5e7402c 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -79,7 +79,7 @@ limitations under the License. .mx_Login_type_container { display: flex; margin-bottom: 14px; - color: $primary-fg-color; + color: $authpage-primary-color; } .mx_Login_type_label { diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index fd54cd135a..6216bdd4b8 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -21,19 +21,41 @@ limitations under the License. padding: 25px 60px; box-sizing: border-box; font-size: 12px; - color: $authpage-body-color; + color: $authpage-secondary-color; } .mx_AuthBody h2 { font-size: 24px; font-weight: 600; margin-top: 8px; + color: $authpage-primary-color; } .mx_AuthBody h3 { font-size: 14px; font-weight: 600; - color: $primary-fg-color; + color: $authpage-primary-color; +} + +.mx_AuthBody input[type=text], +.mx_AuthBody input[type=password] { + color: $authpage-primary-color; +} + +.mx_AuthBody .mx_Field input, +.mx_AuthBody .mx_Field select { + color: $authpage-primary-color; + background-color: $authpage-body-bg-color; +} + +.mx_AuthBody .mx_Field label { + color: $authpage-primary-color; +} + +.mx_AuthBody .mx_Field input:focus + label, +.mx_AuthBody .mx_Field input:not(:placeholder-shown) + label, +.mx_AuthBody .mx_Field select + label /* Always show a select's label on top to not collide with the value */ { + background-color: $authpage-body-bg-color; } .mx_AuthBody_editServerDetails { diff --git a/res/css/views/auth/_ServerTypeSelector.scss b/res/css/views/auth/_ServerTypeSelector.scss index 03f5386501..ed781726b7 100644 --- a/res/css/views/auth/_ServerTypeSelector.scss +++ b/res/css/views/auth/_ServerTypeSelector.scss @@ -34,7 +34,7 @@ limitations under the License. .mx_ServerTypeSelector_label { text-align: center; font-weight: 600; - color: $primary-fg-color; + color: $authpage-primary-color; margin: 8px 0; } @@ -54,7 +54,7 @@ limitations under the License. height: 18px; margin-bottom: 12px; font-weight: 600; - color: $primary-fg-color; + color: $authpage-primary-color; } .mx_ServerTypeSelector_logo > div { diff --git a/res/css/views/context_menus/_TopLeftMenu.scss b/res/css/views/context_menus/_TopLeftMenu.scss index f305f0fae3..b3ef703144 100644 --- a/res/css/views/context_menus/_TopLeftMenu.scss +++ b/res/css/views/context_menus/_TopLeftMenu.scss @@ -31,10 +31,18 @@ limitations under the License. mask-image: url('$(res)/img/feather-icons/home.svg'); } + li.mx_TopLeftMenu_icon_welcome::after { + mask-image: url('$(res)/img/feather-icons/gift.svg'); + } + li.mx_TopLeftMenu_icon_settings::after { mask-image: url('$(res)/img/feather-icons/settings.svg'); } + li.mx_TopLeftMenu_icon_signin::after { + mask-image: url('$(res)/img/feather-icons/sign-in.svg'); + } + li.mx_TopLeftMenu_icon_signout::after { mask-image: url('$(res)/img/feather-icons/sign-out.svg'); } diff --git a/res/css/views/dialogs/_CreateGroupDialog.scss b/res/css/views/dialogs/_CreateGroupDialog.scss index 500e12ee49..128eacc3ce 100644 --- a/res/css/views/dialogs/_CreateGroupDialog.scss +++ b/res/css/views/dialogs/_CreateGroupDialog.scss @@ -43,10 +43,9 @@ limitations under the License. .mx_CreateGroupDialog_prefix, .mx_CreateGroupDialog_suffix { - height: 35px; padding: 0px 5px; line-height: 37px; - background-color: $input-border-color; + background-color: $input-darker-bg-color; border: 1px solid $input-border-color; text-align: center; } diff --git a/res/css/views/dialogs/_IncomingSasDialog.scss b/res/css/views/dialogs/_IncomingSasDialog.scss new file mode 100644 index 0000000000..3a9d645a98 --- /dev/null +++ b/res/css/views/dialogs/_IncomingSasDialog.scss @@ -0,0 +1,24 @@ +/* +Copyright 2019 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. +*/ + +.mx_IncomingSasDialog_opponentProfile_image { + position: relative; +} + +.mx_IncomingSasDialog_opponentProfile h2 { + display: inline-block; + margin-left: 10px; +} diff --git a/res/css/views/dialogs/_SettingsDialog.scss b/res/css/views/dialogs/_SettingsDialog.scss index 4a9708f6d1..abf0048cfd 100644 --- a/res/css/views/dialogs/_SettingsDialog.scss +++ b/res/css/views/dialogs/_SettingsDialog.scss @@ -16,8 +16,8 @@ limitations under the License. .mx_SettingsDialog { .mx_Dialog { - max-width: 900px; - width: 80%; + max-width: 1000px; + width: 90%; height: 80%; border-radius: 4px; padding-top: 0; @@ -30,7 +30,7 @@ limitations under the License. .mx_TabbedView .mx_SettingsTab { box-sizing: border-box; - min-width: 550px; + min-width: 580px; padding-right: 130px; // Put some padding on the bottom to avoid the settings tab from diff --git a/res/css/views/dialogs/_UnknownDeviceDialog.scss b/res/css/views/dialogs/_UnknownDeviceDialog.scss index a924704769..02e0fb1fe5 100644 --- a/res/css/views/dialogs/_UnknownDeviceDialog.scss +++ b/res/css/views/dialogs/_UnknownDeviceDialog.scss @@ -28,28 +28,28 @@ limitations under the License. flex-direction: column; } +.mx_UnknownDeviceDialog ul { + list-style: none; + padding: 0; +} +// userid +.mx_UnknownDeviceDialog p { + font-weight: bold; + font-size: 16px; +} + .mx_UnknownDeviceDialog .mx_DeviceVerifyButtons { - float: right; + flex-direction: row !important; } .mx_UnknownDeviceDialog .mx_Dialog_content { margin-bottom: 24px; } -.mx_UnknownDeviceDialog .mx_MemberDeviceInfo { - float: right; - clear: both; - padding: 0px; - padding-top: 8px; +.mx_UnknownDeviceDialog_deviceList > li { + padding: 4px; } -.mx_UnknownDeviceDialog .mx_MemberDeviceInfo_textButton { - @mixin mx_DialogButton_small; - background-color: $primary-bg-color; - color: $accent-color; -} - -.mx_UnknownDeviceDialog .mx_UnknownDeviceDialog_deviceList li { - height: 40px; - border-bottom: 1px solid $primary-hairline-color; +.mx_UnknownDeviceDialog_deviceList > li > * { + padding-bottom: 0; } diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 8de0b82554..25ad51a3fb 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -40,8 +40,7 @@ limitations under the License. } .mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled { - color: $button-primary-disabled-fg-color; - background-color: $button-primary-disabled-bg-color; + opacity: 0.4; } .mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_primary_sm { @@ -51,8 +50,7 @@ limitations under the License. } .mx_AccessibleButton_kind_primary_sm.mx_AccessibleButton_disabled { - color: $button-primary-disabled-fg-color; - background-color: $button-primary-disabled-bg-color; + opacity: 0.4; } .mx_AccessibleButton_kind_danger { @@ -74,4 +72,4 @@ limitations under the License. .mx_AccessibleButton_kind_danger_sm.mx_AccessibleButton_disabled { color: $button-danger-disabled-fg-color; background-color: $button-danger-disabled-bg-color; -} \ No newline at end of file +} diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 6ce6b33a05..075bd28a11 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* TODO: Consider unifying with general input styles in _dharma.scss */ +/* TODO: Consider unifying with general input styles in _light.scss */ .mx_Field { position: relative; @@ -31,6 +31,7 @@ limitations under the License. transition: border-color 0.25s; border: 1px solid $input-border-color; padding: 8px 9px; + color: $primary-fg-color; background-color: $primary-bg-color; } diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 8094062628..f42b5eaa6b 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -14,6 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* +the tile title bar is 5 (top border) + 12 (title, buttons) + 5 (bottom padding) px = 22px +the body is assumed to be 300px (assumed by at least the sticker pickerm, perhaps elsewhere), +so the body height would be 300px - 22px (room for title bar) = 278px +BUT! the sticker picker also assumes it's a little less high than that because the iframe +for the sticker picker doesn't have any padding or margin on it's bottom. +so subtracking another 5px, which brings us at 273px. +*/ +$AppsDrawerBodyHeight: 273px; + .mx_AppsDrawer { margin: 5px; } @@ -83,7 +93,7 @@ limitations under the License. } .mx_AppTile_persistedWrapper { - height: 280px; + height: $AppsDrawerBodyHeight; } .mx_AppTile_mini .mx_AppTile_persistedWrapper { @@ -189,7 +199,7 @@ limitations under the License. } .mx_AppTileBody{ - height: 280px; + height: $AppsDrawerBodyHeight; width: 100%; overflow: hidden; } @@ -208,7 +218,7 @@ limitations under the License. .mx_AppTileBody iframe { width: 100%; - height: 280px; + height: $AppsDrawerBodyHeight; overflow: hidden; border: none; padding: 0; @@ -332,7 +342,7 @@ form.mx_Custom_Widget_Form div { align-items: center; font-weight: bold; position: relative; - height: 280px; + height: $AppsDrawerBodyHeight; } .mx_AppLoading .mx_Spinner { diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 7c9a6babea..a8f34565b5 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -533,7 +533,7 @@ limitations under the License. } .mx_EventTile_e2eIcon { - top: 7px; + top: 3px; } .mx_EventTile_editButton { diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 7ee7efcaff..db8eb4995d 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -180,27 +180,36 @@ limitations under the License. color: $accent-color; } -.mx_MessageComposer_upload, -.mx_MessageComposer_hangup, -.mx_MessageComposer_voicecall, -.mx_MessageComposer_videocall, -.mx_MessageComposer_apps, -.mx_MessageComposer_stickers { - /*display: table-cell;*/ - /*vertical-align: middle;*/ - /*padding-left: 10px;*/ - padding-right: 12px; +.mx_MessageComposer_button { + margin-right: 12px; cursor: pointer; padding-top: 4px; + height: 20px; + width: 20px; + background-color: $composer-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; } -.mx_MessageComposer_upload object, -.mx_MessageComposer_hangup object, -.mx_MessageComposer_voicecall object, -.mx_MessageComposer_videocall object, -.mx_MessageComposer_apps object, -.mx_MessageComposer_stickers object { - pointer-events: none; +.mx_MessageComposer_upload { + mask-image: url('$(res)/img/feather-icons/paperclip.svg'); +} + +.mx_MessageComposer_hangup { + mask-image: url('$(res)/img/hangup.svg'); +} + +.mx_MessageComposer_voicecall { + mask-image: url('$(res)/img/feather-icons/phone.svg'); +} + +.mx_MessageComposer_videocall { + mask-image: url('$(res)/img/feather-icons/video.svg'); +} + +.mx_MessageComposer_stickers { + mask-image: url('$(res)/img/feather-icons/face.svg'); } .mx_MessageComposer_formatting { diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss new file mode 100644 index 0000000000..c5a149d5cd --- /dev/null +++ b/res/css/views/rooms/_RoomBreadcrumbs.scss @@ -0,0 +1,54 @@ +/* +Copyright 2019 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. +*/ + +.mx_RoomBreadcrumbs { + overflow-x: auto; + position: relative; + height: 32px; + margin: 8px; + margin-bottom: 0; + + overflow-x: hidden; + display: flex; + flex-direction: row; + + > * { + margin-left: 4px; + } + + &::after { + content: ""; + position: absolute; + width: 15px; + top: 0; + right: 0; + height: 100%; + background: linear-gradient(to right, $panel-gradient); + } + + .mx_RoomBreadcrumbs_animate { + margin-left: 0; + transition: transform 0.3s, width 0.3s; + width: 32px; + transform: scale(1); + } + + .mx_RoomBreadcrumbs_preAnimate { + width: 0; + transform: scale(0); + } +} + diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 1359bc5f57..93f984e65b 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -58,7 +58,6 @@ limitations under the License. .mx_RoomHeader_buttons { display: flex; - align-items: center; background-color: $primary-bg-color; padding-right: 5px; } @@ -116,10 +115,6 @@ limitations under the License. opacity: 0.6; } -.mx_RoomHeader_settingsButton object { - pointer-events: none; -} - .mx_RoomHeader_name, .mx_RoomHeader_avatar, .mx_RoomHeader_avatarPicker, @@ -160,6 +155,7 @@ limitations under the License. font-weight: 400; font-size: 13px; margin: 0 7px; + margin-top: 4px; // to align baseline of topic with room name overflow: hidden; text-overflow: ellipsis; border-bottom: 1px solid transparent; @@ -199,10 +195,32 @@ limitations under the License. .mx_RoomHeader_button { margin-left: 10px; cursor: pointer; + height: 20px; + width: 20px; + background-color: $roomheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_RoomHeader_button object { - pointer-events: none; +.mx_RoomHeader_settingsButton { + mask-image: url('$(res)/img/feather-icons/settings.svg'); +} + +.mx_RoomHeader_forgetButton { + mask-image: url('$(res)/img/leave.svg'); + width: 26px; +} + +.mx_RoomHeader_searchButton { + mask-image: url('$(res)/img/feather-icons/search.svg'); +} + +.mx_RoomHeader_shareButton { + mask-image: url('$(res)/img/feather-icons/share.svg'); +} + +.mx_RoomHeader_manageIntegsButton { + mask-image: url('$(res)/img/feather-icons/grid.svg'); } .mx_RoomHeader_showPanel { @@ -219,6 +237,7 @@ limitations under the License. .mx_RoomHeader_pinnedButton { position: relative; + mask-image: url('$(res)/img/icons-pin.svg'); } .mx_RoomHeader_pinsIndicator { diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 220790784e..6b2e2573e5 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -154,9 +154,8 @@ limitations under the License. } .mx_RoomTile_unread, .mx_RoomTile_highlight { - font-weight: 700 ! important; - .mx_RoomTile_name { + // font-weight: 700; // bold is too loud in the end color: $roomtile-selected-color; } } @@ -176,7 +175,7 @@ limitations under the License. } .mx_RoomTile:focus { - filter: none ! important; + filter: none !important; background-color: $roomtile-focused-bg-color; } diff --git a/res/css/views/rooms/_Stickers.scss b/res/css/views/rooms/_Stickers.scss index 669ca13545..d33ecc0bb6 100644 --- a/res/css/views/rooms/_Stickers.scss +++ b/res/css/views/rooms/_Stickers.scss @@ -7,8 +7,12 @@ height: 300px; } -.mx_Stickers_content .mx_AppTileFullWidth { - border: none; +#mx_persistedElement_stickerPicker .mx_AppTileFullWidth { + height: unset; + box-sizing: border-box; + border-left: none; + border-right: none; + border-bottom: none; } .mx_Stickers_contentPlaceholder { diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index a899aec0fa..7fd8bceb50 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -21,7 +21,7 @@ limitations under the License. } .mx_SettingsTab_subheading { - font-size: 14px; + font-size: 16px; display: block; font-family: $font-family; font-weight: 600; @@ -32,7 +32,7 @@ limitations under the License. .mx_SettingsTab_subsectionText { color: $settings-subsection-fg-color; - font-size: 12px; + font-size: 14px; padding-bottom: 12px; display: block; margin: 0 100px 0 0; // Align with the rest of the view @@ -40,16 +40,17 @@ limitations under the License. .mx_SettingsTab_section .mx_SettingsFlag { margin-right: 100px; - height: 25px; margin-bottom: 10px; } .mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_label { - vertical-align: bottom; + vertical-align: middle; display: inline-block; - font-size: 12px; + font-size: 14px; color: $primary-fg-color; max-width: calc(100% - 48px); // Force word wrap instead of colliding with the switch + box-sizing: border-box; + padding-right: 10px; } .mx_SettingsTab_section .mx_SettingsFlag .mx_ToggleSwitch { diff --git a/res/img/feather-icons/e2e/blacklisted.svg b/res/img/feather-icons/e2e/blacklisted.svg index 63897c2227..ac99d23f05 100644 --- a/res/img/feather-icons/e2e/blacklisted.svg +++ b/res/img/feather-icons/e2e/blacklisted.svg @@ -2,5 +2,5 @@ - + diff --git a/res/img/feather-icons/e2e/verified.svg b/res/img/feather-icons/e2e/verified.svg index f143f854e6..459a552a40 100644 --- a/res/img/feather-icons/e2e/verified.svg +++ b/res/img/feather-icons/e2e/verified.svg @@ -1,3 +1,3 @@ - + diff --git a/res/img/feather-icons/e2e/warning.svg b/res/img/feather-icons/e2e/warning.svg index e6c246dba9..3d5fba550c 100644 --- a/res/img/feather-icons/e2e/warning.svg +++ b/res/img/feather-icons/e2e/warning.svg @@ -2,5 +2,5 @@ - + diff --git a/res/img/feather-icons/face.svg b/res/img/feather-icons/face.svg index 0a359b2dea..a8ca856b67 100644 --- a/res/img/feather-icons/face.svg +++ b/res/img/feather-icons/face.svg @@ -3,9 +3,9 @@ - - - + + + diff --git a/res/img/feather-icons/files.svg b/res/img/feather-icons/files.svg index c66d9ad121..e3bfe30ab0 100644 --- a/res/img/feather-icons/files.svg +++ b/res/img/feather-icons/files.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/gift.svg b/res/img/feather-icons/gift.svg new file mode 100644 index 0000000000..c225b50f9d --- /dev/null +++ b/res/img/feather-icons/gift.svg @@ -0,0 +1 @@ + diff --git a/res/img/feather-icons/grid.svg b/res/img/feather-icons/grid.svg index e6912b0cc7..4f7ab30d97 100644 --- a/res/img/feather-icons/grid.svg +++ b/res/img/feather-icons/grid.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/life-buoy.svg b/res/img/feather-icons/life-buoy.svg index 20bd0f0b5d..6318f7df9a 100644 --- a/res/img/feather-icons/life-buoy.svg +++ b/res/img/feather-icons/life-buoy.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/notifications.svg b/res/img/feather-icons/notifications.svg index 2fe85e810c..a590031ac3 100644 --- a/res/img/feather-icons/notifications.svg +++ b/res/img/feather-icons/notifications.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/paperclip.svg b/res/img/feather-icons/paperclip.svg index ed2bb88681..74a90e0fa3 100644 --- a/res/img/feather-icons/paperclip.svg +++ b/res/img/feather-icons/paperclip.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/phone.svg b/res/img/feather-icons/phone.svg index 58b257f113..85661c5320 100644 --- a/res/img/feather-icons/phone.svg +++ b/res/img/feather-icons/phone.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/search-input.svg b/res/img/feather-icons/search-input.svg index 3be5acb32e..028b84d559 100644 --- a/res/img/feather-icons/search-input.svg +++ b/res/img/feather-icons/search-input.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/search.svg b/res/img/feather-icons/search.svg index 8b14246f64..9ce0724ea7 100644 --- a/res/img/feather-icons/search.svg +++ b/res/img/feather-icons/search.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/settings.svg b/res/img/feather-icons/settings.svg index ea7ce5c55b..a65a15a75f 100644 --- a/res/img/feather-icons/settings.svg +++ b/res/img/feather-icons/settings.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/share.svg b/res/img/feather-icons/share.svg index a012e1b7a5..7098af58aa 100644 --- a/res/img/feather-icons/share.svg +++ b/res/img/feather-icons/share.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/sign-in.svg b/res/img/feather-icons/sign-in.svg new file mode 100644 index 0000000000..9fe617eee1 --- /dev/null +++ b/res/img/feather-icons/sign-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/img/feather-icons/sign-out.svg b/res/img/feather-icons/sign-out.svg index 06ab9903ec..172eb98afb 100644 --- a/res/img/feather-icons/sign-out.svg +++ b/res/img/feather-icons/sign-out.svg @@ -1,5 +1,5 @@ - + diff --git a/res/img/feather-icons/upload.svg b/res/img/feather-icons/upload.svg index 8ec5d95c2c..30c89d3819 100644 --- a/res/img/feather-icons/upload.svg +++ b/res/img/feather-icons/upload.svg @@ -1,5 +1,5 @@ - + diff --git a/res/img/feather-icons/user-add.svg b/res/img/feather-icons/user-add.svg index cbb25934c1..6b5210c1d6 100644 --- a/res/img/feather-icons/user-add.svg +++ b/res/img/feather-icons/user-add.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/user.svg b/res/img/feather-icons/user.svg index a789e580d5..210ef99e6a 100644 --- a/res/img/feather-icons/user.svg +++ b/res/img/feather-icons/user.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/users.svg b/res/img/feather-icons/users.svg index b0deac0a9e..b90aafdd4a 100644 --- a/res/img/feather-icons/users.svg +++ b/res/img/feather-icons/users.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/video.svg b/res/img/feather-icons/video.svg index a4c456832f..da77b6c57a 100644 --- a/res/img/feather-icons/video.svg +++ b/res/img/feather-icons/video.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/warning-triangle.svg b/res/img/feather-icons/warning-triangle.svg index 02196cbf43..3d18e30fb2 100644 --- a/res/img/feather-icons/warning-triangle.svg +++ b/res/img/feather-icons/warning-triangle.svg @@ -1,5 +1,13 @@ - - - - + + + + + + + + diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index ee5e1cf5d6..c0d0f15613 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -1,186 +1,196 @@ +// unified palette +// try to use these colors when possible +$bg-color: #181b21; +$base-color: #1b1f25; +$base-text-color: #edf3ff; +$header-panel-bg-color: #22262e; +$header-panel-border-color: #000000; +$header-panel-text-primary-color: #a1b2d1; +$header-panel-text-secondary-color: #c8c8cd; +$text-primary-color: #edf3ff; +$text-secondary-color: #a1b2d1; +$search-bg-color: #181b21; +$search-placeholder-color: #61708b; +$room-highlight-color: #343a46; // typical text (dark-on-white in light skin) -$primary-fg-color: #212121; -$primary-bg-color: #2d2d2d; - -// used for focusing form controls -$focus-bg-color: #101010; +$primary-fg-color: $text-primary-color; +$primary-bg-color: $bg-color; // used for dialog box text -$light-fg-color: #747474; +$light-fg-color: $header-panel-text-secondary-color; -// button UI (white-on-green in light skin) -$accent-fg-color: $primary-bg-color; -$accent-color: #76CFA6; -$accent-color-alt: $accent-color; -$accent-color-50pct: #76CFA67F; +// used for focusing form controls +$focus-bg-color: $room-highlight-color; -$selection-fg-color: $primary-fg-color; +$mention-user-pill-bg-color: $warning-color; +$other-user-pill-bg-color: $room-highlight-color; -$focus-brightness: 200%; +// informational plinth +$info-plinth-bg-color: $header-panel-bg-color; +$info-plinth-fg-color: #888; -// red warning colour -$warning-color: #ff0064; -$warning-bg-color: #DF2A8B; -$info-bg-color: #2A9EDF; +$preview-bar-bg-color: $header-panel-bg-color; -// groups -$info-plinth-bg-color: #454545; - -$other-user-pill-bg-color: rgba(255, 255, 255, 0.1); - -$preview-bar-bg-color: #333; - -// left-panel style muted accent color -$secondary-accent-color: $primary-bg-color; -$tertiary-accent-color: #454545; - -// stop the tinter trying to change the secondary accent color -// by overriding the key to something untintable -// XXX: this is a bit of a hack. -#mx_theme_secondaryAccentColor { - color: #c0ffee ! important; -} - -#mx_theme_tertiaryAccentColor { - color: #c0ffee ! important; -} - -// used by RoomDirectory permissions -$plinth-bg-color: #474747; - -// used by RoomDropTarget -$droptarget-bg-color: rgba(45,45,45,0.5); +$tagpanel-bg-color: $base-color; // used by AddressSelector -$selected-color: #000000; +$selected-color: $room-highlight-color; // selected for hoverover & selected event tiles -$event-selected-color: #353535; +$event-selected-color: $search-bg-color; // used for the hairline dividers in RoomView -$primary-hairline-color: #474747; +$primary-hairline-color: $header-panel-border-color; // used for the border of input text fields -$input-border-color: #3a3a3a; +$input-border-color: #e7e7e7; +$input-darker-bg-color: $search-bg-color; +$input-darker-fg-color: $search-placeholder-color; +$input-lighter-bg-color: #f2f5f8; +$input-lighter-fg-color: $input-darker-fg-color; +$input-focused-border-color: #238cf5; +$input-valid-border-color: $accent-color; -$input-darker-bg-color: #c1c9d6; -$input-darker-fg-color: #9fa9ba; -$button-bg-color: #7ac9a1; -$button-fg-color: white; -// apart from login forms, which have stronger border -$strong-input-border-color: #656565; +$field-focused-label-bg-color: $bg-color; -// used for UserSettings EditableText -$input-underline-color: $primary-fg-color; -$input-fg-color: $primary-fg-color; // scrollbars $scrollbar-thumb-color: rgba(255, 255, 255, 0.2); $scrollbar-track-color: transparent; + // context menus -$menu-border-color: rgba(187, 187, 187, 0.5); -$menu-bg-color: #373737; -$menu-selected-color: #f5f8fa; +$menu-border-color: $header-panel-border-color; +$menu-bg-color: $header-panel-bg-color; +$menu-box-shadow-color: $bg-color; +$menu-selected-color: $room-highlight-color; -$avatar-initial-color: #2d2d2d; -$avatar-bg-color: #ffffff; -$menu-selected-color: #f5f8fa; +$avatar-initial-color: #ffffff; +$avatar-bg-color: $bg-color; -$h3-color: $primary-fg-color; - -$dialog-background-bg-color: #000; +$dialog-background-bg-color: $header-panel-bg-color; $lightbox-background-bg-color: #000; -$greyed-fg-color: #888; - -$neutral-badge-color: #888; - -$preview-widget-bar-color: $menu-bg-color; -$preview-widget-fg-color: $greyed-fg-color; - -$blockquote-bar-color: #ddd; -$blockquote-fg-color: #777; - $settings-grey-fg-color: #a2a2a2; +$settings-profile-placeholder-bg-color: #e7e7e7; +$settings-profile-overlay-bg-color: #000; +$settings-profile-overlay-placeholder-bg-color: transparent; +$settings-profile-overlay-fg-color: #fff; +$settings-profile-overlay-placeholder-fg-color: #454545; +$settings-subsection-fg-color: $text-secondary-color; -$voip-decline-color: #f48080; -$voip-accept-color: #80f480; +$topleftmenu-color: $text-primary-color; +$roomheader-color: $text-primary-color; +$roomheader-addroom-color: $header-panel-text-primary-color; +$tagpanel-button-color: $header-panel-text-primary-color; +$roomheader-button-color: $header-panel-text-primary-color; +$groupheader-button-color: $header-panel-text-primary-color; +$rightpanel-button-color: $header-panel-text-primary-color; +$composer-button-color: $header-panel-text-primary-color; +$roomtopic-color: $text-secondary-color; +$eventtile-meta-color: $roomtopic-color; -$rte-bg-color: #353535; -$rte-code-bg-color: #000; +$header-divider-color: $header-panel-text-primary-color; -$room-warning-bg-color: #2d2d2d; +$roomtile-name-color: $header-panel-text-primary-color; +$roomtile-selected-color: $text-primary-color; +$roomtile-notified-color: $text-primary-color; +$roomtile-selected-bg-color: $room-highlight-color; +$roomtile-focused-bg-color: $room-highlight-color; -// ******************** +$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); -$roomtile-name-color: rgba(186, 186, 186, 0.8); -$roomtile-selected-bg-color: #333; -$roomtile-focused-bg-color: rgba(255, 255, 255, 0.2); +$panel-divider-color: $header-panel-border-color; -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; - -$roomsublist-background: rgba(0, 0, 0, 0.2); -$roomsublist-label-fg-color: $h3-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - -$panel-divider-color: rgba(118, 207, 166, 0.2); - -// ******************** - -$widget-menu-bar-bg-color: $tertiary-accent-color; - -// ******************** - -// event tile lifecycle -$event-encrypting-color: rgba(171, 221, 188, 0.4); -$event-sending-color: #888; -$event-notsent-color: #f44; - -// event redaction -$event-redacted-fg-color: #606060; -$event-redacted-border-color: #000000; +$widget-menu-bar-bg-color: $search-bg-color; // event timestamp -$event-timestamp-color: #acacac; +$event-timestamp-color: $text-secondary-color; -$edit-button-url: "$(res)/img/icon_context_message_dark.svg"; -$copy-button-url: "$(res)/img/icon_copy_message_dark.svg"; +// Tabbed views +$tab-label-fg-color: $text-primary-color; +$tab-label-active-fg-color: $text-primary-color; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: $accent-color; +$tab-label-icon-bg-color: $text-primary-color; +$tab-label-active-icon-bg-color: $text-primary-color; -// e2e -$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color -$e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: $accent-color; +$button-secondary-bg-color: transparent; +$button-danger-fg-color: #ffffff; +$button-danger-bg-color: $notice-primary-color; +$button-danger-disabled-fg-color: #ffffff; +$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color -/*** ImageView ***/ -$lightbox-bg-color: #454545; -$lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; +$room-warning-bg-color: $header-panel-bg-color; -$imagebody-giflabel: rgba(1, 1, 1, 0.7); -$imagebody-giflabel-border: rgba(1, 1, 1, 0.2); -$imagebody-giflabel-color: rgba(0, 0, 0, 1); +$panel-gradient: rgba(34, 38, 46, 0), rgba(34, 38, 46, 1); -// unused? -$progressbar-color: #000; +/*** form elements ***/ + +// .mx_textinput is a container for a text input +// + some other controls like buttons, ... +// it has the appearance of a text box so the controls +// appear to be part of the input + +.mx_Dialog, .mx_MatrixChat { + + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + background-color: transparent; + color: $input-darker-fg-color; + border: 1px solid #c1c1c1; + } + + .mx_textinput { + > input[type=text], + > input[type=search] { + color: $primary-fg-color; + } + input::placeholder { + color: $roomsublist-label-fg-color; + } + } +} + +/*** panels ***/ +.dark-panel { + background-color: $header-panel-bg-color; +} + +.dark-panel { + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + color: $input-darker-fg-color; + background-color: $input-darker-bg-color; + border: none; + } +} + +.light-panel { + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + color: $input-lighter-fg-color; + background-color: $input-lighter-bg-color; + border: none; + } +} + +// ***** Mixins! ***** -// XXX: copypasted from _base in order to pick up the right FG color... @define-mixin mx_DialogButton { /* align images in buttons (eg spinners) */ vertical-align: middle; border: 0px; - border-radius: 36px; + border-radius: 4px; font-family: $font-family; font-size: 14px; - color: $accent-fg-color; - background-color: $accent-color; + color: $button-fg-color; + background-color: $button-bg-color; width: auto; padding: 7px; padding-left: 1.5em; @@ -190,12 +200,16 @@ $progressbar-color: #000; outline: none; } +@define-mixin mx_DialogButton_danger { + background-color: $accent-color; +} + @define-mixin mx_DialogButton_secondary { // flip colours for the secondary ones font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; } // Nasty hacks to apply a filter to arbitrary monochrome artwork to make it @@ -232,8 +246,3 @@ $progressbar-color: #000; } } } - -// Add a line to the right side of the left panel to distinguish it from the middle panel -.mx_LeftPanel { - border-right: 1px solid $tertiary-accent-color; -} diff --git a/res/themes/dark/css/dark.scss b/res/themes/dark/css/dark.scss index a7d18fa1a6..e7ae7c8cf8 100644 --- a/res/themes/dark/css/dark.scss +++ b/res/themes/dark/css/dark.scss @@ -1,5 +1,5 @@ @import "../../light/css/_paths.scss"; @import "../../light/css/_fonts.scss"; -@import "../../light/css/_base.scss"; +@import "../../light/css/_light.scss"; @import "_dark.scss"; @import "../../../../res/css/_components.scss"; diff --git a/res/themes/dharma/css/_fonts.scss b/res/themes/dharma/css/_fonts.scss deleted file mode 100644 index ac15847e44..0000000000 --- a/res/themes/dharma/css/_fonts.scss +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Nunito. - * Includes extended Latin and Vietnamese character sets - * Current URLs are taken from - * https://github.com/alexeiva/NunitoFont/releases/tag/v3.500 - * ...in order to include cyrillic. - * - * Previously, they were - * https://fonts.googleapis.com/css?family=Nunito:400,400i,600,600i,700,700i&subset=latin-ext,vietnamese - * - * We explicitly do not include Nunito's italic variants, as they are not italic enough - * and it's better to rely on the browser's built-in obliquing behaviour. - */ - -/* the 'src' links are relative to the bundle.css, which is in a subdirectory. - */ -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 400; - src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); -} -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 600; - src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); -} -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 700; - src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); -} - -/* - * Fira Mono - * Used for monospace copy, i.e. code - */ - -@font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); - font-weight: 700; - font-style: normal; -} diff --git a/res/themes/dharma/css/dharma.scss b/res/themes/dharma/css/dharma.scss deleted file mode 100644 index 24dc0ce18d..0000000000 --- a/res/themes/dharma/css/dharma.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import "../../light/css/_paths.scss"; -@import "_fonts.scss"; -@import "_dharma.scss"; -@import "../../../../res/css/_components.scss"; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss deleted file mode 100644 index 8185ba0ace..0000000000 --- a/res/themes/light/css/_base.scss +++ /dev/null @@ -1,276 +0,0 @@ -/* Open Sans lacks combining diacritics, so these will fall through - to the next font. Helevetica's diacritics however do not combine - nicely with Open Sans (on OSX, at least) and result in a huge - horizontal mess. Arial empirically gets it right, hence prioritising - Arial here. */ -$font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; - -// typical text (dark-on-white in light skin) -$primary-fg-color: #454545; -$primary-bg-color: #ffffff; - -// used for dialog box text -$light-fg-color: #747474; - -// used for focusing form controls -$focus-bg-color: #dddddd; - -// button UI (white-on-green in light skin) -$accent-fg-color: #ffffff; -$accent-color: #76CFA6; -$accent-color-alt: $accent-color; -$accent-color-50pct: #76CFA67F; - -$selection-fg-color: $primary-bg-color; - -$focus-brightness: 125%; - -// red warning colour -$warning-color: #ff0064; -// background colour for warnings -$warning-bg-color: #DF2A8B; -$info-bg-color: #2A9EDF; -$mention-user-pill-bg-color: #ff0064; -$other-user-pill-bg-color: rgba(0, 0, 0, 0.1); - -// pinned events indicator -$pinned-unread-color: #ff0064; // $warning-color -$pinned-color: #888; - -// informational plinth -$info-plinth-bg-color: #f7f7f7; -$info-plinth-fg-color: #888; - -$preview-bar-bg-color: #f7f7f7; - -// left-panel style muted accent color -$secondary-accent-color: #eaf5f0; -$tertiary-accent-color: #d3efe1; - -$tagpanel-bg-color: $tertiary-accent-color; - -// used by RoomDirectory permissions -$plinth-bg-color: $secondary-accent-color; - -// used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.5); - -// used by AddressSelector -$selected-color: $secondary-accent-color; - -// selected for hoverover & selected event tiles -$event-selected-color: #f7f7f7; - -// used for the hairline dividers in RoomView -$primary-hairline-color: #e5e5e5; - -// used for the border of input text fields -$input-border-color: #f0f0f0; -$input-border-dark-color: #b8b8b8; - -$input-darker-bg-color: #c1c9d6; -$input-darker-fg-color: #9fa9ba; -$button-bg-color: #7ac9a1; -$button-fg-color: white; -// apart from login forms, which have stronger border -$strong-input-border-color: #c7c7c7; -$input-focused-border-color: #238cf5; -$input-valid-border-color: #7ac9a1; - -$field-focused-label-bg-color: #ffffff; - -// used for UserSettings EditableText -$input-underline-color: rgba(151, 151, 151, 0.5); -$input-fg-color: rgba(74, 74, 74, 0.9); -// scrollbars -$scrollbar-thumb-color: rgba(0, 0, 0, 0.2); -$scrollbar-track-color: transparent; -// context menus -$menu-border-color: rgba(187, 187, 187, 0.5); -$menu-bg-color: #f6f6f6; -$menu-selected-color: #f5f8fa; - -$avatar-initial-color: #ffffff; -$avatar-bg-color: #ffffff; - -$h3-color: #3d3b39; - -$dialog-title-fg-color: #454545; -$dialog-backdrop-color: rgba(46, 48, 51, 0.38); -$dialog-shadow-color: rgba(0, 0, 0, 0.48); -$dialog-close-fg-color: #9fa9ba; - -$dialog-background-bg-color: #e9e9e9; -$lightbox-background-bg-color: #000; - -$greyed-fg-color: #888; - -$neutral-badge-color: #dbdbdb; - -$preview-widget-bar-color: #ddd; -$preview-widget-fg-color: $greyed-fg-color; - -$blockquote-bar-color: #ddd; -$blockquote-fg-color: #777; - -$settings-grey-fg-color: #a2a2a2; -$settings-profile-placeholder-bg-color: #e7e7e7; -$settings-profile-overlay-bg-color: #000; -$settings-profile-overlay-placeholder-bg-color: transparent; -$settings-profile-overlay-fg-color: #fff; -$settings-profile-overlay-placeholder-fg-color: #454545; -$settings-subsection-fg-color: #61708b; - -$voip-decline-color: #f48080; -$voip-accept-color: #80f480; - -$rte-bg-color: #e9e9e9; -$rte-code-bg-color: rgba(0, 0, 0, 0.04); -$rte-room-pill-color: #aaa; -$rte-group-pill-color: #aaa; - -$topleftmenu-color: $primary-fg-color; -$roomheader-color: $primary-fg-color; -$roomheader-addroom-color: $primary-bg-color; -$roomtopic-color: $settings-grey-fg-color; -$eventtile-meta-color: $roomtopic-color; - -$composer-e2e-icon-color: #c9ced6; - -// ******************** - -$roomtile-name-color: rgba(69, 69, 69, 0.8); -$roomtile-selected-color: $roomtile-name-color; -$roomtile-notified-color: $roomtile-name-color; -$roomtile-selected-bg-color: rgba(255, 255, 255, 0.8); -$roomtile-focused-bg-color: rgba(255, 255, 255, 0.9); - -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; - -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); - -$roomsublist-background: rgba(0, 0, 0, 0.05); -$roomsublist-label-fg-color: $h3-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - -$panel-divider-color: rgba(118, 207, 166, 0.2); - -// ******************** - -$widget-menu-bar-bg-color: $tertiary-accent-color; - -// ******************** - -// event tile lifecycle -$event-encrypting-color: #abddbc; -$event-sending-color: #ddd; -$event-notsent-color: #f44; - -// event redaction -$event-redacted-fg-color: #e2e2e2; -$event-redacted-border-color: #cccccc; - -// event timestamp -$event-timestamp-color: #acacac; - -$edit-button-url: "$(res)/img/icon_context_message.svg"; -$copy-button-url: "$(res)/img/icon_copy_message.svg"; - -// e2e -$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color -$e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; - -/*** ImageView ***/ -$lightbox-bg-color: #454545; -$lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; - -$imagebody-giflabel: rgba(0, 0, 0, 0.7); -$imagebody-giflabel-border: rgba(0, 0, 0, 0.2); -$imagebody-giflabel-color: rgba(255, 255, 255, 1); - -// Tabbed views -$tab-label-fg-color: #45474a; -$tab-label-active-fg-color: #ffffff; -$tab-label-bg-color: transparent; -$tab-label-active-bg-color: #7ac9a1; -$tab-label-icon-bg-color: #454545; -$tab-label-active-icon-bg-color: #ffffff; - -// Buttons -$button-primary-fg-color: #ffffff; -$button-primary-bg-color: #7ac9a1; -$button-primary-disabled-fg-color: #ffffff; -$button-primary-disabled-bg-color: #bce4d0; -$button-danger-fg-color: #ffffff; -$button-danger-bg-color: #f56679; -$button-danger-disabled-fg-color: #ffffff; -$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color - -// Toggle switch -$togglesw-off-color: #c1c9d6; -$togglesw-on-color: #7ac9a1; -$togglesw-ball-color: #fff; - -// unused? -$progressbar-color: #000; - -$room-warning-bg-color: #fff8e3; - -$memberstatus-placeholder-color: $roomtile-name-color; - -$authpage-bg-color: #2e3649; -$authpage-modal-bg-color: rgba(255, 255, 255, 0.59); -$authpage-body-bg-color: #ffffff; -$authpage-lang-color: #4e5054; -$authpage-body-color: #61708b; - -// ***** Mixins! ***** - -@define-mixin mx_DialogButton { - /* align images in buttons (eg spinners) */ - vertical-align: middle; - border: 0px; - border-radius: 36px; - font-family: $font-family; - font-size: 14px; - color: $accent-fg-color; - background-color: $accent-color; - width: auto; - padding: 7px; - padding-left: 1.5em; - padding-right: 1.5em; - cursor: pointer; - display: inline-block; - outline: none; -} - -@define-mixin mx_DialogButton_danger { - background-color: $warning-color; -} - -@define-mixin mx_DialogButton_hover { -} - -@define-mixin mx_DialogButton_small { - @mixin mx_DialogButton; - font-size: 15px; - padding: 0px 1.5em 0px 1.5em; -} - -@define-mixin mx_DialogButton_secondary { - // flip colours for the secondary ones - font-weight: 600; - border: 1px solid $accent-color ! important; - color: $accent-color; - background-color: $accent-fg-color; -} diff --git a/res/themes/light/css/_fonts.scss b/res/themes/light/css/_fonts.scss index c080663acf..ac15847e44 100644 --- a/res/themes/light/css/_fonts.scss +++ b/res/themes/light/css/_fonts.scss @@ -1,50 +1,36 @@ /* - * Open Sans - * Includes extended Latin, Greek, Cyrillic and Vietnamese character sets + * Nunito. + * Includes extended Latin and Vietnamese character sets + * Current URLs are taken from + * https://github.com/alexeiva/NunitoFont/releases/tag/v3.500 + * ...in order to include cyrillic. + * + * Previously, they were + * https://fonts.googleapis.com/css?family=Nunito:400,400i,600,600i,700,700i&subset=latin-ext,vietnamese + * + * We explicitly do not include Nunito's italic variants, as they are not italic enough + * and it's better to rely on the browser's built-in obliquing behaviour. */ /* the 'src' links are relative to the bundle.css, which is in a subdirectory. */ @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); } - @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); - font-weight: 400; - font-style: italic; + font-family: 'Nunito'; + font-style: normal; + font-weight: 600; + src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); } - @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); - font-weight: 600; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); - font-weight: 600; - font-style: italic; -} - -@font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); - font-weight: 700; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); - font-weight: 700; - font-style: italic; + font-family: 'Nunito'; + font-style: normal; + font-weight: 700; + src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); } /* diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/light/css/_light.scss similarity index 87% rename from res/themes/dharma/css/_dharma.scss rename to res/themes/light/css/_light.scss index e389cc944c..7bd84d6191 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/light/css/_light.scss @@ -6,6 +6,13 @@ Arial here. */ $font-family: 'Nunito', Arial, Helvetica, Sans-Serif; +// unified palette +// try to use these colors when possible +$accent-color: #03b381; +$notice-primary-color: #ff4b55; +$notice-secondary-color: #61708b; +$header-panel-bg-color: #f2f5f8; + // typical text (dark-on-white in light skin) $primary-fg-color: #454545; $primary-bg-color: #ffffff; @@ -18,7 +25,6 @@ $focus-bg-color: #dddddd; // button UI (white-on-green in light skin) $accent-fg-color: #ffffff; -$accent-color: #7ac9a1; $accent-color-50pct: #92caad; $accent-color-alt: #238CF5; @@ -27,7 +33,7 @@ $selection-fg-color: $primary-bg-color; $focus-brightness: 105%; // red warning colour -$warning-color: #f56679; +$warning-color: $notice-primary-color; // background colour for warnings $warning-bg-color: #DF2A8B; $info-bg-color: #2A9EDF; @@ -35,8 +41,8 @@ $mention-user-pill-bg-color: $warning-color; $other-user-pill-bg-color: rgba(0, 0, 0, 0.1); // pinned events indicator -$pinned-unread-color: #ff0064; // $warning-color -$pinned-color: #888; +$pinned-unread-color: $notice-primary-color; +$pinned-color: $notice-secondary-color; // informational plinth $info-plinth-bg-color: #f7f7f7; @@ -72,11 +78,11 @@ $input-darker-fg-color: #9fa9ba; $input-lighter-bg-color: #f2f5f8; $input-lighter-fg-color: $input-darker-fg-color; $input-focused-border-color: #238cf5; -$input-valid-border-color: #7ac9a1; +$input-valid-border-color: $accent-color; $field-focused-label-bg-color: #ffffff; -$button-bg-color: #7ac9a1; +$button-bg-color: $accent-color; $button-fg-color: white; // apart from login forms, which have stronger border @@ -91,6 +97,7 @@ $scrollbar-track-color: transparent; // context menus $menu-border-color: #ebedf8; $menu-bg-color: #fff; +$menu-box-shadow-color: rgba(118, 131, 156, 0.6); $menu-selected-color: #f5f8fa; $avatar-initial-color: #ffffff; @@ -139,10 +146,16 @@ $rte-group-pill-color: #aaa; $topleftmenu-color: #212121; $roomheader-color: #45474a; $roomheader-addroom-color: #91A1C0; +$tagpanel-button-color: #91A1C0; +$roomheader-button-color: #91A1C0; +$groupheader-button-color: #91A1C0; +$rightpanel-button-color: #91A1C0; +$composer-button-color: #91A1C0; $roomtopic-color: #9fa9ba; $eventtile-meta-color: $roomtopic-color; $composer-e2e-icon-color: #c9ced6; +$header-divider-color: #91A1C0; // ******************** @@ -152,14 +165,14 @@ $roomtile-notified-color: #212121; $roomtile-selected-bg-color: #fff; $roomtile-focused-bg-color: #fff; -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; +$username-variant1-color: #368bd6; +$username-variant2-color: #ac3ba8; +$username-variant3-color: #03b381; +$username-variant4-color: #e64f7a; +$username-variant5-color: #ff812d; +$username-variant6-color: #2dc2c5; +$username-variant7-color: #5c56f5; +$username-variant8-color: #74d12c; $roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); @@ -205,26 +218,24 @@ $lightbox-border-color: #ffffff; $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; -$tab-label-active-bg-color: #7ac9a1; +$tab-label-active-bg-color: $accent-color; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; // Buttons $button-primary-fg-color: #ffffff; -$button-primary-bg-color: #7ac9a1; -$button-primary-disabled-fg-color: #ffffff; -$button-primary-disabled-bg-color: #bce4d0; +$button-primary-bg-color: $accent-color; +$button-secondary-bg-color: $accent-fg-color; $button-danger-fg-color: #ffffff; -$button-danger-bg-color: #f56679; +$button-danger-bg-color: $notice-primary-color; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color // Toggle switch $togglesw-off-color: #c1c9d6; -$togglesw-on-color: #7ac9a1; +$togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; -// unused? $progressbar-color: #000; $room-warning-bg-color: #fff8e3; @@ -235,7 +246,10 @@ $authpage-bg-color: #2e3649; $authpage-modal-bg-color: rgba(255, 255, 255, 0.59); $authpage-body-bg-color: #ffffff; $authpage-lang-color: #4e5054; -$authpage-body-color: #61708b; +$authpage-primary-color: #454545; +$authpage-secondary-color: #61708b; + +$panel-gradient: rgba(242, 245, 248, 0), rgba(242, 245, 248, 1); /*** form elements ***/ @@ -270,23 +284,13 @@ $authpage-body-color: #61708b; border: none; flex: 1; color: $primary-fg-color; - }, + } input::placeholder { - color: $roomsublist-label-fg-color; + color: $roomsublist-label-fg-color; } } } -input[type=text], -input[type=search], -input[type=password] { - padding: 9px; - font-family: $font-family; - font-size: 14px; - font-weight: 600; - min-width: 0; -} - /*** panels ***/ .dark-panel { background-color: $secondary-accent-color; @@ -319,7 +323,6 @@ input[type=search].mx_textinput_icon { background-position: 10px center; } - // FIXME THEME - Tint by CSS rather than referencing a duplicate asset input[type=text].mx_textinput_icon.mx_textinput_search, input[type=search].mx_textinput_icon.mx_textinput_search { @@ -332,7 +335,7 @@ input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration { - display: none; + display: none; } .input[type=text]::-webkit-input-placeholder, @@ -386,5 +389,5 @@ textarea::placeholder { font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; } diff --git a/res/themes/light/css/light.scss b/res/themes/light/css/light.scss index 6ac8d0feba..6acb2d9d94 100644 --- a/res/themes/light/css/light.scss +++ b/res/themes/light/css/light.scss @@ -1,4 +1,4 @@ @import "_paths.scss"; @import "_fonts.scss"; -@import "_base.scss"; +@import "_light.scss"; @import "../../../../res/css/_components.scss"; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index f283eb84a5..e36034c69d 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -177,7 +177,7 @@ class MatrixClientPeg { userId: creds.userId, deviceId: creds.deviceId, timelineSupport: true, - forceTURN: !SettingsStore.getValue('webRtcForcePeerToPeer', false), + forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), verificationMethods: [verificationMethods.SAS] }; diff --git a/src/Unread.js b/src/Unread.js index 55e60f2a9a..9514ec821b 100644 --- a/src/Unread.js +++ b/src/Unread.js @@ -32,7 +32,7 @@ module.exports = { return false; } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') { return false; - } else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { + } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { return false; } const EventTile = sdk.getComponent('rooms.EventTile'); diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 2c54344a30..9ceff69467 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -240,7 +240,6 @@ export default React.createClass({ _renderPhasePassPhrase: function() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let strengthMeter; let helpText; @@ -265,8 +264,15 @@ export default React.createClass({ } return
-

{_t("Secure your encrypted message history with a Recovery Passphrase.")}

-

{_t("You'll need it if you log out or lose access to this device.")}

+

{_t( + "Warning: you should only set up key backup from a trusted computer.", {}, + { b: sub => {sub} }, + )}

+

{_t( + "We'll store an encrypted copy of your keys on our server. " + + "Protect your backup with a passphrase to keep it secure.", + )}

+

{_t("For maximum security, this should be different from your account password.")}

@@ -291,34 +297,12 @@ export default React.createClass({ disabled={!this._passPhraseIsValid()} /> -

{_t( - "If you don't want encrypted message history to be available on other devices, "+ - ".", - {}, - { - button: sub => - {sub} - , - }, - )}

-

{_t( - "Or, if you don't want to create a Recovery Passphrase, skip this step and "+ - ".", - {}, - { - button: sub => - {sub} - , - }, - )}

+
+ {_t("Advanced")} +

+
; }, @@ -353,9 +337,7 @@ export default React.createClass({ const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return

{_t( - "Type in your Recovery Passphrase to confirm you remember it. " + - "If it helps, add it to your password manager or store it " + - "somewhere safe.", + "Please enter your passphrase a second time to confirm.", )}

@@ -392,7 +374,13 @@ export default React.createClass({ } return
-

{_t("Make a copy of this Recovery Key and keep it safe.")}

+

{_t( + "Your recovery key is a safety net - you can use it to restore " + + "access to your encrypted messages if you forget your passphrase.", + )}

+

{_t( + "Keep your recovery key somewhere very secure, like a password manager (or a safe)", + )}

{bodyText}

@@ -455,10 +443,9 @@ export default React.createClass({ const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return

{_t( - "Your encryption keys are now being backed up in the background " + - "to your Homeserver. The initial backup could take several minutes. " + - "You can view key backup upload progress in Settings.")}

- + @@ -484,19 +471,19 @@ export default React.createClass({ _titleForPhase: function(phase) { switch (phase) { case PHASE_PASSPHRASE: - return _t('Create a Recovery Passphrase'); + return _t('Secure your backup with a passphrase'); case PHASE_PASSPHRASE_CONFIRM: - return _t('Confirm Recovery Passphrase'); + return _t('Confirm your passphrase'); case PHASE_OPTOUT_CONFIRM: return _t('Warning!'); case PHASE_SHOWKEY: - return _t('Recovery Key'); + return _t('Recovery key'); case PHASE_KEEPITSAFE: return _t('Keep it safe'); case PHASE_BACKINGUP: return _t('Starting backup...'); case PHASE_DONE: - return _t('Backup Started'); + return _t('Success!'); default: return _t("Create Key Backup"); } diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js index db86178b5a..28281af771 100644 --- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js +++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js @@ -39,36 +39,8 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { } onSetupClick = async () => { - // TODO: Should change to a restore key backup flow that checks the - // recovery passphrase while at the same time also cross-signing the - // device as well in a single flow. Since we don't have that yet, we'll - // look for an unverified device and verify it. Note that this means - // we won't restore keys yet; instead we'll only trust the backup for - // sending our own new keys to it. - let backupSigStatus; - try { - backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(this.props.newVersionInfo); - } catch (e) { - console.log("Unable to fetch key backup status", e); - return; - } - - let unverifiedDevice; - for (const sig of backupSigStatus.sigs) { - if (!sig.device.isVerified()) { - unverifiedDevice = sig.device; - break; - } - } - if (!unverifiedDevice) { - console.log("Unable to find a device to verify."); - return; - } - - const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog'); - Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, { - userId: MatrixClientPeg.get().credentials.userId, - device: unverifiedDevice, + const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog'); + Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, { onFinished: this.props.onFinished, }); } @@ -111,11 +83,6 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { } else { content =
{newMethodDetected} -

{_t( - "Setting up Secure Messages on this device " + - "will re-encrypt this device's message history with " + - "the new recovery method.", - )}

{hackWarning}
{ this.props.children } diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 530a200a18..89fce9c718 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -1157,7 +1157,6 @@ export default React.createClass({ render: function() { const GroupAvatar = sdk.getComponent("avatars.GroupAvatar"); const Spinner = sdk.getComponent("elements.Spinner"); - const TintableSvg = sdk.getComponent("elements.TintableSvg"); const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); if (this.state.summaryLoading && this.state.error === null || this.state.saving) { @@ -1248,13 +1247,17 @@ export default React.createClass({ if (this.state.editing) { rightButtons.push( { _t('Save') } , ); rightButtons.push( - + {_t("Cancel")} , @@ -1262,16 +1265,20 @@ export default React.createClass({ } else { if (summary.user && summary.user.membership === 'join') { rightButtons.push( - - , ); } rightButtons.push( - - + , ); } diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index c3e54ee900..e1516d1f64 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -59,6 +59,10 @@ export default class IndicatorScrollbar extends React.Component { } } + getScrollTop() { + return this._autoHideScrollbar.getScrollTop(); + } + componentWillUnmount() { if (this._scrollElement) { this._scrollElement.removeEventListener("scroll", this.checkOverflow); diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index bd49f8acd4..000195d349 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -182,6 +182,7 @@ const LeftPanel = React.createClass({ render: function() { const RoomList = sdk.getComponent('rooms.RoomList'); + const RoomBreadcrumbs = sdk.getComponent('rooms.RoomBreadcrumbs'); const TagPanel = sdk.getComponent('structures.TagPanel'); const CustomRoomTagPanel = sdk.getComponent('structures.CustomRoomTagPanel'); const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton'); @@ -215,12 +216,17 @@ const LeftPanel = React.createClass({ onCleared={ this.onSearchCleared } collapsed={this.props.collapsed} />); + let breadcrumbs; + if (SettingsStore.isFeatureEnabled("feature_room_breadcrumbs")) { + breadcrumbs = (); + } return (
{ tagPanelContainer }
; } else { - const tiles = this.makeRoomTiles(); - tiles.push(...this.props.extraTiles); return
{this._getHeaderJsx(isCollapsed)} - - { tiles } + +
; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 50fa18e075..8e32802d0a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -283,6 +283,15 @@ module.exports = React.createClass({ } }, + _getRoomId() { + // According to `_onRoomViewStoreUpdate`, `state.roomId` can be null + // if we have a room alias we haven't resolved yet. To work around this, + // first we'll try the room object if it's there, and then fallback to + // the bare room ID. (We may want to update `state.roomId` after + // resolving aliases, so we could always trust it.) + return this.state.room ? this.state.room.roomId : this.state.roomId; + }, + _onWidgetEchoStoreUpdate: function() { this.setState({ showApps: this._shouldShowApps(this.state.room), @@ -784,6 +793,7 @@ module.exports = React.createClass({ this._updateConfCallNotification(); this._updateDMState(); this._checkIfAlone(this.state.room); + this._updateE2EStatus(this.state.room); }, 500), _checkIfAlone: function(room) { @@ -877,13 +887,12 @@ module.exports = React.createClass({ // If the user is a ROU, allow them to transition to a PWLU if (cli && cli.isGuest()) { // Join this room once the user has registered and logged in - const signUrl = this.props.thirdPartyInvite ? - this.props.thirdPartyInvite.inviteSignUrl : undefined; + // (If we failed to peek, we may not have a valid room object.) dis.dispatch({ action: 'do_after_sync_prepared', deferred_action: { action: 'view_room', - room_id: this.state.room.roomId, + room_id: this._getRoomId(), }, }); diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js index fbcd9a7279..10628ccd13 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.js @@ -21,7 +21,7 @@ import { _t } from '../../languageHandler'; import { KeyCode } from '../../Keyboard'; import sdk from '../../index'; import dis from '../../dispatcher'; -import rate_limited_func from '../../ratelimitedfunc'; +import { throttle } from 'lodash'; import AccessibleButton from '../../components/views/elements/AccessibleButton'; module.exports = React.createClass({ @@ -67,12 +67,9 @@ module.exports = React.createClass({ this.onSearch(); }, - onSearch: new rate_limited_func( - function() { - this.props.onSearch(this.refs.search.value); - }, - 500, - ), + onSearch: throttle(function() { + this.props.onSearch(this.refs.search.value); + }, 200, {trailing: true, leading: true}), _onKeyDown: function(ev) { switch (ev.keyCode) { diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 69abd8b88b..6b578f0f68 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -64,6 +64,17 @@ module.exports = React.createClass({ getInitialState: function() { const customURLsAllowed = !SdkConfig.get()['disable_custom_urls']; + let initialPhase = PHASE_SERVER_DETAILS; + if ( + // if we have these two, skip to the good bit + // (they could come in from the URL params in a + // registration email link) + (this.props.clientSecret && this.props.sessionId) || + // or if custom URLs aren't allowed, skip them + !customURLsAllowed + ) { + initialPhase = PHASE_REGISTRATION; + } return { busy: false, @@ -87,7 +98,7 @@ module.exports = React.createClass({ hsUrl: this.props.customHsUrl, isUrl: this.props.customIsUrl, // Phase of the overall registration dialog. - phase: customURLsAllowed ? PHASE_SERVER_DETAILS : PHASE_REGISTRATION, + phase: initialPhase, flows: null, }; }, @@ -111,7 +122,7 @@ module.exports = React.createClass({ }); }, - onServerTypeChange(type) { + onServerTypeChange(type, initial) { this.setState({ serverType: type, }); @@ -137,9 +148,15 @@ module.exports = React.createClass({ hsUrl: this.props.defaultHsUrl, isUrl: this.props.defaultIsUrl, }); - this.setState({ - phase: PHASE_SERVER_DETAILS, - }); + // if this is the initial value from the control and we're + // already in the registration phase, don't go back to the + // server details phase (but do if it's actually a change resulting + // from user interaction). + if (!initial || !this.state.phase === PHASE_REGISTRATION) { + this.setState({ + phase: PHASE_SERVER_DETAILS, + }); + } break; } }, @@ -372,9 +389,12 @@ module.exports = React.createClass({ // If we're on a different phase, we only show the server type selector, // which is always shown if we allow custom URLs at all. if (PHASES_ENABLED && this.state.phase !== PHASE_SERVER_DETAILS) { + // if we've been given a custom HS URL we should actually pass that, so + // that the appropriate section is selected at the start to match the + // homeserver URL we're using return
; diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js index de76e6acf9..7a28eec0ed 100644 --- a/src/components/views/auth/ServerTypeSelector.js +++ b/src/components/views/auth/ServerTypeSelector.js @@ -90,7 +90,11 @@ export default class ServerTypeSelector extends React.PureComponent { selected: type, }; if (onChange) { - onChange(type); + // FIXME: Supply a second 'initial' param here to flag that this is + // initialising the value rather than from user interaction + // (which sometimes we'll want to ignore). Must be a better way + // to do this. + onChange(type, true); } } diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index c990b5705d..ffa2a0bf5c 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -90,6 +90,11 @@ module.exports = React.createClass({ this.closeMenu(); }, + e2eInfoClicked: function() { + this.props.e2eInfoCallback(); + this.closeMenu(); + }, + onViewSourceClick: function() { const ViewSource = sdk.getComponent('structures.ViewSource'); Modal.createTrackedDialog('View Event Source', '', ViewSource, { @@ -332,6 +337,13 @@ module.exports = React.createClass({ ); } + let e2eInfo; + if (this.props.e2eInfoCallback) { + e2eInfo =
+ { _t('End-to-end encryption information') } +
; + } + return (
{ resendButton } @@ -347,6 +359,7 @@ module.exports = React.createClass({ { replyButton } { externalURLButton } { collapseReplyThread } + { e2eInfo }
); }, diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js index 8b868e7b11..c0203a3ac8 100644 --- a/src/components/views/context_menus/TagTileContextMenu.js +++ b/src/components/views/context_menus/TagTileContextMenu.js @@ -68,7 +68,7 @@ export default class TagTileContextMenu extends React.Component {
- { _t('Remove') } + { _t('Hide') }
; } diff --git a/src/components/views/context_menus/TopLeftMenu.js b/src/components/views/context_menus/TopLeftMenu.js index 1d58db3c49..8583f631f2 100644 --- a/src/components/views/context_menus/TopLeftMenu.js +++ b/src/components/views/context_menus/TopLeftMenu.js @@ -20,11 +20,15 @@ import { _t } from '../../../languageHandler'; import LogoutDialog from "../dialogs/LogoutDialog"; import Modal from "../../../Modal"; import SdkConfig from '../../../SdkConfig'; +import MatrixClientPeg from '../../../MatrixClientPeg'; export class TopLeftMenu extends React.Component { constructor() { super(); + this.viewHomePage = this.viewHomePage.bind(this); + this.viewWelcomePage = this.viewWelcomePage.bind(this); this.openSettings = this.openSettings.bind(this); + this.signIn = this.signIn.bind(this); this.signOut = this.signOut.bind(this); } @@ -41,6 +45,8 @@ export class TopLeftMenu extends React.Component { } render() { + const isGuest = MatrixClientPeg.get().isGuest(); + let homePageSection = null; if (this.hasHomePage()) { homePageSection =
    @@ -48,14 +54,26 @@ export class TopLeftMenu extends React.Component {
; } + let signInOutSection; + if (isGuest) { + signInOutSection =
    +
  • {_t("Sign in")}
  • +
; + } else { + signInOutSection =
    +
  • {_t("Sign out")}
  • +
; + } + return
{homePageSection}
    -
  • {_t("Settings")}
  • +
  • {_t("Welcome")}
    -
  • {_t("Sign out")}
  • +
  • {_t("Settings")}
+ {signInOutSection}
; } @@ -64,11 +82,21 @@ export class TopLeftMenu extends React.Component { this.closeMenu(); } + viewWelcomePage() { + dis.dispatch({action: 'view_welcome_page'}); + this.closeMenu(); + } + openSettings() { dis.dispatch({action: 'view_user_settings'}); this.closeMenu(); } + signIn() { + dis.dispatch({action: 'start_login'}); + this.closeMenu(); + } + signOut() { Modal.createTrackedDialog('Logout E2E Export', '', LogoutDialog); this.closeMenu(); diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index 47ee73c61c..c901942fcd 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -60,6 +60,11 @@ export default class DeviceVerifyDialog extends React.Component { } _onSwitchToLegacyClick = () => { + if (this._verifier) { + this._verifier.removeListener('show_sas', this._onVerifierShowSas); + this._verifier.cancel('User cancel'); + this._verifier = null; + } this.setState({mode: MODE_LEGACY}); } @@ -184,11 +189,21 @@ export default class DeviceVerifyDialog extends React.Component { _renderSasVerificationPhaseWaitAccept() { const Spinner = sdk.getComponent("views.elements.Spinner"); + const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); return (

{_t("Waiting for partner to accept...")}

+

{_t( + "Nothing appearing? Not all clients support interactive verification yet. " + + ".", + {}, {button: sub => + {sub} + }, + )}

); } diff --git a/src/components/views/dialogs/IncomingSasDialog.js b/src/components/views/dialogs/IncomingSasDialog.js index 2a76e8a904..da2211c10f 100644 --- a/src/components/views/dialogs/IncomingSasDialog.js +++ b/src/components/views/dialogs/IncomingSasDialog.js @@ -16,6 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import MatrixClientPeg from '../../../MatrixClientPeg'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; @@ -37,9 +38,12 @@ export default class IncomingSasDialog extends React.Component { this.state = { phase: PHASE_START, sasVerified: false, + opponentProfile: null, + opponentProfileError: null, }; this.props.verifier.on('show_sas', this._onVerifierShowSas); this.props.verifier.on('cancel', this._onVerifierCancel); + this._fetchOpponentProfile(); } componentWillUnmount() { @@ -49,6 +53,21 @@ export default class IncomingSasDialog extends React.Component { this.props.verifier.removeListener('show_sas', this._onVerifierShowSas); } + async _fetchOpponentProfile() { + try { + const prof = await MatrixClientPeg.get().getProfileInfo( + this.props.verifier.userId, + ); + this.setState({ + opponentProfile: prof, + }); + } catch (e) { + this.setState({ + opponentProfileError: e, + }); + } + } + _onFinished = () => { this.props.onFinished(this.state.phase === PHASE_VERIFIED); } @@ -93,10 +112,39 @@ export default class IncomingSasDialog extends React.Component { _renderPhaseStart() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const Spinner = sdk.getComponent("views.elements.Spinner"); + const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + + let profile; + if (this.state.opponentProfile) { + profile =
+ +

{this.state.opponentProfile.displayname}

+
; + } else if (this.state.opponentProfileError) { + profile =
+ +

{this.props.verifier.userId}

+
; + } else { + profile = ; + } return (
-

{this.props.verifier.userId}

+ {profile}

{_t( "Verify this user to mark them as trusted. " + "Trusting users gives you extra peace of mind when using " + diff --git a/src/components/views/dialogs/LogoutDialog.js b/src/components/views/dialogs/LogoutDialog.js index 16b92af1c8..53b9f01da4 100644 --- a/src/components/views/dialogs/LogoutDialog.js +++ b/src/components/views/dialogs/LogoutDialog.js @@ -22,6 +22,10 @@ import { _t } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; export default class LogoutDialog extends React.Component { + defaultProps = { + onFinished: function() {}, + } + constructor() { super(); this._onSettingsLinkClick = this._onSettingsLinkClick.bind(this); @@ -29,13 +33,37 @@ export default class LogoutDialog extends React.Component { this._onFinished = this._onFinished.bind(this); this._onSetRecoveryMethodClick = this._onSetRecoveryMethodClick.bind(this); this._onLogoutConfirm = this._onLogoutConfirm.bind(this); + + this.state = { + loading: false, + backupInfo: null, + error: null, + }; + + if (!MatrixClientPeg.get().getKeyBackupEnabled()) { + this._loadBackupStatus(); + } + } + + async _loadBackupStatus() { + try { + const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); + this.setState({ + loading: false, + backupInfo, + }); + } catch (e) { + console.log("Unable to fetch key backup status", e); + this.setState({ + loading: false, + error: e, + }); + } } _onSettingsLinkClick() { // close dialog - if (this.props.onFinished) { - this.props.onFinished(); - } + this.props.onFinished(); } _onExportE2eKeysClicked() { @@ -52,9 +80,7 @@ export default class LogoutDialog extends React.Component { dis.dispatch({action: 'logout'}); } // close dialog - if (this.props.onFinished) { - this.props.onFinished(); - } + this.props.onFinished(); } _onSetRecoveryMethodClick() { @@ -63,72 +89,83 @@ export default class LogoutDialog extends React.Component { ); // close dialog - if (this.props.onFinished) { - this.props.onFinished(); - } + this.props.onFinished(); } _onLogoutConfirm() { dis.dispatch({action: 'logout'}); // close dialog - if (this.props.onFinished) { - this.props.onFinished(); - } + this.props.onFinished(); } render() { const description =

{_t( - "When you log out, you'll lose your secure message history. To prevent " + - "this, set up a recovery method.", - )}

-

{_t( - "Alternatively, advanced users can also manually export encryption keys in " + - "Settings before logging out.", {}, - { - a: sub => {sub}, - }, + "Encrypted messages are secured with end-to-end encryption. " + + "Only you and the recipient(s) have the keys to read these messages.", )}

+

{_t("Back up your keys before signing out to avoid losing them.")}

; if (!MatrixClientPeg.get().getKeyBackupEnabled()) { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + + let dialogContent; + if (this.state.loading) { + const Spinner = sdk.getComponent('views.elements.Spinner'); + + dialogContent = ; + } else { + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + let setupButtonCaption; + if (this.state.backupInfo) { + setupButtonCaption = _t("Use Key Backup"); + } else { + // if there's an error fetching the backup info, we'll just assume there's + // no backup for the purpose of the button caption + setupButtonCaption = _t("Start using Key Backup"); + } + + dialogContent =
+
+ { description } +
+ + + +
+ {_t("Advanced")} +

+
+
; + } // Not quite a standard question dialog as the primary button cancels // the action and does something else instead, whilst non-default button // confirms the action. return ( -
- { description } -
- - - + {dialogContent}
); } else { const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog'); return ( - { device.deviceId } - -
- { device.getDisplayName() } - - ); -} - -DeviceListEntry.propTypes = { - userId: PropTypes.string.isRequired, - - // deviceinfo - device: PropTypes.object.isRequired, -}; - - function UserUnknownDeviceList(props) { + const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo'); const {userId, userDevices} = props; const deviceListEntries = Object.keys(userDevices).map((deviceId) => - , +
  • , ); return ( diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 712d8d2b4e..0f390a02c9 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -230,10 +230,15 @@ export default React.createClass({ const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); title = _t("Enter Recovery Passphrase"); content =
    - {_t( +

    {_t( + "Warning: you should only set up key backup " + + "from a trusted computer.", {}, + { b: sub => {sub} }, + )}

    +

    {_t( "Access your secure message history and set up secure " + "messaging by entering your recovery passphrase.", - )}
    + )}

    - {_t( +

    {_t( + "Warning: you should only set up key backup " + + "from a trusted computer.", {}, + { b: sub => {sub} }, + )}

    +

    {_t( "Access your secure message history and set up secure " + "messaging by entering your recovery key.", - )}
    + )}

    = this.topCount && + (range.topCount + range.renderCount) <= (this.topCount + this.renderCount); + } + + expand(amount) { + const topGrow = Math.min(amount, this.topCount); + const bottomGrow = Math.min(amount, this.bottomCount); + return new ItemRange( + this.topCount - topGrow, + this.renderCount + topGrow + bottomGrow, + this.bottomCount - bottomGrow, + ); + } +} + +export default class LazyRenderList extends React.Component { + constructor(props) { + super(props); + const renderRange = LazyRenderList.getVisibleRangeFromProps(props).expand(OVERFLOW_ITEMS); + this.state = {renderRange}; + } + + static getVisibleRangeFromProps(props) { + const {items, itemHeight, scrollTop, height} = props; + const length = items ? items.length : 0; + const topCount = Math.max(0, Math.floor(scrollTop / itemHeight)); + const itemsAfterTop = length - topCount; + const renderCount = Math.min(Math.ceil(height / itemHeight), itemsAfterTop); + const bottomCount = itemsAfterTop - renderCount; + return new ItemRange(topCount, renderCount, bottomCount); + } + + componentWillReceiveProps(props) { + const state = this.state; + const range = LazyRenderList.getVisibleRangeFromProps(props); + const intersectRange = range.expand(OVERFLOW_MARGIN); + + const prevSize = this.props.items ? this.props.items.length : 0; + const listHasChangedSize = props.items.length !== prevSize; + // only update renderRange if the list has shrunk/grown and we need to adjust padding or + // if the new range isn't contained by the old anymore + if (listHasChangedSize || !state.renderRange || !state.renderRange.contains(intersectRange)) { + this.setState({renderRange: range.expand(OVERFLOW_ITEMS)}); + } + } + + shouldComponentUpdate(nextProps, nextState) { + const itemsChanged = nextProps.items !== this.props.items; + const rangeChanged = nextState.renderRange !== this.state.renderRange; + return itemsChanged || rangeChanged; + } + + render() { + const {itemHeight, items, renderItem} = this.props; + + const {renderRange} = this.state; + const paddingTop = renderRange.topCount * itemHeight; + const paddingBottom = renderRange.bottomCount * itemHeight; + const renderedItems = (items || []).slice( + renderRange.topCount, + renderRange.topCount + renderRange.renderCount, + ); + + return (
    + { renderedItems.map(renderItem) } +
    ); + } +} diff --git a/src/components/views/elements/ManageIntegsButton.js b/src/components/views/elements/ManageIntegsButton.js index e5cc21d85e..3240050b6a 100644 --- a/src/components/views/elements/ManageIntegsButton.js +++ b/src/components/views/elements/ManageIntegsButton.js @@ -24,7 +24,6 @@ import ScalarMessaging from '../../../ScalarMessaging'; import Modal from "../../../Modal"; import { _t } from '../../../languageHandler'; import AccessibleButton from './AccessibleButton'; -import TintableSvg from './TintableSvg'; export default class ManageIntegsButton extends React.Component { constructor(props) { @@ -76,6 +75,7 @@ export default class ManageIntegsButton extends React.Component { if (this.scalarClient !== null) { const integrationsButtonClasses = classNames({ mx_RoomHeader_button: true, + mx_RoomHeader_manageIntegsButton: true, mx_ManageIntegsButton_error: !!this.state.scalarError, }); @@ -94,8 +94,10 @@ export default class ManageIntegsButton extends React.Component { } integrationsButton = ( - - + { integrationsWarningTriangle } { integrationsErrorPopup } diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.js index 6867b0bb9d..13379d49e3 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.js +++ b/src/components/views/right_panel/GroupHeaderButtons.js @@ -65,12 +65,14 @@ export default class GroupHeaderButtons extends HeaderButtons { ]; return [ - , - - - ; + onClick={this.onClick}> + ; } } @@ -62,14 +61,14 @@ HeaderButton.propTypes = { isHighlighted: PropTypes.bool.isRequired, // The phase to swap to when the button is clicked clickPhase: PropTypes.string.isRequired, - // The source file of the icon to display - iconSrc: PropTypes.string.isRequired, // The badge to display above the icon badge: PropTypes.node, // The parameters to track the click event analytics: PropTypes.arrayOf(PropTypes.string).isRequired, + // Button name + name: PropTypes.string.isRequired, // Button title title: PropTypes.string.isRequired, }; diff --git a/src/components/views/right_panel/HeaderButtons.js b/src/components/views/right_panel/HeaderButtons.js index 3f5f58121d..b7155b922f 100644 --- a/src/components/views/right_panel/HeaderButtons.js +++ b/src/components/views/right_panel/HeaderButtons.js @@ -88,7 +88,7 @@ export default class HeaderButtons extends React.Component { render() { // inline style as this will be swapped around in future commits - return
    + return
    { this.renderButtons() }
    ; } diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.js index 1985909f4a..8d10094637 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.js +++ b/src/components/views/right_panel/RoomHeaderButtons.js @@ -52,17 +52,20 @@ export default class RoomHeaderButtons extends HeaderButtons { ]; return [ - , - , - ); + const icon = (
    ); + if (props.onClick) { + return ({ icon }); + } else { + return icon; + } } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 8520d804e0..8e1fb5af9f 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -327,6 +327,7 @@ module.exports = withMatrixClient(React.createClass({ top: y, eventTileOps: tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined, collapseReplyThread: replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined, + e2eInfoCallback: () => this.onCryptoClicked(), onFinished: function() { self.setState({menu: false}); }, @@ -773,29 +774,31 @@ module.exports.haveTileForEvent = function(e) { function E2ePadlockUndecryptable(props) { return ( - + ); } function E2ePadlockUnverified(props) { return ( - + ); } function E2ePadlockUnencrypted(props) { return ( - + ); } function E2ePadlock(props) { if (SettingsStore.getValue("alwaysShowEncryptionIcons")) { - return
    ; + return (
    ); } else { - return
    ; + return (
    ); } } diff --git a/src/components/views/rooms/MemberDeviceInfo.js b/src/components/views/rooms/MemberDeviceInfo.js index 67f99e4d1d..ff88c6f6e6 100644 --- a/src/components/views/rooms/MemberDeviceInfo.js +++ b/src/components/views/rooms/MemberDeviceInfo.js @@ -30,7 +30,7 @@ export default class MemberDeviceInfo extends React.Component { mx_MemberDeviceInfo_icon_unverified: this.props.device.isUnverified(), }); const indicator = (
    ); - const deviceName = this.props.device.ambiguous ? + const deviceName = (this.props.device.ambiguous || this.props.showDeviceId) ? (this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" : this.props.device.getDisplayName(); diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 5df0da7491..f4c600af8d 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -941,6 +941,8 @@ module.exports = withMatrixClient(React.createClass({ } let roomMemberDetails = null; + let e2eIconElement; + if (this.props.member.roomId) { // is in room const PowerSelector = sdk.getComponent('elements.PowerSelector'); roomMemberDetails =
    @@ -959,6 +961,11 @@ module.exports = withMatrixClient(React.createClass({ {statusLabel}
    ; + + const isEncrypted = this.props.matrixClient.isRoomEncrypted(this.props.member.roomId); + if (this.state.e2eStatus && isEncrypted) { + e2eIconElement = (); + } } const avatarUrl = this.props.member.getMxcAvatarUrl(); @@ -967,7 +974,7 @@ module.exports = withMatrixClient(React.createClass({ const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl, 800, 800); avatarElement =
    -
    +
    ; } const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); @@ -979,7 +986,7 @@ module.exports = withMatrixClient(React.createClass({ {_t('Close')} - { this.state.e2eStatus ? : undefined } + { e2eIconElement } { memberName }
    { avatarElement } diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 7117825d76..10142b2b4b 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -306,7 +306,6 @@ export default class MessageComposer extends React.Component { render() { const uploadInputStyle = {display: 'none'}; const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); - const TintableSvg = sdk.getComponent("elements.TintableSvg"); const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput"); const controls = []; @@ -335,17 +334,26 @@ export default class MessageComposer extends React.Component { // Call buttons if (this.props.callState && this.props.callState !== 'ended') { hangupButton = - - {_t('Hangup')} + ; } else { callButton = - - + ; videoCallButton = - - + ; } @@ -385,9 +393,11 @@ export default class MessageComposer extends React.Component { // check separately for whether we can call, but this is slightly // complex because of conference calls. const uploadButton = ( - - + this.setState({rooms}), 0); + } + } + } + + onAction(payload) { + switch (payload.action) { + case 'view_room': + if (this._previousRoomId) { + this._appendRoomId(this._previousRoomId); + } + this._previousRoomId = payload.room_id; + } + } + + _appendRoomId(roomId) { + const room = MatrixClientPeg.get().getRoom(roomId); + if (!room) { + return; + } + const rooms = this.state.rooms.slice(); + const existingIdx = rooms.findIndex((r) => r.room.roomId === room.roomId); + if (existingIdx !== -1) { + rooms.splice(existingIdx, 1); + } + rooms.splice(0, 0, {room, animated: false}); + if (rooms.length > MAX_ROOMS) { + rooms.splice(MAX_ROOMS, rooms.length - MAX_ROOMS); + } + this.setState({rooms}); + } + + _viewRoom(room) { + dis.dispatch({action: "view_room", room_id: room.roomId}); + } + + render() { + // check for collapsed here and + // not at parent so we keep + // rooms in our state + // when collapsing and expanding + if (this.props.collapsed) { + return null; + } + const rooms = this.state.rooms; + const avatars = rooms.map(({room, animated}, i) => { + const isFirst = i === 0; + const classes = classNames({ + "mx_RoomBreadcrumbs_preAnimate": isFirst && !animated, + "mx_RoomBreadcrumbs_animate": isFirst, + }); + return ( + this._viewRoom(room)}> + + + ); + }); + return (
    { avatars }
    ); + } +} diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index e25b25d110..5e56f59425 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -32,6 +32,7 @@ import {CancelButton} from './SimpleRoomHeader'; import SettingsStore from "../../../settings/SettingsStore"; import RoomHeaderButtons from '../right_panel/RoomHeaderButtons'; import E2EIcon from './E2EIcon'; +import * as cryptodevices from '../../../cryptodevices'; module.exports = React.createClass({ displayName: 'RoomHeader', @@ -145,9 +146,14 @@ module.exports = React.createClass({ return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0); }, + _onShowDevicesClick: function() { + if (this.props.e2eStatus === "warning") { + cryptodevices.showUnknownDeviceDialogForMessages(MatrixClientPeg.get(), this.props.room); + } + }, + render: function() { const RoomAvatar = sdk.getComponent("avatars.RoomAvatar"); - const TintableSvg = sdk.getComponent("elements.TintableSvg"); const EmojiText = sdk.getComponent('elements.EmojiText'); let searchStatus = null; @@ -156,7 +162,7 @@ module.exports = React.createClass({ let pinnedEventsButton = null; const e2eIcon = this.props.e2eStatus ? - : + : undefined; if (this.props.onCancelClick) { @@ -221,8 +227,10 @@ module.exports = React.createClass({ if (this.props.onSettingsClick) { settingsButton = - - + ; } @@ -238,7 +246,6 @@ module.exports = React.createClass({ { pinsIndicator } - ; } @@ -253,24 +260,30 @@ module.exports = React.createClass({ let forgetButton; if (this.props.onForgetClick) { forgetButton = - - + ; } let searchButton; if (this.props.onSearchClick && this.props.inRoom) { searchButton = - - + ; } let shareRoomButton; if (this.props.inRoom) { shareRoomButton = - - + ; } diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 56eb4b713d..227dd318ed 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; import SettingsStore from "../../../settings/SettingsStore"; +import Timer from "../../../utils/Timer"; const React = require("react"); const ReactDOM = require("react-dom"); @@ -41,6 +42,7 @@ import {Resizer} from '../../../resizer'; import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2'; const HIDE_CONFERENCE_CHANS = true; const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/; +const HOVER_MOVE_TIMEOUT = 1000; function labelForTagName(tagName) { if (tagName.startsWith('u.')) return tagName.slice(2); @@ -73,6 +75,7 @@ module.exports = React.createClass({ getInitialState: function() { + this._hoverClearTimer = null; this._subListRefs = { // key => RoomSubList ref }; @@ -95,7 +98,7 @@ module.exports = React.createClass({ // update overflow indicators this._checkSubListsOverflow(); // don't store height for collapsed sublists - if(!this.collapsedState[key]) { + if (!this.collapsedState[key]) { this.subListSizes[key] = size; window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes)); @@ -357,11 +360,32 @@ module.exports = React.createClass({ this.forceUpdate(); }, - onMouseEnter: function(ev) { - this.setState({hover: true}); + onMouseMove: async function(ev) { + if (!this._hoverClearTimer) { + this.setState({hover: true}); + this._hoverClearTimer = new Timer(HOVER_MOVE_TIMEOUT); + this._hoverClearTimer.start(); + let finished = true; + try { + await this._hoverClearTimer.finished(); + } catch (err) { + finished = false; + } + this._hoverClearTimer = null; + if (finished) { + this.setState({hover: false}); + this._delayedRefreshRoomList(); + } + } else { + this._hoverClearTimer.restart(); + } }, onMouseLeave: function(ev) { + if (this._hoverClearTimer) { + this._hoverClearTimer.abort(); + this._hoverClearTimer = null; + } this.setState({hover: false}); // Refresh the room list just in case the user missed something. @@ -774,7 +798,7 @@ module.exports = React.createClass({ return (
    + onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}> { subListComponents }
    ); diff --git a/src/components/views/rooms/RoomRecoveryReminder.js b/src/components/views/rooms/RoomRecoveryReminder.js index 01dd71db01..12fc78be4e 100644 --- a/src/components/views/rooms/RoomRecoveryReminder.js +++ b/src/components/views/rooms/RoomRecoveryReminder.js @@ -39,6 +39,7 @@ export default class RoomRecoveryReminder extends React.PureComponent { loading: true, error: null, backupInfo: null, + notNowClicked: false, }; } @@ -77,6 +78,10 @@ export default class RoomRecoveryReminder extends React.PureComponent { } } + onOnNotNowClick = () => { + this.setState({notNowClicked: true}); + } + onDontAskAgainClick = () => { // When you choose "Don't ask again" from the room reminder, we show a // dialog to confirm the choice. @@ -104,46 +109,54 @@ export default class RoomRecoveryReminder extends React.PureComponent { } render() { - if (this.state.loading) { + // If there was an error loading just don't display the banner: we'll try again + // next time the user switchs to the room. + if (this.state.error || this.state.loading || this.state.notNowClicked) { return null; } const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton"); - let body; - if (this.state.error) { - body =
    - {_t("Unable to load key backup status")} -
    ; - } else if (this.state.backupInfo) { - // A key backup exists for this account, but we're not using it. - body =
    -

    {_t( - "Secure Key Backup should be active on all of your devices to avoid " + - "losing access to your encrypted messages.", - )}

    -
    ; + let setupCaption; + if (this.state.backupInfo) { + setupCaption = _t("Use Key Backup"); } else { - body = _t( - "Securely back up your decryption keys to the server to make sure " + - "you'll always be able to read your encrypted messages.", - ); + setupCaption = _t("Start using Key Backup"); } return (
    {_t( - "Don't risk losing your encrypted messages!", + "Never lose encrypted messages", )}
    -
    {body}
    +
    +

    {_t( + "Messages in this room are secured with end-to-end " + + "encryption. Only you and the recipient(s) have the " + + "keys to read these messages.", + )}

    +

    {_t( + "Securely back up your keys to avoid losing them. " + + "Learn more.", {}, + { + // TODO: We don't have this link yet: this will prevent the translators + // having to re-translate the string when we do. + a: sub => '', + }, + )}

    +
    - {_t("Activate Secure Key Backup")} + {setupCaption} +

    + { _t("Not now") } +

    - { _t("No thanks, I'll download a copy of my decryption keys before I log out") } + { _t("Don't ask me again") }

    diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index ed214812b5..f9e9d64b9e 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -108,13 +108,6 @@ module.exports = React.createClass({ return statusUser._unstable_statusMessage; }, - onRoomTimeline: function(ev, room) { - if (room !== this.props.room) return; - this.setState({ - notificationCount: this.props.room.getUnreadNotificationCount(), - }); - }, - onRoomName: function(room) { if (room !== this.props.room) return; this.setState({ @@ -159,7 +152,6 @@ module.exports = React.createClass({ componentWillMount: function() { MatrixClientPeg.get().on("accountData", this.onAccountData); - MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); MatrixClientPeg.get().on("Room.name", this.onRoomName); ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange); this.dispatcherRef = dis.register(this.onAction); @@ -179,7 +171,6 @@ module.exports = React.createClass({ const cli = MatrixClientPeg.get(); if (cli) { MatrixClientPeg.get().removeListener("accountData", this.onAccountData); - MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); } ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange); @@ -306,7 +297,7 @@ module.exports = React.createClass({ render: function() { const isInvite = this.props.room.getMyMembership() === "invite"; - const notificationCount = this.state.notificationCount; + const notificationCount = this.props.notificationCount; // var highlightCount = this.props.room.getUnreadNotificationCount("highlight"); const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge(); diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index b8fe3afb97..c9f3195b08 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -226,6 +226,7 @@ export default class Stickerpicker extends React.Component { showTitle={false} showMinimise={true} showDelete={false} + showCancel={false} showPopout={false} onMinimiseClick={this._onHideStickersClick} handleMinimisePointerEvents={true} @@ -321,7 +322,6 @@ export default class Stickerpicker extends React.Component { } render() { - const TintableSvg = sdk.getComponent("elements.TintableSvg"); const ContextualMenu = sdk.getComponent('structures.ContextualMenu'); const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu'); let stickersButton; @@ -347,11 +347,11 @@ export default class Stickerpicker extends React.Component { - + title={_t("Hide Stickers")} + > ; } else { // Show show-stickers button @@ -359,10 +359,10 @@ export default class Stickerpicker extends React.Component { - + title={_t("Show Stickers")} + > ; } return
    diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/KeyBackupPanel.js index fb870938c7..cc1b3fd017 100644 --- a/src/components/views/settings/KeyBackupPanel.js +++ b/src/components/views/settings/KeyBackupPanel.js @@ -42,7 +42,7 @@ export default class KeyBackupPanel extends React.PureComponent { } componentWillMount() { - this._loadBackupStatus(); + this._checkKeyBackupStatus(); MatrixClientPeg.get().on('crypto.keyBackupStatus', this._onKeyBackupStatus); MatrixClientPeg.get().on( @@ -70,9 +70,32 @@ export default class KeyBackupPanel extends React.PureComponent { } _onKeyBackupStatus() { + // This just loads the current backup status rather than forcing + // a re-check otherwise we risk causing infinite loops this._loadBackupStatus(); } + async _checkKeyBackupStatus() { + try { + const {backupInfo, trustInfo} = await MatrixClientPeg.get().checkKeyBackup(); + this.setState({ + backupInfo, + backupSigStatus: trustInfo, + error: null, + loading: false, + }); + } catch (e) { + console.log("Unable to fetch check backup status", e); + if (this._unmounted) return; + this.setState({ + error: e, + backupInfo: null, + backupSigStatus: null, + loading: false, + }); + } + } + async _loadBackupStatus() { this.setState({loading: true}); try { @@ -80,6 +103,7 @@ export default class KeyBackupPanel extends React.PureComponent { const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo); if (this._unmounted) return; this.setState({ + error: null, backupInfo, backupSigStatus, loading: false, @@ -89,9 +113,10 @@ export default class KeyBackupPanel extends React.PureComponent { if (this._unmounted) return; this.setState({ error: e, + backupInfo: null, + backupSigStatus: null, loading: false, }); - return; } } @@ -111,10 +136,10 @@ export default class KeyBackupPanel extends React.PureComponent { Modal.createTrackedDialog('Delete Backup', '', QuestionDialog, { title: _t('Delete Backup'), description: _t( - "Delete your backed up encryption keys from the server? " + - "You will no longer be able to use your recovery key to read encrypted message history", + "Are you sure? You will lose your encrypted messages if your " + + "keys are not backed up properly.", ), - button: _t('Delete backup'), + button: _t('Delete Backup'), danger: true, onFinished: (proceed) => { if (!proceed) return; @@ -135,6 +160,10 @@ export default class KeyBackupPanel extends React.PureComponent { render() { const Spinner = sdk.getComponent("elements.Spinner"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); + const encryptedMessageAreEncrypted = _t( + "Encrypted messages are secured with end-to-end encryption. " + + "Only you and the recipient(s) have the keys to read these messages.", + ); if (this.state.error) { return ( @@ -145,14 +174,25 @@ export default class KeyBackupPanel extends React.PureComponent { } else if (this.state.loading) { return ; } else if (this.state.backupInfo) { + const EmojiText = sdk.getComponent('elements.EmojiText'); let clientBackupStatus; + let restoreButtonCaption = _t("Restore from Backup"); + if (MatrixClientPeg.get().getKeyBackupEnabled()) { - clientBackupStatus = _t("This device is using key backup"); + clientBackupStatus =
    +

    {encryptedMessageAreEncrypted}

    +

    {_t("This device is backing up your keys. ")}

    +
    ; } else { - clientBackupStatus = _t( - "This device is not using key backup. Restore the backup to start using it.", {}, - {b: x => {x}}, - ); + clientBackupStatus =
    +

    {encryptedMessageAreEncrypted}

    +

    {_t( + "This device is not backing up your keys.", {}, + {b: sub => {sub}}, + )}

    +

    {_t("Back up your keys before signing out to avoid losing them.")}

    +
    ; + restoreButtonCaption = _t("Use key backup"); } let uploadStatus; @@ -243,18 +283,25 @@ export default class KeyBackupPanel extends React.PureComponent {

    - { _t("Restore backup") } + {restoreButtonCaption}     - { _t("Delete backup") } + { _t("Delete Backup") }

    ; } else { return
    - {_t("No backup is present")}

    +
    +

    {_t( + "Your keys are not being backed up from this device.", {}, + {b: sub => {sub}}, + )}

    +

    {encryptedMessageAreEncrypted}

    +

    {_t("Back up your keys before signing out to avoid losing them.")}

    +
    - { _t("Start a new backup") } + { _t("Start using Key Backup") }
    ; } diff --git a/src/components/views/settings/tabs/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/GeneralUserSettingsTab.js index bfcc7b945c..2364475239 100644 --- a/src/components/views/settings/tabs/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralUserSettingsTab.js @@ -54,6 +54,7 @@ export default class GeneralUserSettingsTab extends React.Component { if (this.state.theme === newTheme) return; SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme); + this.setState({theme: newTheme}); dis.dispatch({action: 'set_theme', value: newTheme}); }; @@ -138,17 +139,14 @@ export default class GeneralUserSettingsTab extends React.Component { } _renderThemeSection() { - // TODO: Re-enable theme selection once the themes actually work const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return (
    {_t("Theme")} - - + - -
    @@ -164,7 +162,7 @@ export default class GeneralUserSettingsTab extends React.Component { {_t("Deactivating your account is a permanent action - be careful!")} - {_t("Close Account")} + {_t("Deactivate Account")}
    ); diff --git a/src/components/views/settings/tabs/VoiceSettingsTab.js b/src/components/views/settings/tabs/VoiceSettingsTab.js index 65f38c7841..aefb114dd3 100644 --- a/src/components/views/settings/tabs/VoiceSettingsTab.js +++ b/src/components/views/settings/tabs/VoiceSettingsTab.js @@ -174,7 +174,7 @@ export default class VoiceSettingsTab extends React.Component { {microphoneDropdown} {webcamDropdown} - +
    ); diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 230f228dba..e92648a0d2 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -245,7 +245,7 @@ "No display name": "Няма име", "New passwords don't match": "Новите пароли не съвпадат", "Passwords can't be empty": "Полето с парола не може да е празно", - "Export E2E room keys": "Експортирай E2E ключове за стая", + "Export E2E room keys": "Експортирай E2E ключове", "Do you want to set an email address?": "Искате ли да зададете имейл адрес?", "Current password": "Текуща парола", "Password": "Парола", @@ -430,7 +430,7 @@ "Privileged Users": "Потребители с привилегии", "No users have specific privileges in this room": "Никой няма специфични привилегии в тази стая", "Banned users": "Блокирани потребители", - "This room is not accessible by remote Matrix servers": "Тази стая не е достъпна за далечни Matrix сървъри", + "This room is not accessible by remote Matrix servers": "Тази стая не е достъпна за отдалечени Matrix сървъри", "Leave room": "Напусни стаята", "Tagged as: ": "Етикет: ", "To link to a room it must have an address.": "За да дадете линк към стаята, тя трябва да има адрес.", @@ -466,7 +466,7 @@ "'%(alias)s' is not a valid format for an address": "%(alias)s не е валиден формат на адрес", "not specified": "неопределен", "not set": "незададен", - "Remote addresses for this room:": "Далечни адреси на тази стая:", + "Remote addresses for this room:": "Отдалечени адреси на тази стая:", "Addresses": "Адреси", "The main address for this room is": "Основният адрес на тази стая е", "Local addresses for this room:": "Локалните адреси на тази стая са:", @@ -822,13 +822,13 @@ "User Interface": "Потребителски интерфейс", "Autocomplete Delay (ms):": "Забавяне на визуализацията на автоматичните подсказки (мс):", "": "<не се поддържа>", - "Import E2E room keys": "Импортирай E2E ключове за стая", + "Import E2E room keys": "Импортирай E2E ключове", "Cryptography": "Криптография", "Device ID:": "Идентификатор на устройството:", "Device key:": "Ключ на устройството:", "Ignored Users": "Игнорирани потребители", "Riot collects anonymous analytics to allow us to improve the application.": "Riot събира анонимни статистики, за да ни позволи да подобрим приложението.", - "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Поверителността е важна за нас, затова за нашите статистики не събираме лични или идентифициращи Вас данни.", + "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Поверителността е важна за нас, затова не събираме лични или идентифициращи Вас данни.", "Learn more about how we use analytics.": "Научете повече за това как използваме статистическите данни.", "Labs": "Експерименти", "These are experimental features that may break in unexpected ways": "Това са експериментални функции , които могат да се счупят по неочаквани начини", @@ -1003,7 +1003,7 @@ "Unable to fetch notification target list": "Неуспешно извличане на списък с устройства получаващи известия", "Add an email address above to configure email notifications": "За конфигурация на имейл известия добавете имейл адрес по-горе.", "Expand panel": "Разшири панела", - "On": "Включено", + "On": "Вкл.", "%(count)s Members|other": "%(count)s Членове", "Filter room names": "Филтрирай стаи по име", "Changelog": "Списък на промените", @@ -1118,7 +1118,7 @@ "Set Password": "Задаване на парола", "An error occurred whilst saving your email notification preferences.": "Възникна грешка при запазване на настройките за имейл известяване.", "Enable audible notifications in web client": "Включване на звукови известия в уеб клиент", - "Off": "Изключено", + "Off": "Изкл.", "Riot does not know how to join a room on this network": "Riot не знае как да се присъедини към стая от тази мрежа", "Mentions only": "Само при споменаване", "You can now return to your account after signing out, and sign in on other devices.": "Вече можете да се върнете в профила си след излизане от него и да влезете от други устройства.", @@ -1201,7 +1201,7 @@ "A call is already in progress!": "В момента вече тече разговор!", "You have no historical rooms": "Нямате стаи в архива", "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "В шифровани стаи като тази, по подразбиране URL прегледите са изключени, за да се подсигури че сървърът (където става генерирането на прегледите) не може да събира информация за връзките споделени в стаята.", - "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Когато някой сподели URL връзка в съобщение, може да бъде показан URL преглед даващ повече информация за връзката (заглавие, описание и картинка от уебсайта).", + "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Когато се сподели URL връзка в съобщение, може да бъде показан URL преглед даващ повече информация за връзката (заглавие, описание и картинка от уебсайта).", "The email field must not be blank.": "Имейл полето не може да бъде празно.", "The user name field must not be blank.": "Полето за потребителско име не може да е празно.", "The phone number field must not be blank.": "Полето за телефонен номер не може да е празно.", @@ -1515,7 +1515,7 @@ "2018 theme": "Тема 2018", "Account management": "Управление на акаунта", "Deactivating your account is a permanent action - be careful!": "Деактивирането на акаунта е необратимо действие - внимавайте!", - "Close Account": "Затвори акаунта", + "Deactivate Account": "Затвори акаунта", "For help with using Riot, click here.": "За помощ при използването на Riot, кликнете тук.", "For help with using Riot, click here or start a chat with our bot using the button below.": "За помощ при използването на Riot, кликнете тук или започнете чат с бота ни използвайки бутона по-долу.", "Chat with Riot Bot": "Чати с Riot Bot", @@ -1530,7 +1530,7 @@ "Autocomplete delay (ms)": "Забавяне преди подсказки (милисекунди)", "Roles & Permissions": "Роли и привилегии", "To link to this room, please add an alias.": "За да генерирате връзки към тази стая, въведете адрес за нея.", - "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Промени в настройките за четене на историята се прилагат само за нови съобщения. Видимостта на съществуващата история остава непроменена.", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Промени в настройките за четене на историята касаят само за нови съобщения. Видимостта на съществуващата история не се променя.", "Security & Privacy": "Сигурност и поверителност", "Encryption": "Шифроване", "Once enabled, encryption cannot be disabled.": "Веднъж включено, шифроването не може да бъде изключено.", @@ -1618,5 +1618,149 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Устройството откри, че паролата за възстановяване и ключът за Защитени Съобщения са били премахнати.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Ако сте извършили това по погрешка, може да настройте Защитени Съобщения за това устройство, което ще зашифрова наново историята на съобщенията за това устройство, използвайки новия метод за възстановяване.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Ако не сте премахнали метода за възстановяване, е възможно нападател да се опитва да получи достъп до акаунта Ви. Променете паролата на акаунта и настройте нов метод за възстановяване веднага от Настройки.", - "Tabbed settings": "Настройки по раздели" + "Tabbed settings": "Настройки по раздели", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Файлът '%(fileName)s' надхвърля ограничението за размер на файлове за този сървър", + "Gets or sets the room topic": "Взима или настройва темата на стаята", + "This room has no topic.": "Тази стая няма тема.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s направи стаята публична за всеки знаещ връзката.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s направи стаята само за поканени.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s промени правилото за влизане на %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s позволи на гости да влизат в стаята.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s спря достъпа на гости за влизане в стаята.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s промени правилото за достъп на гости на %(rule)s", + "Group & filter rooms by custom tags (refresh to apply changes)": "Групирай & филтрирай стаи по собствен етикет (презареди, за да влезе в сила)", + "Verify this user by confirming the following emoji appear on their screen.": "Потвърдете този потребител, като установите че следното емоджи се вижда на екрана им.", + "Unable to find a supported verification method.": "Не може да бъде намерен поддържан метод за потвърждение.", + "Dog": "Куче", + "Cat": "Котка", + "Lion": "Лъв", + "Horse": "Кон", + "Unicorn": "Еднорог", + "Pig": "Прасе", + "Elephant": "Слон", + "Rabbit": "Заек", + "Panda": "Панда", + "Rooster": "Петел", + "Penguin": "Пингвин", + "Turtle": "Костенурка", + "Fish": "Риба", + "Octopus": "Октопод", + "Butterfly": "Пеперуда", + "Flower": "Цвете", + "Tree": "Дърво", + "Cactus": "Кактус", + "Mushroom": "Гъба", + "Globe": "Земя", + "Moon": "Луна", + "Cloud": "Облак", + "Fire": "Огън", + "Banana": "Банан", + "Apple": "Ябълка", + "Strawberry": "Ягода", + "Corn": "Царевица", + "Pizza": "Пица", + "Cake": "Торта", + "Heart": "Сърце", + "Smiley": "Усмивка", + "Robot": "Робот", + "Hat": "Шапка", + "Glasses": "Очила", + "Spanner": "Гаечен ключ", + "Santa": "Дядо Коледа", + "Thumbs up": "Палец нагоре", + "Umbrella": "Чадър", + "Hourglass": "Пясъчен часовник", + "Clock": "Часовник", + "Gift": "Подарък", + "Light bulb": "Лампа", + "Book": "Книга", + "Pencil": "Молив", + "Paperclip": "Кламер", + "Scisors": "Ножица", + "Padlock": "Катинар", + "Key": "Ключ", + "Hammer": "Чук", + "Telephone": "Телефон", + "Flag": "Флаг", + "Train": "Влак", + "Bicycle": "Колело", + "Aeroplane": "Самолет", + "Rocket": "Ракета", + "Trophy": "Трофей", + "Ball": "Топка", + "Guitar": "Китара", + "Trumpet": "Тромпет", + "Bell": "Звънец", + "Anchor": "Котва", + "Headphones": "Слушалки", + "Folder": "Папка", + "Pin": "Кабърче", + "Your homeserver does not support device management.": "Вашият сървър не поддържа управление на устройствата.", + "This device is not using key backup. Restore the backup to start using it.": "Това устройство не ползва резервно копие на ключовете. Възстановете резервното копие за да започнете да го използвате.", + "This backup is trusted because it has been restored on this device": "Това резервно копие е доверено, понеже е било възстановено на това устройство", + "Some devices for this user are not trusted": "Някои устройства на този потребител не са доверени", + "Some devices in this encrypted room are not trusted": "Някои устройства в тази шифрована стая не са доверени", + "All devices for this user are trusted": "Всички устройства на този потребител са доверени", + "All devices in this encrypted room are trusted": "Всички устройства в тази шифрована стая са доверени", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "Защитено резервно копие на ключовете трябва да е активно на всички Ваши устройства, за да предотвратите загуба на достъп до шифрованите съобщения.", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "Направете защитено резервно копие на ключовете за шифроване върху сървъра, за да подсигурите че винаги ще може да четете шифрованите съобщения.", + "Don't risk losing your encrypted messages!": "Не рискувайте да загубите шифрованите съобщения!", + "Activate Secure Key Backup": "Активирай защитено резервно копие на ключовете", + "No thanks, I'll download a copy of my decryption keys before I log out": "Не благодаря, ще сваля копие на ключовете за шифроване преди да изляза от профила", + "Room Settings": "Настройки на стаята", + "Recovery Key Mismatch": "Ключа за възстановяване не съвпада", + "Incorrect Recovery Passphrase": "Неправилна парола за възстановяване", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "Резервното копие не можа да бъде разшифровано с тази парола: потвърдете, че сте въвели правилната парола.", + "This homeserver would like to make sure you are not a robot.": "Сървърът иска да потвърди, че не сте робот.", + "Sign in to %(serverName)s": "Вход в %(serverName)s", + "Change": "Промени", + "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "Използвай имейл адрес за да възстановиш акаунта си. Други потребители могат да те канят в стаи посредством информацията за контакт.", + "Couldn't load page": "Страницата не можа да бъде заредена", + "This homeserver does not support communities": "Този сървър не поддържа общности", + "Failed to get protocol list from homeserver": "Неуспешно взимане на списъка с протоколи от сървъра", + "The homeserver may be too old to support third party networks": "Този сървър може да е прекалено стар и да не поддържа външни мрежи", + "Your account on %(serverName)s": "Вашият акаунт в %(serverName)s", + "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "%(hsUrl)s не изглежда да е валиден адрес на сървър. Въведете валиден адрес, започващ с името на протокола (https://..).", + "A verification email will be sent to your inbox to confirm setting your new password.": "Ще Ви бъде изпратен имейл за потвърждение на новата парола.", + "Your password has been reset.": "Паролата беше анулирана.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Бяхте изхвърлени от профила на всички ваши устройства и вече няма да получавате известия на тях. За да включите известията отново, влезте пак от всяко едно устройство.", + "This homeserver does not support login using email address.": "Този сървър не поддържа влизане в профил посредством имейл адрес.", + "Guest access is disabled on this homeserver.": "Достъпът за гости е изключен на този сървър.", + "Registration has been disabled on this homeserver.": "Регистрацията е изключена на този сървър.", + "Unable to query for supported registration methods.": "Неуспешно взимане на поддържаните методи за регистрация.", + "Show recent room avatars above the room list (refresh to apply changes)": "Показвай снимки на скоро-използваните стаи над списъка със стаите (презаредете за да влезе в сила)", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Сигурни ли сте? Ако нямате работещо резервно копие на ключовете, ще загубите достъп до шифрованите съобщения.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Шифрованите съобщения са защитени с шифроване от край до край. Само Вие и получателят (получателите) имате ключове за четенето им.", + "Restore from Backup": "Възстанови от резервно копие", + "This device is backing up your keys. ": "Това устройство прави резервни копия на ключовете. ", + "This device is not backing up your keys.": "Това устройство не прави резервно копия на ключовете.", + "Back up your keys before signing out to avoid losing them.": "Направете резервно копие на ключовете преди изход от профила, за да не ги загубите.", + "Use key backup": "Използвай резервно копие на ключовете", + "Your keys are not being backed up from this device.": "Това устройство не прави резервно копие на ключовете Ви.", + "Start using Key Backup": "Започни използване на резервни копия за ключове", + "Use Key Backup": "Използвай резервно копие на ключовете", + "Never lose encrypted messages": "Никога не губете шифровани съобщения", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Съобщенията в тази стая са защитени с шифроване от край до край. Само Вие и получателят (получателите) имате ключове за разчитането им.", + "Securely back up your keys to avoid losing them. Learn more.": "Правете защитено резервно копие на ключовете, за да не ги загубите. Научи повече.", + "Not now": "Не сега", + "Don't ask me again": "Не ме питай пак", + "Nothing appearing? Not all clients support interactive verification yet. .": "Нищо не се появява? Не всички клиенти поддържат интерактивно потвърждение. .", + "I don't want my encrypted messages": "Не искам шифрованите си съобщения", + "Manually export keys": "Експортирай ключове ръчно", + "You'll lose access to your encrypted messages": "Ще загубите достъп до шифрованите си съобщения", + "Are you sure you want to sign out?": "Сигурни ли сте, че искате да излезете от профила?", + "Warning: you should only set up key backup from a trusted computer.": "Внимание: настройването на резервно копие на ключовете трябва да се прави само от доверен компютър.", + "Hide": "Скрий", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Ще съхраним шифровано копие на ключовете на сървърът ни. Предпазете резервното копие с парола.", + "For maximum security, this should be different from your account password.": "За максимална сигурност, по-добре паролата да е различна от тази за акаунта Ви.", + "Set up with a Recovery Key": "Настрой с ключ за възстановяване", + "Please enter your passphrase a second time to confirm.": "Въведете паролата отново за потвърждение.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Ключът за възстановяване дава допълнителна сигурност - може да го използвате за да възстановите достъпа до шифрованите съобщения, в случай че забравите паролата.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Пазете ключът за възстановяване на много сигурно място, например в password manager програма или сейф", + "Your keys are being backed up (the first backup could take a few minutes).": "Прави се резервно копие на ключовете Ви (първото копие може да отнеме няколко минути).", + "Okay": "Добре", + "Secure your backup with a passphrase": "Защитете резервното копие с парола", + "Confirm your passphrase": "Потвърдете паролата", + "Recovery key": "Ключ за възстановяване", + "Success!": "Успешно!" } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index ee114c51aa..8c85581e10 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1480,7 +1480,7 @@ "Enable Community Filter Panel": "Community-Filter-Panel aktivieren", "Messages containing my username": "Nachrichten, die meinen Benutzernamen enthalten", "The other party cancelled the verification.": "Die Gegenstelle hat die Überprüfung abgebrochen.", - "Verified!": "Bestätigt!", + "Verified!": "Verifiziert!", "You've successfully verified this user.": "Du hast diesen Benutzer erfolgreich verifiziert.", "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Sichere Nachrichten mit diesem Benutzer sind Ende-zu-Ende-verschlüsselt und können nicht von Dritten gelesen werden.", "Got It": "Verstanden", @@ -1516,7 +1516,7 @@ "Theme": "Design", "2018 theme": "2018 Design", "Account management": "Benutzerkontenverwaltung", - "Close Account": "Benutzerkonto schließen", + "Deactivate Account": "Benutzerkonto schließen", "For help with using Riot, click here.": "Um Hilfe zur Benutzung von Riot zu erhalten, klicke hier.", "For help with using Riot, click here or start a chat with our bot using the button below.": "Um Hilfe zur Benutzung von Riot zu erhalten, klicke hier oder beginne einen Chat mit unserem Bot, indem du den unteren Button klickst.", "Chat with Riot Bot": "Chatte mit dem Riot Bot", @@ -1528,5 +1528,122 @@ "Room Addresses": "Raum-Adressen", "Deactivating your account is a permanent action - be careful!": "Das Deaktivieren deines Kontos ist nicht widerruflich - sei vorsichtig!", "Preferences": "Einstellungen", - "Room list": "Raumliste" + "Room list": "Raumliste", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Die Datei '%(fileName)s' überschreitet die maximale Größe für Uploads auf diesem Heimserver", + "This room has no topic.": "Dieser Raum hat kein Thema.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s machte den Raum für jeden, der den Link kennt öffentlich.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s machte den Raum nur für Eingeladene zugreifbar.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s änderte die Zutrittsregel auf '%(rule)s'", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s erlaubte Gäste diesem Raum beizutreten.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s verbot Gästen diesem Raum beizutreten.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s änderte den Gastzugriff auf '%(rule)s'", + "Group & filter rooms by custom tags (refresh to apply changes)": "Gruppiere & filtere Räume nach angepassten Tags (neu laden um Änderungen zu übernehmen)", + "Unable to find a supported verification method.": "Konnte kein unterstützte Verifikationsmethode finden.", + "Dog": "Hund", + "Cat": "Katze", + "Lion": "Löwe", + "Horse": "Pferd", + "Unicorn": "Einhorn", + "Pig": "Schwein", + "Elephant": "Elefant", + "Rabbit": "Kaninchen", + "Panda": "Panda", + "Rooster": "Hahn", + "Penguin": "Pinguin", + "Turtle": "Schildkröte", + "Fish": "Fisch", + "Octopus": "Oktopus", + "Butterfly": "Schmetterling", + "Flower": "Blume", + "Tree": "Baum", + "Cactus": "Kaktus", + "Mushroom": "Pilz", + "Globe": "Globus", + "Moon": "Mond", + "Cloud": "Wolke", + "Fire": "Feuer", + "Banana": "Banane", + "Apple": "Apfel", + "Strawberry": "Erdbeere", + "Corn": "Weizen", + "Pizza": "Pizza", + "Cake": "Kuchen", + "Heart": "Herz", + "Smiley": "Smiley", + "Robot": "Roboter", + "Hat": "Hut", + "Glasses": "Brille", + "Spanner": "Schraubenschlüssel", + "Santa": "Nikolaus", + "Thumbs up": "Daumen hoch", + "Umbrella": "Regenschirm", + "Hourglass": "Sanduhr", + "Clock": "Uhr", + "Gift": "Geschenk", + "Light bulb": "Glühbirne", + "Book": "Buch", + "Pencil": "Stift", + "Paperclip": "Büroklammer", + "Scisors": "Schere", + "Padlock": "Vorhängeschloss", + "Key": "Schlüssel", + "Hammer": "Hammer", + "Telephone": "Telefon", + "Flag": "Flagge", + "Train": "Zug", + "Bicycle": "Fahrrad", + "Aeroplane": "Flugzeug", + "Rocket": "Rakete", + "Trophy": "Pokal", + "Ball": "Ball", + "Guitar": "Gitarre", + "Trumpet": "Trompete", + "Bell": "Glocke", + "Anchor": "Anker", + "Headphones": "Kopfhörer", + "Folder": "Ordner", + "Pin": "Stecknadel", + "Your homeserver does not support device management.": "Dein Heimserver unterstützt Geräte-Management nicht.", + "This device is not using key backup. Restore the backup to start using it.": "Dieses Gerät benutzt die Schlüsselsicherung nicht. Stelle die Sicherung wieder her um sie zu benutzen.", + "This backup is trusted because it has been restored on this device": "Dieser Sicherung wird vertraut, weil es auf diesem Gerät wiederhergestellt wurde", + "Timeline": "Chatverlauf", + "Autocomplete delay (ms)": "Verzögerung zur Autvervollständigung (ms)", + "Roles & Permissions": "Rollen & Berechtigungen", + "To link to this room, please add an alias.": "Um zu diesem Raum zu verlinken, füge bitte einen Alias hinzu.", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Änderungen daran, wer den Chatverlauf lesen kann werden nur zukünftige Nachrichten in diesem Raum angewendet. Die Sichtbarkeit der existierenden Historie bleibt unverändert.", + "Security & Privacy": "Sicherheit & Datenschutz", + "Encryption": "Verschlüsselung", + "Once enabled, encryption cannot be disabled.": "Sobald aktiviert, kann die Verschlüsselung nicht mehr deaktiviert werden.", + "Encrypted": "Verschlüsselt", + "Ignored users": "Ignorierte Benutzer", + "Key backup": "Schlüsselsicherung", + "Gets or sets the room topic": "Frage das Thema des Raums ab oder setze es", + "Verify this user by confirming the following emoji appear on their screen.": "Verifizieren Sie diesen Benutzer, indem Sie bestätigen, dass folgendes Emoji auf dessen Bildschirm erscheint.", + "Missing media permissions, click the button below to request.": "Fehlende Medienberechtigungen. Drücke auf den Knopf unten, um sie anzufordern.", + "Request media permissions": "Medienberechtigungen anfordern", + "Some devices for this user are not trusted": "Nicht allen Geräte dieses Benutzers wird vertraut", + "Some devices in this encrypted room are not trusted": "Nicht allen Geräten in diesem Verschlüsselten Raum wird vertraut", + "All devices for this user are trusted": "Allen Geräten dieses Benutzers wird vertraut", + "All devices in this encrypted room are trusted": "Allen Geräten in diesem Raum wird vertraut", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "Secure Key Backup sollte auf allen Geräten aktiviert sein, damit Du den Zugang zu deinen verschlüsselten Nachrichten nicht verlierst.", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "Speichere deine Entschlüsselungskeys sicher auf dem Server, um sicherzustellen dass Du immer Zugang auf deine verschlüsselten Nachrichten hast.", + "Don't risk losing your encrypted messages!": "Gehe nicht das Risiko ein, Zugang zu deinen verschlüsselten Nachrichten zu verlieren!", + "Activate Secure Key Backup": "Secure Key Backup aktivieren", + "No thanks, I'll download a copy of my decryption keys before I log out": "Nein danke, ich lade eine Kopie meiner Entschlüsselungskeys herunter bevor ich mich auslogge", + "This room is using an unstable room version. If you aren't expecting this, please upgrade the room.": "Dieser Raum verwendet eine instabile Raumversion. Bitte upgrade den Raum, wenn du nicht damit gerechnet hast.", + "Click here to upgrade to the latest room version.": "Hier klicken, um auf die neueste Raumversion upzugraden.", + "Main address": "Primäre Adresse", + "Room avatar": "Raum-Bild", + "Upload room avatar": "Raum-Bild hochladen", + "No room avatar": "Kein Raum-Bild", + "Room Name": "Raum-Name", + "Room Topic": "Raum-Thema", + "Join": "Beitreten", + "Use Legacy Verification (for older clients)": "Legacy Verifizierung verwenden (für veraltete Clients)", + "Verify by comparing a short text string.": "Verifizieren durch Vergleichen eines kurzen Textes.", + "Begin Verifying": "Verifizierung beginnen", + "Waiting for partner to accept...": "Warte auf Annahme durch den Gesprächspartner...", + "Use two-way text verification": "Bidirektionale Textverifizierung verwenden", + "Waiting for partner to confirm...": "Warte auf Bestätigung des Gesprächspartners...", + "Incoming Verification Request": "Eingehende Verifikationsanfrage" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f48e306641..fe41beb7ae 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -270,6 +270,7 @@ "Failed to join room": "Failed to join room", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", + "Show recent room avatars above the room list (refresh to apply changes)": "Show recent room avatars above the room list (refresh to apply changes)", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", @@ -291,7 +292,7 @@ "Automatically replace plain text Emoji": "Automatically replace plain text Emoji", "Mirror local video feed": "Mirror local video feed", "Enable Community Filter Panel": "Enable Community Filter Panel", - "Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls", + "Allow Peer-to-Peer for 1:1 calls": "Allow Peer-to-Peer for 1:1 calls", "Send analytics data": "Send analytics data", "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device", "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", @@ -439,11 +440,14 @@ "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", "Delete Backup": "Delete Backup", - "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history": "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history", - "Delete backup": "Delete backup", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", "Unable to load key backup status": "Unable to load key backup status", - "This device is using key backup": "This device is using key backup", - "This device is not using key backup. Restore the backup to start using it.": "This device is not using key backup. Restore the backup to start using it.", + "Restore from Backup": "Restore from Backup", + "This device is backing up your keys. ": "This device is backing up your keys. ", + "This device is not backing up your keys.": "This device is not backing up your keys.", + "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", + "Use key backup": "Use key backup", "Backing up %(sessionsRemaining)s keys...": "Backing up %(sessionsRemaining)s keys...", "All keys backed up": "All keys backed up", "Backup has a signature from unknown device with ID %(deviceId)s.": "Backup has a signature from unknown device with ID %(deviceId)s.", @@ -457,9 +461,8 @@ "Advanced": "Advanced", "Backup version: ": "Backup version: ", "Algorithm: ": "Algorithm: ", - "Restore backup": "Restore backup", - "No backup is present": "No backup is present", - "Start a new backup": "Start a new backup", + "Your keys are not being backed up from this device.": "Your keys are not being backed up from this device.", + "Start using Key Backup": "Start using Key Backup", "Error saving email notification preferences": "Error saving email notification preferences", "An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.", "Keywords": "Keywords", @@ -519,13 +522,11 @@ "Phone numbers": "Phone numbers", "Language and region": "Language and region", "Theme": "Theme", - "Light theme": "Light theme", + "Default theme": "Default theme", "Dark theme": "Dark theme", - "2018 theme": "2018 theme", - "Status.im theme": "Status.im theme", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", - "Close Account": "Close Account", + "Deactivate Account": "Deactivate Account", "Legal": "Legal", "For help with using Riot, click here.": "For help with using Riot, click here.", "For help with using Riot, click here or start a chat with our bot using the button below.": "For help with using Riot, click here or start a chat with our bot using the button below.", @@ -785,11 +786,12 @@ "You are trying to access a room.": "You are trying to access a room.", "Click here to join the discussion!": "Click here to join the discussion!", "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", - "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.", - "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.", - "Don't risk losing your encrypted messages!": "Don't risk losing your encrypted messages!", - "Activate Secure Key Backup": "Activate Secure Key Backup", - "No thanks, I'll download a copy of my decryption keys before I log out": "No thanks, I'll download a copy of my decryption keys before I log out", + "Use Key Backup": "Use Key Backup", + "Never lose encrypted messages": "Never lose encrypted messages", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", + "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", + "Not now": "Not now", + "Don't ask me again": "Don't ask me again", "Add a topic": "Add a topic", "This room is using an unstable room version. If you aren't expecting this, please upgrade the room.": "This room is using an unstable room version. If you aren't expecting this, please upgrade the room.", "Click here to upgrade to the latest room version.": "Click here to upgrade to the latest room version.", @@ -1049,7 +1051,6 @@ "Continue With Encryption Disabled": "Continue With Encryption Disabled", "Unknown error": "Unknown error", "Incorrect password": "Incorrect password", - "Deactivate Account": "Deactivate Account", "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.", @@ -1061,6 +1062,7 @@ "Verify by comparing a short text string.": "Verify by comparing a short text string.", "Begin Verifying": "Begin Verifying", "Waiting for partner to accept...": "Waiting for partner to accept...", + "Nothing appearing? Not all clients support interactive verification yet. .": "Nothing appearing? Not all clients support interactive verification yet. .", "Waiting for %(userId)s to confirm...": "Waiting for %(userId)s to confirm...", "Use two-way text verification": "Use two-way text verification", "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", @@ -1100,11 +1102,10 @@ "Clear cache and resync": "Clear cache and resync", "Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!", "Updating Riot": "Updating Riot", - "When you log out, you'll lose your secure message history. To prevent this, set up a recovery method.": "When you log out, you'll lose your secure message history. To prevent this, set up a recovery method.", - "Alternatively, advanced users can also manually export encryption keys in Settings before logging out.": "Alternatively, advanced users can also manually export encryption keys in Settings before logging out.", - "Set a Recovery Method": "Set a Recovery Method", - "I understand, log out without": "I understand, log out without", - "When signing in again, you can access encrypted chat history by restoring your key backup. You'll need your recovery passphrase or, if you didn't set a recovery passphrase, your recovery key (that you downloaded).": "When signing in again, you can access encrypted chat history by restoring your key backup. You'll need your recovery passphrase or, if you didn't set a recovery passphrase, your recovery key (that you downloaded).", + "I don't want my encrypted messages": "I don't want my encrypted messages", + "Manually export keys": "Manually export keys", + "You'll lose access to your encrypted messages": "You'll lose access to your encrypted messages", + "Are you sure you want to sign out?": "Are you sure you want to sign out?", "Thanks for testing the Riot Redesign. If you run into any bugs or visual issues, please let us know on GitHub.": "Thanks for testing the Riot Redesign. If you run into any bugs or visual issues, please let us know on GitHub.", "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.", "Report bugs & give feedback": "Report bugs & give feedback", @@ -1171,6 +1172,7 @@ "Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!", "Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys", "Enter Recovery Passphrase": "Enter Recovery Passphrase", + "Warning: you should only set up key backup from a trusted computer.": "Warning: you should only set up key backup from a trusted computer.", "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Access your secure message history and set up secure messaging by entering your recovery passphrase.", "Next": "Next", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options", @@ -1200,6 +1202,7 @@ "Quote": "Quote", "Source URL": "Source URL", "Collapse Reply Thread": "Collapse Reply Thread", + "End-to-end encryption information": "End-to-end encryption information", "Failed to set Direct Message status of room": "Failed to set Direct Message status of room", "unknown error code": "unknown error code", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", @@ -1216,6 +1219,9 @@ "Set status": "Set status", "Set a new status...": "Set a new status...", "View Community": "View Community", + "Hide": "Hide", + "Sign in": "Sign in", + "Welcome": "Welcome", "Login": "Login", "powered by Matrix": "powered by Matrix", "Robot check is currently unavailable on desktop - please use a web browser": "Robot check is currently unavailable on desktop - please use a web browser", @@ -1246,7 +1252,6 @@ "Username": "Username", "Mobile phone number": "Mobile phone number", "Not sure of your password? Set a new one": "Not sure of your password? Set a new one", - "Sign in": "Sign in", "Sign in to %(serverName)s": "Sign in to %(serverName)s", "Change": "Change", "Sign in with": "Sign in with", @@ -1469,7 +1474,6 @@ "unencrypted": "unencrypted", "Decryption error": "Decryption error", "Session ID": "Session ID", - "End-to-end encryption information": "End-to-end encryption information", "Event information": "Event information", "Sender device information": "Sender device information", "Passphrases must match": "Passphrases must match", @@ -1487,19 +1491,19 @@ "Import": "Import", "Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.", "Keep going...": "Keep going...", - "Secure your encrypted message history with a Recovery Passphrase.": "Secure your encrypted message history with a Recovery Passphrase.", - "You'll need it if you log out or lose access to this device.": "You'll need it if you log out or lose access to this device.", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.", + "For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.", "Enter a passphrase...": "Enter a passphrase...", - "If you don't want encrypted message history to be available on other devices, .": "If you don't want encrypted message history to be available on other devices, .", - "Or, if you don't want to create a Recovery Passphrase, skip this step and .": "Or, if you don't want to create a Recovery Passphrase, skip this step and .", + "Set up with a Recovery Key": "Set up with a Recovery Key", "That matches!": "That matches!", "That doesn't match.": "That doesn't match.", "Go back to set it again.": "Go back to set it again.", - "Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.": "Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.", + "Please enter your passphrase a second time to confirm.": "Please enter your passphrase a second time to confirm.", "Repeat your passphrase...": "Repeat your passphrase...", "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.", "As a safety net, you can use it to restore your encrypted message history.": "As a safety net, you can use it to restore your encrypted message history.", - "Make a copy of this Recovery Key and keep it safe.": "Make a copy of this Recovery Key and keep it safe.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Keep your recovery key somewhere very secure, like a password manager (or a safe)", "Your Recovery Key": "Your Recovery Key", "Copy to clipboard": "Copy to clipboard", "Download": "Download", @@ -1508,15 +1512,16 @@ "Print it and store it somewhere safe": "Print it and store it somewhere safe", "Save it on a USB key or backup drive": "Save it on a USB key or backup drive", "Copy it to your personal cloud storage": "Copy it to your personal cloud storage", - "Your encryption keys are now being backed up in the background to your Homeserver. The initial backup could take several minutes. You can view key backup upload progress in Settings.": "Your encryption keys are now being backed up in the background to your Homeserver. The initial backup could take several minutes. You can view key backup upload progress in Settings.", + "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).", + "Okay": "Okay", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.", "Set up Secure Message Recovery": "Set up Secure Message Recovery", - "Create a Recovery Passphrase": "Create a Recovery Passphrase", - "Confirm Recovery Passphrase": "Confirm Recovery Passphrase", - "Recovery Key": "Recovery Key", + "Secure your backup with a passphrase": "Secure your backup with a passphrase", + "Confirm your passphrase": "Confirm your passphrase", + "Recovery key": "Recovery key", "Keep it safe": "Keep it safe", "Starting backup...": "Starting backup...", - "Backup Started": "Backup Started", + "Success!": "Success!", "Create Key Backup": "Create Key Backup", "Unable to create key backup": "Unable to create key backup", "Retry": "Retry", @@ -1529,7 +1534,6 @@ "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "This device is encrypting history using the new recovery method.": "This device is encrypting history using the new recovery method.", "Go to Settings": "Go to Settings", - "Setting up Secure Messages on this device will re-encrypt this device's message history with the new recovery method.": "Setting up Secure Messages on this device will re-encrypt this device's message history with the new recovery method.", "Set up Secure Messages": "Set up Secure Messages", "Recovery Method Removed": "Recovery Method Removed", "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "This device has detected that your recovery passphrase and key for Secure Messages have been removed.", diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 1a55796e6c..fb0c6c7eab 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -1097,11 +1097,11 @@ "Username available": "Nombre de usuario disponible", "This will be your account name on the homeserver, or you can pick a different server.": "Este será el nombre de su cuenta en el servidor doméstico, o puede elegir un servidor diferente.", "If you already have a Matrix account you can log in instead.": "Si ya tiene una cuenta de Matrix puede conectarse: log in.", - "Share Room": "Compartir Sala", + "Share Room": "Compartir sala", "Link to most recent message": "Enlazar a mensaje más reciente", - "Share User": "Compartir Usuario", + "Share User": "Compartir usuario", "Share Community": "Compartir Comunidad", - "Share Room Message": "Compartir Mensaje de Sala", + "Share Room Message": "Compartir el mensaje de la sala", "Link to selected message": "Enlazar a mensaje seleccionado", "COPY": "COPIAR", "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "Está actualmente prohibiendo dispositivos sin verificar; para enviar mensajes a los mismos deber verificarlos.", @@ -1267,5 +1267,20 @@ "%(senderName)s removed the main address for this room.": "%(senderName)s eliminó la dirección principal para esta sala.", "Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot ahora utiliza de 3 a 5 veces menos memoria, porque solo carga información sobre otros usuarios cuando es necesario. Por favor, ¡aguarda mientras volvemos a sincronizar con el servidor!", "Updating Riot": "Actualizando Riot", - "Unable to query for supported registration methods": "No es posible consultar por los métodos de registro compatibles" + "Unable to query for supported registration methods": "No es posible consultar por los métodos de registro compatibles", + "Room version:": "Versión de la sala:", + "Developer options": "Opciones de desarrollador", + "Room version": "Versión de la sala", + "Room information": "Información de la sala", + "Room Topic": "Tema de la sala", + "Theme": "Tema", + "2018 theme": "Tema 2018", + "Voice & Video": "Voz y video", + "Gets or sets the room topic": "Obtiene o establece el tema de la sala", + "This room has no topic.": "Esta sala no tiene tema.", + "Sets the room name": "Establece el nombre de la sala", + "Upload profile picture": "Subir imagen de perfil", + "Phone numbers": "Números de teléfono", + "Email addresses": "Correos electrónicos", + "Language and region": "Idioma y región" } diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index d9f796c03f..a53b4e2f24 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -1457,5 +1457,171 @@ "The following users may not exist": "Hurrengo erabiltzaileak agian ez dira existitzen", "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Ezin izan dira behean zerrendatutako Matrix ID-een profilak, berdin gonbidatu nahi dituzu?", "Invite anyway and never warn me again": "Gonbidatu edonola ere eta ez abisatu inoiz gehiago", - "Invite anyway": "Gonbidatu hala ere" + "Invite anyway": "Gonbidatu hala ere", + "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Identitate zerbitzari pertsonalizatu bat ere ezarri dezakezu, baina orduan ezin izango dituzu erabiltzaileak e-mail helbidearen bidez gonbidatu, edo ezin izango zaituzte e-mail helbidearen bidez zu gonbidatu.", + "Whether or not you're logged in (we don't record your username)": "Saioa hasi duzun ala ez (ez dugu zure erabiltzaile-izena gordetzen)", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "'%(fileName)s' fitxategiak igoerarako hasiera-zerbitzari honek duen tamaina muga gainditzen du", + "Upgrades a room to a new version": "Gela bat bertsio berriago batera eguneratzen du", + "Gets or sets the room topic": "Gelaren mintzagaia jaso edo ezartzen du", + "This room has no topic.": "Gela honek ez du mintzagairik.", + "Sets the room name": "Gelaren izena ezartzen du", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s erabiltzaileak gela hau eguneratu du.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s erabiltzaileak gela publikoa bihurtu du esteka dakien edonorentzat.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s erabiltzaileak gela soilik gonbidatuentzat bihurtu du.", + "%(displayName)s is typing …": "%(displayName)s idazten ari da …", + "%(names)s and %(count)s others are typing …|other": "%(names)s eta beste %(count)s idatzen ari dira …", + "%(names)s and %(count)s others are typing …|one": "%(names)s eta beste bat idazten ari dira …", + "%(names)s and %(lastPerson)s are typing …": "%(names)s eta %(lastPerson)s idazten ari dira …", + "Group & filter rooms by custom tags (refresh to apply changes)": "Taldekatu eta iragazi gelak etiketa pertsonalekin (freskatu aldaketak aplikatzeko)", + "Render simple counters in room header": "Jarri kontagailu sinpleak gelaren goiburuan", + "Enable Emoji suggestions while typing": "Gaitu Emojiak proposatzea idatzi bitartean", + "Show a placeholder for removed messages": "Erakutsi kendutako mezuek utzitako hutsunea", + "Show join/leave messages (invites/kicks/bans unaffected)": "Erakutsi elkartu/atera mezuak (gonbidapenak, kanporatzeak eta debekatzeei ez die eragiten)", + "Show avatar changes": "Erakutsi abatar aldaketak", + "Show display name changes": "Erakutsi pantaila-izenen aldaketak", + "Show read receipts": "Erakutsi irakurragiriak", + "Show avatars in user and room mentions": "Erakutsi abatarra kide eta gelen aipamenetan", + "Enable big emoji in chat": "Gaitu emoji handiak txatean", + "Send typing notifications": "Bidali idazte-jakinarazpenak", + "Enable Community Filter Panel": "Gaitu komunitateen iragazi panela", + "Messages containing my username": "Nire erabiltzaile-izena duten mezuak", + "The other party cancelled the verification.": "Beste parteak egiaztaketa ezeztatu du.", + "Verified!": "Egiaztatuta!", + "You've successfully verified this user.": "Ongi egiaztatu duzu erabiltzaile hau.", + "Got It": "Ulertuta", + "Dog": "Txakurra", + "Cat": "Katua", + "Lion": "Lehoia", + "Horse": "Zaldia", + "Unicorn": "Unikornioa", + "Pig": "Txerria", + "Elephant": "Elefantea", + "Rabbit": "Untxia", + "Panda": "Panda hartza", + "Rooster": "Oilarra", + "Penguin": "Pinguinoa", + "Turtle": "Dordoka", + "Fish": "Arraina", + "Octopus": "Olagarroa", + "Butterfly": "Tximeleta", + "Flower": "Lorea", + "Tree": "Zuhaitza", + "Cactus": "Kaktusa", + "Mushroom": "Perretxikoa", + "Globe": "Lurra", + "Moon": "Ilargia", + "Cloud": "Hodeia", + "Fire": "Sua", + "Banana": "Banana", + "Apple": "Sagarra", + "Strawberry": "Marrubia", + "Corn": "Artoa", + "Pizza": "Pizza", + "Cake": "Pastela", + "Heart": "Bihotza", + "Smiley": "Irrifartxoa", + "Robot": "Robota", + "Hat": "Txanoa", + "Glasses": "Betaurrekoak", + "Spanner": "Giltza ingelesa", + "Santa": "Santa", + "Umbrella": "Aterkina", + "Clock": "Erlojua", + "Gift": "Oparia", + "Light bulb": "Bonbilla", + "Book": "Liburua", + "Pencil": "Arkatza", + "Scisors": "Artaziak", + "Key": "Giltza", + "Hammer": "Mailua", + "Telephone": "Telefonoa", + "Room avatar": "Gelaren abatarra", + "Upload room avatar": "Igo gelaren abatarra", + "No room avatar": "Gelaren abatarrik ez", + "Room Name": "Gelaren izena", + "Room Topic": "Gelaren mintzagaia", + "Verify by comparing a short text string.": "Egiaztatu testu-kate labur bat konparatuz.", + "Begin Verifying": "Hasi egiaztaketa", + "Set a Recovery Method": "Ezarri berreskuratze metodo bat", + "I understand, log out without": "Ulertzen du, amaitu saioa hau gabe", + "Thanks for testing the Riot Redesign. If you run into any bugs or visual issues, please let us know on GitHub.": "Eskerrik asko Riot birdiseinatua probatzeagatik. Akatsen bat edo itxura okerreko zerbait aurkituz gero, jakinarazi GitHub bidez.", + "Report bugs & give feedback": "Eman akatsen berri eta egin iruzkinak", + "Go back": "Joan atzera", + "Room Settings": "Gelaren ezarpenak", + "Recovery Key Mismatch": "Berreskuratze gakoak ez datoz bat", + "Update status": "Eguneratu egoera", + "Set status": "Ezarri egoera", + "This homeserver would like to make sure you are not a robot.": "Hasiera-zerbitzari honek robota ez zarela egiaztatu nahi du.", + "Your Modular server": "Zure Modular zerbitzaria", + "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.": "Sartu zure Modular hasiera-zerbitzariaren helbidea. Zure domeinua erabili dezake edo modular.imren azpi-domeinua izan daiteke.", + "Server Name": "Zerbitzariaren izena", + "The username field must not be blank.": "Erabiltzaile-izena eremua ezin da hutsik egon.", + "Username": "Erabiltzaile-izena", + "Not sure of your password? Set a new one": "Ez duzu pasahitza gogoratzen? Ezarri beste bat", + "Sign in to %(serverName)s": "Hasi saioa %(serverName)s zerbitzarian", + "Change": "Aldatu", + "Create your account": "Sortu zure kontua", + "Create your %(serverName)s account": "Sortu zure %(serverName)s kontua", + "Email (optional)": "E-mail (aukerakoa)", + "Phone (optional)": "Telefonoa (aukerakoa)", + "Confirm": "Berretsi", + "Other servers": "Beste zerbitzariak", + "Homeserver URL": "Hasiera-zerbitzariaren URLa", + "Identity Server URL": "Identitate zerbitzariaren URLa", + "Free": "Dohan", + "Join millions for free on the largest public server": "Elkartu milioika pertsonekin dohain hasiera zerbitzari publiko handienean", + "Other": "Beste bat", + "Find other public servers or use a custom server": "Aurkitu beste zerbitzari publiko bat edo erabili zurea", + "Couldn't load page": "Ezin izan da orria kargatu", + "Guest": "Gonbidatua", + "Your account": "Zure kontua", + "Your account on %(serverName)s": "Zure %(serverName)s zerbitzariko kontua", + "General": "Orokorra", + "Room Addresses": "Gelaren helbideak", + "Set a new account password...": "Ezarri kontuaren pasahitz berria...", + "Email addresses": "E-mail helbideak", + "Phone numbers": "Telefono zenbakiak", + "Language and region": "Hizkuntza eta eskualdea", + "Theme": "Itxura", + "2018 theme": "2018ko itxura", + "Account management": "Kontuen kudeaketa", + "Deactivating your account is a permanent action - be careful!": "Kontua desgaitzea behin betiko ekintza bat da, kontuz ibili!", + "Deactivate Account": "Itxi kontua", + "For help with using Riot, click here.": "Riot erabiltzeko laguntza behar baduzu, egin klik hemen.", + "For help with using Riot, click here or start a chat with our bot using the button below.": "Riot erabiltzeko laguntza behar baduzu, egin klik hemen edo hasi txat bat gure botarekin beheko botoia sakatuz.", + "Chat with Riot Bot": "Txateatu Riot botarekin", + "Help & About": "Laguntza eta honi buruz", + "Bug reporting": "Akatsen berri ematea", + "FAQ": "FAQ", + "Versions": "Bertsioak", + "Preferences": "Hobespenak", + "Room list": "Gelen zerrenda", + "Timeline": "Denbora-lerroa", + "Autocomplete delay (ms)": "Automatikoki osatzeko atzerapena (ms)", + "Roles & Permissions": "Rolak eta baimenak", + "To link to this room, please add an alias.": "Gela honetara estekatzeko, gehitu ezizen bat.", + "Security & Privacy": "Segurtasuna eta pribatutasuna", + "Encryption": "Zifratzea", + "Once enabled, encryption cannot be disabled.": "Behin gaituta, zifratzea ezin da desgaitu.", + "Encrypted": "Zifratuta", + "Ignored users": "Ezikusitako erabiltzaileak", + "Key backup": "Gakoen babes-kopia", + "Voice & Video": "Ahotsa eta bideoa", + "Activate Secure Key Backup": "Aktibatu gako seguruen babes-kopia", + "This room is using an unstable room version. If you aren't expecting this, please upgrade the room.": "Gela honek gela bertsio desegonkor bat darabil. Hau ez bada espero zenuena, eguneratu gela.", + "Click here to upgrade to the latest room version.": "Egin klik hemen gela azken bertsiora eguneratzeko.", + "Main address": "Helbide nagusia", + "Join": "Elkartu", + "Incorrect Recovery Passphrase": "Berreskuratze pasaesaldi okerra", + "Premium": "Ordainpekoa", + "Premium hosting for organisations Learn more": "Elkarteentzako ordainpeko ostatua ikasi gehiago", + "Sign in instead": "Orduan hasi saioa", + "Your password has been reset.": "Zure pasahitza berrezarri da.", + "Set a new password": "Ezarri pasahitz berria", + "Create account": "Sortu kontua", + "You need to enter a username.": "Erabiltzaile-izen bat sartu behar duzu.", + "Keep going...": "Jarraitu...", + "Starting backup...": "Babes-kopia hasten...", + "Backup Started": "Babes-kopia hasita", + "Recovery Method Removed": "Berreskuratze metodoa kendu da" } diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index d39091b619..3670efc173 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -265,7 +265,7 @@ "Show panel": "Näytä paneeli", "Sign in": "Kirjaudu sisään", "Sign out": "Kirjaudu ulos", - "%(count)s of your messages have not been sent.|other": "Jotkut viesteistäsi ei ole lähetetty.", + "%(count)s of your messages have not been sent.|other": "Osaa viesteistäsi ei ole lähetetty.", "Someone": "Joku", "Start a chat": "Aloita keskustelu", "Start Chat": "Aloita keskustelu", @@ -924,7 +924,7 @@ "All notifications are currently disabled for all targets.": "Kaikki ilmoitukset on kytketty pois kaikilta kohteilta.", "Uploading report": "Ladataan raporttia", "Sunday": "Sunnuntai", - "Failed to add tag %(tagName)s to room": "Tagin %(tagName)s lisäämineen huoneelle ei onnistunut", + "Failed to add tag %(tagName)s to room": "Tagin %(tagName)s lisääminen huoneeseen epäonnistui", "Notification targets": "Ilmoituksen kohteet", "Failed to set direct chat tag": "Suoran viestittelyn tagin asettaminen epäonnistui", "Today": "Tänään", @@ -1049,7 +1049,7 @@ "Off": "Pois päältä", "Riot does not know how to join a room on this network": "Riot ei tiedä miten liittya huoneeseen tässä verkossa", "Mentions only": "Vain maininnat", - "Failed to remove tag %(tagName)s from room": "Tagin %(tagName)s poistaminen huoneelta epäonnistui", + "Failed to remove tag %(tagName)s from room": "Tagin %(tagName)s poistaminen huoneesta epäonnistui", "Wednesday": "Keskiviikko", "You can now return to your account after signing out, and sign in on other devices.": "Voit nyt palata tilillesi kirjauduttua ulos, sekä kirjautua muilla laitteilla.", "Enable email notifications": "Ota käyttöön sähköposti-ilmoitukset", @@ -1065,5 +1065,18 @@ "Collapse panel": "Piilota paneeli", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Nykyisellä selaimellasi ohjelman ulkonäkö voi olla aivan virheellinen, ja jotkut ominaisuudet eivät saata toimia. Voit jatkaa jos haluat kokeilla mutta et voi odottaa saavasi apua mahdollisesti ilmeneviin ongelmiin!", "Checking for an update...": "Tarkistetaan päivityksen saatavuutta...", - "There are advanced notifications which are not shown here": "On kehittyneitä ilmoituksia joita ei näytetä tässä" + "There are advanced notifications which are not shown here": "On kehittyneitä ilmoituksia joita ei näytetä tässä", + "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Voit myös määrittää toisen identiteettipalvelimen, mutta et voi kutsua muita käyttäjiä sähköpostin perusteella, eivätkä se voi kutsua sinua.", + "Your language of choice": "Valitsemasi kieli", + "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Yksityisyydensuoja on meille tärkeää, joten emme kerää mitään henkilökohtaista tai yksilöivää tietoa analytiikkaamme varten.", + "Send an encrypted reply…": "Lähetä salattu vastaus…", + "Send a reply (unencrypted)…": "Lähetä vastaus (salaamaton)…", + "Send an encrypted message…": "Lähetä salattu viesti…", + "Send a message (unencrypted)…": "Lähetä viesti (salaamaton)…", + "In reply to ": "Vastauksena käyttäjälle ", + "This room is not public. You will not be able to rejoin without an invite.": "Tämä huone ei ole julkinen. Tarvitset kutsun liittyäksesi huoneeseen.", + "%(count)s of your messages have not been sent.|one": "Viestiäsi ei lähetetty.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s asetti näyttönimekseen %(displayName)s.", + "Learn more about how we use analytics.": "Lue lisää analytiikkakäytännöistämme.", + "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Täällä ei ole muita! Haluaisitko kutsua muita tai lopettaa tyhjästä huoneesta huomauttamisen?" } diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 8e01397c25..1940a7999b 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1526,7 +1526,7 @@ "2018 theme": "Thème 2018", "Account management": "Gestion du compte", "Deactivating your account is a permanent action - be careful!": "La désactivation du compte est une action permanente. Soyez prudent !", - "Close Account": "Fermer le compte", + "Deactivate Account": "Fermer le compte", "For help with using Riot, click here.": "Pour obtenir de l'aide sur l'utilisation de Riot, cliquez ici.", "For help with using Riot, click here or start a chat with our bot using the button below.": "Pour obtenir de l'aide sur l'utilisation de Riot, cliquez ici ou commencez une discussion avec notre bot en utilisant le bouton ci-dessous.", "Start a chat with Riot Bot": "Commencer une discussion avec le bot Riot", @@ -1629,5 +1629,149 @@ "Recovery Method Removed": "Méthode de récupération supprimée", "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Cet appareil a détecté que votre phrase de passe et votre clé de récupération ont été supprimées.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Si vus avez fait cela par accident, vous pouvez configurer les messages sécurisés sur cet appareil, ce qui re-chiffrera l'historique des messages de cet appareil avec une nouvelle méthode de récupération.", - "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Si vous n'avez pas supprimé la méthode de récupération, un attaquant peut être en train d'essayer d'accéder à votre compte. Modifiez le mot de passe de votre compte et configurez une nouvelle méthode de récupération dans les réglages." + "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Si vous n'avez pas supprimé la méthode de récupération, un attaquant peut être en train d'essayer d'accéder à votre compte. Modifiez le mot de passe de votre compte et configurez une nouvelle méthode de récupération dans les réglages.", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Le fichier \"%(fileName)s\" dépasse la taille limite autorisée par ce serveur pour les téléchargements", + "Gets or sets the room topic": "Récupère ou défini le sujet du salon", + "This room has no topic.": "Ce salon n'a pas de sujet.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s a rendu le salon public à tous ceux qui en connaissent le lien.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s a rendu le salon disponible sur invitation seulement.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s a changé la règle d’adhésion en %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s a autorisé les invités à rejoindre le salon.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s a empêché les invités de rejoindre le salon.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a changé l'accès des invités en %(rule)s", + "Group & filter rooms by custom tags (refresh to apply changes)": "Grouper et filtrer les salons grâce à des étiquettes personnalisées (actualiser pour appliquer les changements)", + "Verify this user by confirming the following emoji appear on their screen.": "Vérifier cet utilisateur en confirmant que l’émoji suivant apparaît sur son écran.", + "Unable to find a supported verification method.": "Impossible de trouver une méthode de vérification prise en charge.", + "Dog": "Chien", + "Cat": "Chat", + "Lion": "Lion", + "Horse": "Cheval", + "Unicorn": "Licorne", + "Pig": "Cochon", + "Elephant": "Éléphant", + "Rabbit": "Lapin", + "Panda": "Panda", + "Rooster": "Coq", + "Penguin": "Manchot", + "Turtle": "Tortue", + "Fish": "Poisson", + "Octopus": "Pieuvre", + "Butterfly": "Papillon", + "Flower": "Fleur", + "Tree": "Arbre", + "Cactus": "Cactus", + "Mushroom": "Champignon", + "Globe": "Terre", + "Moon": "Lune", + "Cloud": "Nuage", + "Fire": "Feu", + "Banana": "Banane", + "Apple": "Pomme", + "Strawberry": "Fraise", + "Corn": "Maïs", + "Pizza": "Pizza", + "Cake": "Gâteau", + "Heart": "Cœur", + "Smiley": "Smiley", + "Robot": "Robot", + "Hat": "Chapeau", + "Glasses": "Lunettes", + "Spanner": "Clé", + "Santa": "Père Noël", + "Thumbs up": "Pouce levé", + "Umbrella": "Parapluie", + "Hourglass": "Sablier", + "Clock": "Horloge", + "Gift": "Cadeau", + "Light bulb": "Ampoule", + "Book": "Livre", + "Pencil": "Crayon", + "Paperclip": "Trombone", + "Scisors": "Ciseaux", + "Padlock": "Cadenas", + "Key": "Clé", + "Hammer": "Marteau", + "Telephone": "Téléphone", + "Flag": "Drapeau", + "Train": "Train", + "Bicycle": "Vélo", + "Aeroplane": "Avion", + "Rocket": "Fusée", + "Trophy": "Trophée", + "Ball": "Balle", + "Guitar": "Guitare", + "Trumpet": "Trompette", + "Bell": "Cloche", + "Anchor": "Ancre", + "Headphones": "Écouteurs", + "Folder": "Dossier", + "Pin": "Épingle", + "Your homeserver does not support device management.": "Votre serveur d'accueil ne prend pas en charge la gestion des appareils.", + "This device is not using key backup. Restore the backup to start using it.": "Cet appareil n’utilise pas la sauvegarde des clés. Récupérez votre sauvegarde pour commencer à l’utiliser.", + "This backup is trusted because it has been restored on this device": "Cette sauvegarde est fiable car elle a été récupérée sur cet appareil", + "Some devices for this user are not trusted": "Certains appareil pour cet utilisateur ne sont pas fiables", + "Some devices in this encrypted room are not trusted": "Certains appareils dans ce salon chiffré ne sont pas fiables", + "All devices for this user are trusted": "Tous les appareils de cet utilisateur sont fiables", + "All devices in this encrypted room are trusted": "Tous les appareils de ce salon chiffré sont fiables", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "La sauvegarde de clés sécurisée devrait être active sur tous vos appareils pour éviter de perdre l'accès à vos messages chiffrés.", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "Sauvegardez de façon sécurisée vos clés de déchiffrement vers le serveur pour être sûr(e) que vous pourrez toujours lire vos messages chiffrés.", + "Don't risk losing your encrypted messages!": "Ne prenez pas le risque de perdre vos messages chiffrés !", + "Activate Secure Key Backup": "Activer la sauvegarde de clés sécurisée", + "No thanks, I'll download a copy of my decryption keys before I log out": "Non merci, je téléchargerai une copie des clés de déchiffrement avant de me déconnecter", + "Room Settings": "Paramètres du salon", + "Recovery Key Mismatch": "La clé de récupération ne correspond pas", + "Incorrect Recovery Passphrase": "Phrase de passe de récupération incorrecte", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "La sauvegarde n'a pas pu être déchiffrée avec cette phrase de passe : vérifiez que vous avez saisi la bonne phrase de passe de récupération.", + "This homeserver would like to make sure you are not a robot.": "Ce serveur d'accueil veut s'assurer que vous n'êtes pas un robot.", + "Sign in to %(serverName)s": "Se connecter à %(serverName)s", + "Change": "Changer", + "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "Utilisez une adresse e-mail pour récupérer votre compte. Les autres utilisateurs peuvent vous inviter dans des salons en utilisant vos coordonnées.", + "Couldn't load page": "Impossible de charger la page", + "This homeserver does not support communities": "Ce serveur d'accueil ne prend pas en charge les communautés", + "Failed to get protocol list from homeserver": "Échec lors de la récupération de la liste de protocoles depuis le serveur", + "The homeserver may be too old to support third party networks": "Le serveur d'accueil est peut-être trop vieux pour prendre en charge les réseaux tiers", + "Your account on %(serverName)s": "Votre compte sur %(serverName)s", + "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "L'URL %(hsUrl)s du serveur d'accueil ne semble pas être une URL valide. Veuillez saisir une URL valide incluant le préfixe du protocole.", + "A verification email will be sent to your inbox to confirm setting your new password.": "Un e-mail de vérification sera envoyé à votre adresse pour confirmer la modification de votre mot de passe.", + "Your password has been reset.": "Votre mot de passe a été réinitialisé.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Vous avez été déconnecté de tous les appareils et vous ne recevrez plus de notifications. Pour réactiver les notifications, reconnectez-vous sur chaque appareil.", + "This homeserver does not support login using email address.": "Ce serveur d'accueil ne prend pas en charge la connexion avec une adresse e-mail.", + "Guest access is disabled on this homeserver.": "L'accès des invités est désactivé sur ce serveur d'accueil.", + "Registration has been disabled on this homeserver.": "L'inscription a été désactivée sur ce serveur d'accueil.", + "Unable to query for supported registration methods.": "Impossible de demander les méthodes d'inscription prises en charge.", + "Show recent room avatars above the room list (refresh to apply changes)": "Afficher les avatars des salons récents au-dessus de la liste de salons (actualiser pour appliquer les changements)", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "En êtes-vous sûr(e) ? Vous perdrez vos messages chiffrés si vos clés ne sont pas sauvegardées correctement.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Les messages chiffrés sont sécurisés avec un chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) ont les clés pour lire ces messages.", + "Restore from Backup": "Récupérer depuis la sauvegarde", + "This device is backing up your keys. ": "Cet appareil sauvegarde vos clés. ", + "This device is not backing up your keys.": "Cet appareil ne sauvegarde pas vos clés.", + "Back up your keys before signing out to avoid losing them.": "Sauvegardez vos clés avant de vous déconnecter pour éviter de les perdre.", + "Use key backup": "Utiliser la sauvegarde de clés", + "Your keys are not being backed up from this device.": "Vos clés ne sont pas sauvegardées depuis cet appareil.", + "Start using Key Backup": "Commencer à utiliser la sauvegarde de clés", + "Use Key Backup": "Utiliser la sauvegarde de clés", + "Never lose encrypted messages": "Ne perdez jamais vos messages chiffrés", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Les messages de ce salon sont sécurisés avec le chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) avez les clés pour lire ces messages.", + "Securely back up your keys to avoid losing them. Learn more.": "Sauvegardez vos clés de façon sécurisée pour éviter de les perdre. En savoir plus.", + "Not now": "Pas maintenant", + "Don't ask me again": "Ne plus me demander", + "Nothing appearing? Not all clients support interactive verification yet. .": "Rien n'apparaît ? Certains clients ne prennent pas encore en charge la vérification interactive. .", + "I don't want my encrypted messages": "Je ne veux pas de mes messages chiffrés", + "Manually export keys": "Exporter manuellement les clés", + "You'll lose access to your encrypted messages": "Vous perdrez l’accès à vos messages chiffrés", + "Are you sure you want to sign out?": "Voulez-vous vraiment vous déconnecter ?", + "Warning: you should only set up key backup from a trusted computer.": "Attention : vous ne devriez configurer la sauvegarde des clés que depuis un ordinateur de confiance.", + "Hide": "Masquer", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Nous conserverons une copie chiffrée de vos clés sur notre serveur. Protégez votre sauvegarde avec une phrase de passe pour qu'elle reste sécurisée.", + "For maximum security, this should be different from your account password.": "Pour une sécurité maximale, ceci devrait être différent du mot de passe de votre compte.", + "Set up with a Recovery Key": "Configurer une clé de récupération", + "Please enter your passphrase a second time to confirm.": "Veuillez saisir votre phrase de passe une seconde fois pour la confirmer.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Votre clé de récupération est une mesure de précaution. Vous pouvez l'utiliser pour récupérer l’accès à vos messages chiffrés si vous oubliez votre phrase de passe.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Conservez votre clé de récupération dans un endroit très sécurisé, comme un gestionnaire de mots de passe (ou un coffre-fort)", + "Your keys are being backed up (the first backup could take a few minutes).": "Vous clés sont en cours de sauvegarde (la première sauvegarde peut prendre quelques minutes).", + "Okay": "OK", + "Secure your backup with a passphrase": "Protégez votre sauvegarde avec une phrase de passe", + "Confirm your passphrase": "Confirmez votre phrase de passe", + "Recovery key": "Clé de récupération", + "Success!": "Terminé !" } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 10eca154a8..56e9aaa4a3 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1525,7 +1525,7 @@ "2018 theme": "2018-as téma", "Account management": "Fiók menedzsment", "Deactivating your account is a permanent action - be careful!": "A fiók felfüggesztése végleges - légy óvatos!", - "Close Account": "Fiók bezárása", + "Deactivate Account": "Fiók bezárása", "For help with using Riot, click here.": "A Riot használatában való segítséghez kattints ide.", "For help with using Riot, click here or start a chat with our bot using the button below.": "A Riot használatában való segítségér kattints ide vagy kezdj beszélgetni a botunkkal az alábbi gombra kattintva.", "Start a chat with Riot Bot": "Csevegés kezdése a Riot Bottal", @@ -1629,5 +1629,149 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Ez az eszköz észrevette, hogy a visszaállítási jelmondatot és kulcsot a Biztonságos Üzenetekhez törölték.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Ha véletlenül tetted, a Biztonságos Üzeneteket beállíthatod ezen az eszközön ami újra titkosítja az eszköz üzenet naplóját az új visszaállítási eljárással.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Ha nem te törölted a visszaállítási eljárást, akkor egy támadó hozzá akar férni a fiókodhoz. Azonnal változtasd meg a jelszavadat és állíts be egy visszaállítási eljárást a Beállításokban.", - "Chat with Riot Bot": "Csevegés a Riot Robottal" + "Chat with Riot Bot": "Csevegés a Riot Robottal", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "'%(fileName)s' mérete nagyobb mint amekkorát a Matrix szerver megenged feltölteni", + "Gets or sets the room topic": "Lekérdezi vagy beállítja a szoba témáját", + "This room has no topic.": "A szobának nincs témája.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s hozzáférhetővé tette a szobát bárkinek, aki ismeri a linket.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s beállította, hogy a szobába csak meghívóval lehessen belépni.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s a belépési szabályt erre állította be: %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s megengedte a vendég felhasználóknak, hogy beléphessenek a szobába.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s megtiltotta a vendég felhasználóknak, hogy belépjenek a szobába.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a vendég felhasználók belépési jogait erre állította át: %(rule)s", + "Group & filter rooms by custom tags (refresh to apply changes)": "Szobák csoportosítása és szűrése egyedi címkékkel (frissíts, hogy a változások érvényre jussanak)", + "Verify this user by confirming the following emoji appear on their screen.": "Hitelesítheted a felhasználót, ha megerősíted, hogy az alábbi emoji az ami megjelent a képernyőjén.", + "Unable to find a supported verification method.": "Nem található támogatott hitelesítési eljárás.", + "Dog": "Kutya", + "Cat": "Macska", + "Lion": "Oroszlán", + "Horse": "Ló", + "Unicorn": "Egyszarvú", + "Pig": "Disznó", + "Elephant": "Elefánt", + "Rabbit": "Nyúl", + "Panda": "Panda", + "Rooster": "Kakas", + "Penguin": "Pingvin", + "Turtle": "Teknős", + "Fish": "Hal", + "Octopus": "Polip", + "Butterfly": "Pillangó", + "Flower": "Virág", + "Tree": "Fa", + "Cactus": "Kaktusz", + "Mushroom": "Gomba", + "Globe": "Földgömb", + "Moon": "Hold", + "Cloud": "Felhő", + "Fire": "Tűz", + "Banana": "Banán", + "Apple": "Alma", + "Strawberry": "Eper", + "Corn": "Kukorica", + "Pizza": "Pizza", + "Cake": "Sütemény", + "Heart": "Szív", + "Smiley": "Smiley", + "Robot": "Robot", + "Hat": "Kalap", + "Glasses": "Szemüveg", + "Spanner": "Csavarhúzó", + "Santa": "Télapó", + "Thumbs up": "Hüvelykujj fel", + "Umbrella": "Esernyő", + "Hourglass": "Homokóra", + "Clock": "Óra", + "Gift": "Ajándék", + "Light bulb": "Égő", + "Book": "Könyv", + "Pencil": "Toll", + "Paperclip": "Gémkapocs", + "Scisors": "Olló", + "Padlock": "Lakat", + "Key": "Kulcs", + "Hammer": "Kalapács", + "Telephone": "Telefon", + "Flag": "Zászló", + "Train": "Vonat", + "Bicycle": "Kerékpár", + "Aeroplane": "Repülőgép", + "Rocket": "Rakéta", + "Trophy": "Kupa", + "Ball": "Labda", + "Guitar": "Gitár", + "Trumpet": "Trombita", + "Bell": "Harang", + "Anchor": "Horgony", + "Headphones": "Fejhallgató", + "Folder": "Dosszié", + "Pin": "Gombostű", + "Your homeserver does not support device management.": "A Matrix szervered nem támogatja a eszközkezelést.", + "This device is not using key backup. Restore the backup to start using it.": "Ez az eszköz nem használja a kulcsok biztonsági mentését. Állítsd vissza a mentést, hogy elkezdhesd használni.", + "This backup is trusted because it has been restored on this device": "Ez a mentés megbízható mert ezen az eszközön lett visszaállítva", + "Some devices for this user are not trusted": "A felhasználó néhány eszköze nem megbízható", + "Some devices in this encrypted room are not trusted": "Néhány eszköz nem megbízható ebben a titkosított szobában", + "All devices for this user are trusted": "A felhasználó minden eszköze megbízható", + "All devices in this encrypted room are trusted": "A szobában minden eszköz megbízható", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "A titkosított üzenetek elvesztésének elkerülése végett a Biztonsági Kulcs Mentést minden eszközödön aktiválni kell.", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "A titkosított üzenetekhez való mindenkori hozzáférés biztosításához a visszafejtő kulcsokat biztonságosan a szerverre kell menteni.", + "Don't risk losing your encrypted messages!": "Ne kockáztasd a titkosított üzeneteid elvesztését!", + "Activate Secure Key Backup": "Biztonsági Kulcs Mentés aktiválása", + "No thanks, I'll download a copy of my decryption keys before I log out": "Köszönöm, nem kérem. Kilépés előtt letöltöm a visszafejtési kulcsaim másolatát", + "Room Settings": "Szoba beállítások", + "Recovery Key Mismatch": "Visszaállítási Kulcs eltérés", + "Incorrect Recovery Passphrase": "Visszaállítási jelmondat hiba", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "A mentést nem lehet visszafejteni a jelmondattal: kérlek ellenőrizd, hogy helyesen adtad meg a visszaállítási jelmondatot.", + "This homeserver would like to make sure you are not a robot.": "A Matrix szerver meg kíván győződni arról, hogy nem vagy robot.", + "Sign in to %(serverName)s": "Belépés ide: %(serverName)s", + "Change": "Változtat", + "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "A fiókod visszaállításához használd az e-mail címed. Más felhasználók a névjegy adataidat felhasználva meghívhatnak egy szobába.", + "Couldn't load page": "Az oldal nem tölthető be", + "This homeserver does not support communities": "Ez a Matrix szerver nem támogatja a közösségeket", + "Failed to get protocol list from homeserver": "A Matrix szerverről nem kérhető le a protokoll lista", + "The homeserver may be too old to support third party networks": "A Matrix szerver lehet, hogy túl régi, hogy más hálózatokat támogasson", + "Your account on %(serverName)s": "A felhasználói fiókod itt: %(serverName)s", + "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "A Matrix szerver URL, %(hsUrl)s, nem tűnik érvényesnek. Kérlek érvényes URL-t adjál meg beleértve a protokoll előtagot is.", + "A verification email will be sent to your inbox to confirm setting your new password.": "Egy ellenőrző e-mail lesz elküldve a címedre, hogy megerősíthesd az új jelszó beállításodat.", + "Your password has been reset.": "A jelszavad újra beállításra került.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Minden eszközödről ki vagy jelentkeztetve és „push” értesítéseket sem fogsz kapni. Az értesítések újbóli engedélyezéséhez újra be kell jelentkezned minden eszközön.", + "This homeserver does not support login using email address.": "Ezen a Matrix szerveren nem tudsz e-mail címmel bejelentkezni.", + "Guest access is disabled on this homeserver.": "A vendég felhasználók le vannak tiltva ezen a Matrix szerveren.", + "Registration has been disabled on this homeserver.": "A fiókkészítés le van tiltva ezen a Matrix szerveren.", + "Unable to query for supported registration methods.": "A támogatott regisztrációs módokat nem lehet lekérdezni.", + "Show recent room avatars above the room list (refresh to apply changes)": "Mutasd a legutóbbi szoba profilképét a szobák listája felett (a változtatáshoz frissítsd az oldalt)", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Biztos vagy benne? Ha a kulcsaid nincsenek megfelelően elmentve elveszted a titkosított üzeneteidet.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "A titkosított üzenetek végponttól végpontig titkosítással védettek. Csak neked és a címzetteknek lehet meg a kulcs az üzenet visszafejtéséhez.", + "Restore from Backup": "Visszaállítás mentésből", + "This device is backing up your keys. ": "Ez az eszköz elmenti a kulcsaidat. ", + "This device is not backing up your keys.": "Ez az eszköz nem menti el a kulcsaidat.", + "Back up your keys before signing out to avoid losing them.": "Ments el a kulcsaidat mielőtt kijelentkezel, hogy ne veszítsd el őket.", + "Use key backup": "Kulcs mentés használata", + "Your keys are not being backed up from this device.": "A kulcsaid erről az eszközről nem lesznek mentve.", + "Start using Key Backup": "Kulcs mentés használatának megkezdése", + "Use Key Backup": "Kulcs mentés használata", + "Never lose encrypted messages": "Soha ne veszíts el titkosított üzenetet", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "A szobában az üzenetek végponttól végpontig titkosítva vannak. Csak neked és a címzetteknek vannak meg a kulcsok az üzenetek visszafejtéséhez.", + "Securely back up your keys to avoid losing them. Learn more.": "Mentsd el megfelelően a kulcsaidat, hogy ne vesszenek el. Tudj meg róla többet.", + "Not now": "Most nem", + "Don't ask me again": "Ne kérdezz többet", + "Nothing appearing? Not all clients support interactive verification yet. .": "Nem jelent meg semmi? Az interaktív hitelesítést még nem minden kliens támogatja. .", + "I don't want my encrypted messages": "Nincs szükségem a titkosított üzeneteimre", + "Manually export keys": "Kulcsok kézi mentése", + "You'll lose access to your encrypted messages": "Elveszted a hozzáférést a titkosított üzeneteidhez", + "Are you sure you want to sign out?": "Biztos, hogy ki akarsz jelentkezni?", + "Warning: you should only set up key backup from a trusted computer.": "Figyelmeztetés: csak biztonságos számítógépről állíts be kulcs mentést.", + "Hide": "Eltakar", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "A kulcsaid másolatát titkosítva tároljuk a Matrix szerveren. Védd a mentésedet jelmondattal, hogy biztonságban legyen.", + "For maximum security, this should be different from your account password.": "A maximális biztonság érdekében ez térjen el a felhasználói fióknál használt jelszótól.", + "Set up with a Recovery Key": "Beállítás Visszaállítási Kulccsal", + "Please enter your passphrase a second time to confirm.": "Kérlek add meg a jelmondatot másodszor is a biztonság kedvéért.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "A Visszaállítási Kulcs egy olyan biztonsági elem amivel visszaállíthatod a hozzáférésed a titkosított üzenetekhez még akkor is, ha a jelmondatot elfelejtetted.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "A Visszaállítási Kulcsot nagyon biztonságos helyen tárold, mint pl. egy jelszókezelőben (vagy széfben)", + "Your keys are being backed up (the first backup could take a few minutes).": "A kulcsaid mentése folyamatban van (az első mentés több percig is eltarthat).", + "Okay": "Rendben", + "Secure your backup with a passphrase": "Védd a mentést egy jelmondattal", + "Confirm your passphrase": "Erősítsd meg a jelmondatot", + "Recovery key": "Visszaállítási Kulcs", + "Success!": "Sikeres!" } diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index b1ed91e9ab..2f04e39104 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -300,10 +300,10 @@ "Add rooms to the community": "コミュニティに部屋を追加します", "Room name or alias": "部屋名またはエイリアス", "Add to community": "コミュニティに追加", - "Failed to invite the following users to %(groupId)s:": "次のユーザーを %(groupId)s に招待できませんでした:", + "Failed to invite the following users to %(groupId)s:": "次のユーザーを %(groupId)s に招待できませんでした:", "Failed to invite users to community": "ユーザーをコミュニティに招待できませんでした", "Failed to invite users to %(groupId)s": "ユーザーを %(groupId)s に招待できませんでした", - "Failed to add the following rooms to %(groupId)s:": "次の部屋を %(groupId)s に追加できませんでした:", + "Failed to add the following rooms to %(groupId)s:": "次の部屋を %(groupId)s に追加できませんでした:", "Riot does not have permission to send you notifications - please check your browser settings": "Riotに通知を送信する権限がありません - ブラウザの設定を確認してください", "Riot was not given permission to send notifications - please try again": "Riotに通知を送信する権限がありませんでした。もう一度お試しください", "Unable to enable Notifications": "通知を有効にできません", @@ -323,7 +323,7 @@ "Send Invites": "招待状を送る", "Failed to invite user": "ユーザーを招待できませんでした", "Failed to invite": "招待できませんでした", - "Failed to invite the following users to the %(roomName)s room:": "次のユーザーを %(roomName)s の部屋に招待できませんでした:", + "Failed to invite the following users to the %(roomName)s room:": "次のユーザーを %(roomName)s の部屋に招待できませんでした:", "You need to be logged in.": "ログインする必要があります。", "You need to be able to invite users to do that.": "それをするためにユーザーを招待できる必要があります。", "Unable to create widget.": "ウィジェットを作成できません。", @@ -363,9 +363,9 @@ "Unknown (user, device) pair:": "不明な(ユーザー、端末) ペア:", "Device already verified!": "端末はすでに確認済みです!", "WARNING: Device already verified, but keys do NOT MATCH!": "警告:端末はすでに検証済みですが、キーは一致しません!", - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "警告: キー確認が失敗しました! %(userId)s と端末 %(deviceId)s の署名鍵は、提供された鍵 \"%(fingerprint)s\" と一致しない \"%(fprint)s\" です。 通信が傍受されている可能性があります!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "警告: キー確認が失敗しました! %(userId)s と端末 %(deviceId)s の署名鍵は、提供された鍵 \"%(fingerprint)s\" と一致しない \"%(fprint)s\" です。 通信が傍受されている可能性があります!", "Verified key": "確認済みのキー", - "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "指定した署名鍵は、%(userId)s の端末%(deviceId)s から受け取った署名鍵と一致します。端末を検証済みとしてマークしました。", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "指定した署名鍵は、%(userId)s の端末%(deviceId)s から受け取った署名鍵と一致します。端末を検証済みとしてマークしました。", "Displays action": "アクションを表示", "Forces the current outbound group session in an encrypted room to be discarded": "暗号化されたルーム内の現在のアウトバウンドグループセッションを強制的に破棄します", "Unrecognised command:": "認識できないコマンド:", @@ -609,7 +609,7 @@ "Replying": "返信中", "Failed to set avatar.": "アバターを設定できませんでした。", "Save": "保存", - "(~%(count)s results)|other": "(~%(count)s 結果)", + "(~%(count)s results)|other": "(~%(count)s 結果)", "(~%(count)s results)|one": "(~%(count)s 結果)", "Join Room": "部屋に入る", "Remove avatar": "アバターを削除", @@ -641,7 +641,7 @@ "Rejoin": "再参加", "You have been kicked from %(roomName)s by %(userName)s.": "あなたは %(userName)s により %(roomName)s から追放されました。", "You have been kicked from this room by %(userName)s.": "あなたは %(userName)s によりこの部屋から追放されました。", - "You have been banned from %(roomName)s by %(userName)s.": "あなたは %(userName)s により %(roomName)s からブロックされました。", + "You have been banned from %(roomName)s by %(userName)s.": "あなたは %(userName)s により %(roomName)s からブロックされました。", "You have been banned from this room by %(userName)s.": "あなたは %(userName)s によってこの部屋からブロックされました。", "This room": "この部屋", "%(roomName)s does not exist.": "%(roomName)s は存在しません。", diff --git a/src/i18n/strings/nb_NO.json b/src/i18n/strings/nb_NO.json index 697c9a9e63..64332ded3b 100644 --- a/src/i18n/strings/nb_NO.json +++ b/src/i18n/strings/nb_NO.json @@ -1,13 +1,13 @@ { "Room directory": "Romkatalog", - "This email address is already in use": "Denne e-post adressen er allerede i bruk", + "This email address is already in use": "Denne e-postadressen er allerede i bruk", "This phone number is already in use": "Dette mobilnummeret er allerede i bruk", - "Failed to verify email address: make sure you clicked the link in the email": "Klarte ikke verifisere e-post adressen: dobbelsjekk at du trykket på lenken i e-posten", + "Failed to verify email address: make sure you clicked the link in the email": "Klarte ikke verifisere e-postadressen: dobbelsjekk at du trykket på lenken i e-posten", "The platform you're on": "Platformen du bruker", "The version of Riot.im": "Versjonen av Riot.im", "Whether or not you're logged in (we don't record your user name)": "Om du er logget inn eller ei (vi loggfører ikke brukernavnet ditt)", "Your language of choice": "Ditt valgte språk", - "Your homeserver's URL": "Din hjemservers URL", + "Your homeserver's URL": "Din hjemmetjeners URL", "Fetching third party location failed": "Kunne ikke hente tredjeparts lokalisering", "Advanced notification settings": "Avanserte varslingsinnstillinger", "Sunday": "Søndag", @@ -115,5 +115,128 @@ "There are advanced notifications which are not shown here": "Det er avanserte varsler som ikke vises her", "Dismiss": "Avvis", "Whether or not you're logged in (we don't record your username)": "Om du er logget inn eller ikke (vi lagrer ikke brukernavnet ditt)", - "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Du kan også stille inn en egen idendtitetstjener, men du kommer ikke til å kunne invitere andre brukere med e-post, eller bli invitert med e-post selv." + "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Du kan også stille inn en egen idendtitetstjener, men du kommer ikke til å kunne invitere andre brukere med e-post, eller bli invitert med e-post selv.", + "Which officially provided instance you are using, if any": "Hvilken offisielle leverte instans som du bruker, hvis noen", + "Whether or not you're using the Richtext mode of the Rich Text Editor": "Om du bruker rik-tekstmodus i rik-tekstfeltet", + "Your identity server's URL": "Din identitetstjeners URL", + "e.g. %(exampleValue)s": "f.eks. %(exampleValue)s", + "Every page you use in the app": "Alle sider du bruker i appen", + "e.g. ": "f.eks. ", + "Your User Agent": "Din brukeragent", + "Your device resolution": "Din enhets skjermoppløsing", + "Analytics": "Statistikk", + "The information being sent to us to help make Riot.im better includes:": "Informasjonen som blir sendt til oss for å hjelpe oss med å lage Riot.im bedre inkluderer:", + "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Hvor denne siden inkluderer identifiserende informasjon, sånn som navnet på rommet, brukeren, og gruppe ID, men denne informasjonen blir fjernet før den blir sendt til tjeneren.", + "Call Failed": "Oppringning mislyktes", + "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Det er ukjente enheter i dette rommet: Hvis du fortsetter uten å verifisere dem vil det være mulig for noen å tyvlytte på samtalen din.", + "Review Devices": "Se over enheter", + "Call Anyway": "Ring likevel", + "Answer Anyway": "Svar likevel", + "Call": "Ring", + "Answer": "Svar", + "Call Timeout": "Oppringningen be tidsavbrutt", + "The remote side failed to pick up": "Den andre svarte ikke", + "Unable to capture screen": "Klarte ikke ta opp skjermen", + "Existing Call": "Samtalen er allerede igang", + "You are already in a call.": "Du er allerede i en samtale.", + "VoIP is unsupported": "VoIP er ikke støttet", + "You cannot place VoIP calls in this browser.": "Du kan ikke ringe via VoIP i denne nettleseren.", + "You cannot place a call with yourself.": "Du kan ikke ringe deg selv.", + "Could not connect to the integration server": "Kunne ikke koples til integrasjonstjeneren", + "A conference call could not be started because the intgrations server is not available": "En konferansesamtale kunne ikke startes fordi integrasjonstjeneren ikke er tilgjengelig", + "Call in Progress": "Samtale pågår", + "A call is currently being placed!": "En samtale holder allerede på å starte", + "A call is already in progress!": "En samtale er allerede igang!", + "Permission Required": "Tillatelse kreves", + "You do not have permission to start a conference call in this room": "Du har ikke tillatelse til å starte en konferansesamtale i dette rommet", + "The file '%(fileName)s' failed to upload": "Filen \"%(fileName)s\" klarte ikke å blir lastet opp", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Filen \"%(fileName)s\" er større enn denne hjemmetjenerens grense for opplastninger", + "Upload Failed": "Opplasting feilet", + "Failure to create room": "Klarte ikke rommet", + "Server may be unavailable, overloaded, or you hit a bug.": "Tjeneren kan være utilgjengelig, overbelastet, eller du fant en feil.", + "Send anyway": "Send likevel", + "Send": "Send", + "Sun": "Søn", + "Mon": "Man", + "Tue": "Tir", + "Wed": "Ons", + "Thu": "Tor", + "Fri": "Fre", + "Sat": "Lør", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "Mai", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Okt", + "Nov": "Nov", + "Dec": "Des", + "PM": "PM", + "AM": "AM", + "%(weekDayName)s %(time)s": "%(weekDayName)s. %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s,%(monthName)s,%(day)s,%(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", + "Who would you like to add to this community?": "Hvem har du lyst til å legge til i dette samfunnet?", + "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Advarsel: Enhver person som du legger til i et samfunn vil bli offentlig synlig til alle som kan samfunns IDen", + "Invite new community members": "Inviter nye samfunnsmedlemmer", + "Name or matrix ID": "Navn eller Matrix ID", + "Invite to Community": "Inviter til samfunn", + "Which rooms would you like to add to this community?": "Hvilke rom vil du legge til i dette samfunnet?", + "Show these rooms to non-members on the community page and room list?": "Hvis disse rommene til ikke-medlemmer på samfunn-siden og -romlisten?", + "Add rooms to the community": "Legg rom til i samfunnet", + "Room name or alias": "Romnavn eller alias", + "Add to community": "Legg til i samfunn", + "Failed to invite the following users to %(groupId)s:": "Klarte ikke invitere disse brukerene til %(groupId)s:", + "Failed to invite users to community": "Klarte ikke invitere brukere til samfunnet", + "Failed to invite users to %(groupId)s": "Klarte ikke invitere brukere til %(groupId)s", + "Failed to add the following rooms to %(groupId)s:": "Klarte ikke å legge til de følgende rommene til %(groupId)s:", + "Unnamed Room": "Navnløst rom", + "Unable to load! Check your network connectivity and try again.": "Klarte ikke laste! Sjekk nettverstilkoplingen din og prøv igjen.", + "Riot does not have permission to send you notifications - please check your browser settings": "Riot har ikke tillatelse til å sende deg notifikasjoner - vennligst sjekk nettleserinnstillingene", + "Riot was not given permission to send notifications - please try again": "Riot fikk ikke tillatelse til å sende deg notifikasjoner - vennligst prøv igjen", + "Unable to enable Notifications": "Klarte ikke skru på notifikasjoner", + "This email address was not found": "Denne e-postadressen ble ikke funnet", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "E-postadressen din ser ikke ut til å være koplet til en matrix-ID på denne hjemmetjeneren.", + "Registration Required": "Registrering påkrevet", + "You need to register to do this. Would you like to register now?": "Du må registrere deg for å gjøre dette. Vil du registrere deg nå?", + "Register": "Registrer", + "Default": "Normalverdi", + "Restricted": "Begrenset", + "Moderator": "Moderator", + "Admin": "Administrator", + "Start a chat": "Start en samtale", + "Who would you like to communicate with?": "Hvem vil du prate med?", + "Email, name or matrix ID": "E-post, navn, eller matrix-ID", + "Start Chat": "Start Samtale", + "Invite new room members": "Inviter nye rommedlemmer", + "Who would you like to add to this room?": "Hvem vil du legge til i dette rommet?", + "Send Invites": "Send invitasjoner", + "Failed to invite user": "Klarte ikke invitere bruker", + "Operation failed": "Operasjon mislyktes", + "Failed to invite": "Klarte ikke invitere", + "Failed to invite users to the room:": "Klarte ikke invitere brukere til rommet:", + "Failed to invite the following users to the %(roomName)s room:": "Klarte ikke invitere de følgende brukerne til %(roomName)s rommet:", + "You need to be logged in.": "Du må være logget inn.", + "You need to be able to invite users to do that.": "Du må kunne invitere andre brukere for å gjøre det.", + "Unable to create widget.": "Klarte ikke lage widgeten.", + "Missing roomId.": "Manglende rom-ID.", + "Failed to send request.": "Klarte ikke sende forespørsel.", + "This room is not recognised.": "Dette rommet blir ikke gjenkjent.", + "Power level must be positive integer.": "Maktnivået må være et positivt heltall.", + "You are not in this room.": "Du er ikke i dette rommet.", + "You do not have permission to do that in this room.": "Du har ikke tillatelse til å gjøre det i dette rommet.", + "Missing room_id in request": "Manglende room_id i forespørselen", + "Room %(roomId)s not visible": "Rom %(roomId)s er ikke synlig", + "Missing user_id in request": "Manglende user_id i forespørselen", + "Usage": "Bruk", + "Searches DuckDuckGo for results": "Søker DuckDuckGo for resultater", + "/ddg is not a command": "/ddg er ikke en kommando", + "To use it, just wait for autocomplete results to load and tab through them.": "For å bruke det, bare vent til autofullfør-resultatetene til å laste, og tab deg gjennom dem.", + "Upgrades a room to a new version": "Oppgraderer et rom til en ny versjon", + "Changes your display nickname": "Endrer visningsnavnet ditt" } diff --git a/src/i18n/strings/nn.json b/src/i18n/strings/nn.json index fa4d5f60c6..9b00c50f76 100644 --- a/src/i18n/strings/nn.json +++ b/src/i18n/strings/nn.json @@ -128,7 +128,7 @@ "e.g. ": "t.d. ", "Your User Agent": "Din Brukaragent", "Analytics": "Statistikk", - "Unable to capture screen": "Kunne ikkje visa skjerm", + "Unable to capture screen": "Klarte ikkje ta opp skjermen", "Existing Call": "Samtale er i gang", "To use it, just wait for autocomplete results to load and tab through them.": "For å bruka han, vent på at resultata fyller seg ut og tab gjennom dei.", "Deops user with given id": "AvOPar brukarar med den gjevne IDen", @@ -1225,5 +1225,9 @@ "Only room administrators will see this warning": "Berre romadministratorar vil sjå denne åtvaringa", "Please contact your service administrator to continue using the service.": "Ver venleg og tak kontakt med tenesteadministratoren for å halda fram med å bruka tenesten.", "This homeserver has hit its Monthly Active User limit.": "Heimtenaren har truffe den Månadlege Grensa si for Aktive Brukarar.", - "This homeserver has exceeded one of its resource limits.": "Heimtenaren har gått over ei av ressursgrensene sine." + "This homeserver has exceeded one of its resource limits.": "Heimtenaren har gått over ei av ressursgrensene sine.", + "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Du kan òg velja ein eigendefinert identitetstenar, men då kjem du ikkje til å innvitere brukarar gjennom e-post, eller verta invitert med e-post sjølv.", + "Whether or not you're logged in (we don't record your username)": "Om du er innlogga eller ikkje (vi lagrar ikkje brukarnamnet ditt)", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Fila %(fileName)s er større enn heimetenaren si grense for opplastningar", + "Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen." } diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index d4f2afb2a3..8f55867499 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -1330,5 +1330,7 @@ "Unrecognised address": "Nierozpoznany adres", "Short keyboard patterns are easy to guess": "Krótkie wzory klawiszowe są łatwe do odgadnięcia", "Enable Emoji suggestions while typing": "Włącz podpowiedzi Emoji podczas pisania", - "Show avatar changes": "Pokaż zmiany awatara" + "Show avatar changes": "Pokaż zmiany awatara", + "This room has no topic.": "Ten pokój nie ma tematu.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s zaktualizował(a) ten pokój." } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 3a586d9bb7..9f766b9bf1 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -1347,7 +1347,7 @@ "Account management": "Управление аккаунтом", "Deactivating your account is a permanent action - be careful!": "Деактивация вашей учетной записи — это необратимое действие. Будьте осторожны!", "Chat with Riot Bot": "Чат с ботом Riot", - "Help & About": "Справка и о программе", + "Help & About": "Справка. О программе", "FAQ": "Часто задаваемые вопросы", "Versions": "Версии", "Lazy loading is not supported by your current homeserver.": "Ленивая подгрузка не поддерживается вашим сервером.", @@ -1393,7 +1393,7 @@ "Developer options": "Параметры разработчика", "General": "Основной", "Set a new account password...": "Установить новый пароль учетной записи...", - "Close Account": "Закрыть аккаунт", + "Deactivate Account": "Закрыть аккаунт", "Legal": "Правовой", "At this time it is not possible to reply with an emote.": "В настоящее время невозможно ответить с помощью эмоции.", "Room avatar": "Аватар комнаты", @@ -1463,5 +1463,72 @@ "Create Key Backup": "Создать ключ резервного копирования", "Set up Secure Message Recovery": "Настройка безопасного восстановления сообщений", "Copy it to your personal cloud storage": "Скопируйте в персональное облачное хранилище", - "Save it on a USB key or backup drive": "Сохраните на USB-диске или на резервном диске" + "Save it on a USB key or backup drive": "Сохраните на USB-диске или на резервном диске", + "This room has no topic.": "У этой комнаты нет темы.", + "Group & filter rooms by custom tags (refresh to apply changes)": "Группировать и фильтровать комнаты по тэгам (обновите, чтобы применить изменения)", + "Dog": "Собака", + "Cat": "Кошка", + "Lion": "Лев", + "Horse": "Конь", + "Unicorn": "Единорог", + "Pig": "Поросёнок", + "Elephant": "Слон", + "Rabbit": "Кролик", + "Panda": "Панда", + "Rooster": "Петух", + "Penguin": "Пингвин", + "Fish": "Рыба", + "Turtle": "Черепаха", + "Octopus": "Осьминог", + "Butterfly": "Бабочка", + "Flower": "Цветок", + "Tree": "Дерево", + "Cactus": "Кактус", + "Mushroom": "Гриб", + "Globe": "Земля", + "Moon": "Луна", + "Cloud": "Облако", + "Fire": "Огонь", + "Banana": "Банан", + "Apple": "Яблоко", + "Strawberry": "Клубника", + "Corn": "Кукуруза", + "Pizza": "Пицца", + "Cake": "Кекс", + "Heart": "Сердце", + "Smiley": "Смайл", + "Robot": "Робот", + "Hat": "Шляпа", + "Glasses": "Очки", + "Spanner": "Гаечный ключ", + "Santa": "Санта", + "Thumbs up": "Пальцы вверх", + "Umbrella": "Зонтик", + "Hourglass": "Песочные часы", + "Clock": "Часы", + "Gift": "Подарок", + "Light bulb": "Лампочка", + "Book": "Книга", + "Pencil": "Карандаш", + "Paperclip": "Скрепка для бумаг", + "Scisors": "Ножницы", + "Padlock": "Замок", + "Key": "Ключ", + "Hammer": "Молоток", + "Telephone": "Телефон", + "Flag": "Флаг", + "Train": "Поезда", + "Bicycle": "Велосипед", + "Aeroplane": "Самолёт", + "Rocket": "Ракета", + "Trophy": "Трофей", + "Ball": "Мяч", + "Guitar": "Гитара", + "Trumpet": "Труба", + "Bell": "Колокол", + "Anchor": "Якорь", + "Headphones": "Наушники", + "Folder": "Папка", + "Pin": "Кнопка", + "Your homeserver does not support device management.": "Ваш сервер не поддерживает управление устройством." } diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index f3f80e8dd5..2bf2f13420 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -300,7 +300,7 @@ "AM": "AM", "Room name or alias": "Emër dhome ose alias", "Unknown (user, device) pair:": "Çift (përdorues, pajisje) i panjohur:", - "Device already verified!": "Pajisjeje tashmë e verifikuar!", + "Device already verified!": "Pajisje tashmë e verifikuar!", "Verified key": "Kyç i verifikuar", "Unrecognised command:": "Urdhër jo i pranuar:", "Reason": "Arsye", @@ -1490,7 +1490,7 @@ "2018 theme": "Temë e 2018-s", "Account management": "Administrim llogarish", "Deactivating your account is a permanent action - be careful!": "Çaktivizimi i llogarisë tuaj është një veprim i pakthyeshëm - hapni sytë!", - "Close Account": "Mbylleni Llogarinë", + "Deactivate Account": "Mbylleni Llogarinë", "For help with using Riot, click here.": "Për ndihmë rreth përdorimit të Riot-it, klikoni këtu.", "For help with using Riot, click here or start a chat with our bot using the button below.": "Për ndihmë rreth përdorimit të Riot-it, klikoni këtu, ose nisni një fjalosje me robotin tonë duke përdorur butonin më poshtë.", "Help & About": "Ndihmë & Rreth", @@ -1595,5 +1595,150 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Kjo pajisje ka pikasur se frazëkalimi dhe kyçi juaj i rikthimeve për Mesazhe të Sigurt janë hequr.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Nëse këtë e keni bërë pa dashje, mund të ujdisni Mesazhe të Sigurt në këtë pajisje, gjë që do të sjellë rifshehtëzimin e historikut të mesazheve të pajisjes me një metodë të re rikthimesh.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Nëse metodën e re të rikthimeve s’e keni hequr ju, dikush mund të jetë duke u rrekur të hyjë në llogarinë tuaj. Ndryshoni menjëherë fjalëkalimin e llogarisë tuaj, te Rregullimet, dhe caktoni një metodë të re rikthimesh.", - "Disinvite this user?": "nga {user}" + "Disinvite this user?": "nga {user}", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Kartela '%(fileName)s' tejkalon kufirin e këtij shërbyesi Home për madhësinë e ngarkimeve", + "Gets or sets the room topic": "Merr ose cakton temën e dhomës", + "This room has no topic.": "Kjo dhomë s’ka temë.", + "Verify this user by confirming the following emoji appear on their screen.": "Verifikojeni këtë përdorues duke ripohuar shfaqjen e emoji-t vijues në skenën e tyre.", + "Dog": "Qen", + "Cat": "Mace", + "Lion": "Luan", + "Horse": "Kalë", + "Unicorn": "Njëbrirësh", + "Pig": "Derr", + "Elephant": "Elefant", + "Rabbit": "Lepur", + "Panda": "Panda", + "Rooster": "Këndes", + "Penguin": "Pinguin", + "Turtle": "Breshkë", + "Fish": "Peshk", + "Octopus": "Oktapod", + "Butterfly": "Flutur", + "Flower": "Lule", + "Tree": "Pemë", + "Cactus": "kaktus", + "Mushroom": "Kërpudhë", + "Globe": "Rruzull", + "Moon": "Hëna", + "Cloud": "Re", + "Fire": "Zjarr", + "Banana": "Banane", + "Apple": "Mollë", + "Strawberry": "Luleshtrydhe", + "Corn": "Misër", + "Pizza": "Picë", + "Cake": "Tortë", + "Heart": "Zemër", + "Smiley": "Emotikon", + "Robot": "Robot", + "Hat": "Kapë", + "Glasses": "Syze", + "Spanner": "Ikona në krah të shërbimeve tuaja të lidhura duket si një çelës. Klikimi mbi të ju lejon të formësoni lidhjet tuaja me rrjete dhe shërbime ", + "Umbrella": "Ombrellë", + "Clock": "Sahat", + "Gift": "Dhuratë", + "Light bulb": "Llambë", + "Book": "Libër", + "Pencil": "Laps", + "Paperclip": "Kapëse", + "Scisors": "Gërshërë", + "Padlock": "Dry", + "Hammer": "Çekiç", + "Telephone": "Telefon", + "Flag": "Flamur", + "Train": "Tren", + "Bicycle": "Biçikletë", + "Aeroplane": "Avion", + "Rocket": "Raketë", + "Trophy": "Trofe", + "Ball": "Top", + "Guitar": "Kitarë", + "Trumpet": "Trombë", + "Bell": "Kambanë", + "Anchor": "Spirancë", + "Headphones": "Kufje", + "Folder": "Dosje", + "Your homeserver does not support device management.": "Shërbyesi juaj Home nuk mbulon administrim pajisjesh.", + "Chat with Riot Bot": "Fjalosuni me Robotin Riot", + "Some devices for this user are not trusted": "Disa pajisje për këtë përdorues nuk janë të besuara", + "All devices for this user are trusted": "Krejt pajisjet për këtë përdorues janë të besuara", + "Activate Secure Key Backup": "Aktivizoni Kopjeruajtje të Sigurt Kyçesh", + "Room Settings": "Rregullime Dhome", + "Recovery Key Mismatch": "Mospërputhje Kyçesh", + "Incorrect Recovery Passphrase": "Frazëkalim Rimarrjeje i Pasaktë", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "S’u shfshehtëzua dot kopjeruajtja me këtë frazëkalim: ju lutemi, verifikoni që dhatë frazëkalimin e duhur të rikthimeve.", + "This homeserver would like to make sure you are not a robot.": "Ky Shërbyes Home do të donte të sigurohej se s’jeni robot.", + "Sign in to %(serverName)s": "Bëni hyrjen te %(serverName)s", + "Change": "Ndërroje", + "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "Përdorni një adresë email që të rimerrni llogarinë tuaj. Përdorues të tjerë mund t’ju ftojnë te dhoma duke përdorur hollësitë tuaja për kontakt.", + "Couldn't load page": "S’u ngarkua dot faqja", + "This homeserver does not support communities": "Ky shërbyes Home s’mbulon bashkësi", + "Failed to get protocol list from homeserver": "S’u arrit të merrej listë protokollesh nga Shërbyesi Home", + "The homeserver may be too old to support third party networks": "Shërbyesi Home mund të jetë shumë i vjetër për të mbuluar rrjete nga palë të treta", + "Your account on %(serverName)s": "Llogaria juaj në %(serverName)s", + "Your password has been reset.": "Fjalëkalimi juaj u ricaktua.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jeni nxjerrë jashtë prej krejt pajisjeve dhe s’do të merrni më njoftime push. Që të riaktivizoni njoftimet, bëni sërish hyrjen në çdo pajisje.", + "This homeserver does not support login using email address.": "Ky shërbyes Home nuk mbulon hyrje përmes adresash email.", + "Guest access is disabled on this homeserver.": "Në këtë shërbyes Home është çaktivizuar hyrja si vizitor.", + "Registration has been disabled on this homeserver.": "Në këtë shërbyes Home regjistrimi është çaktivizuar.", + "Unable to query for supported registration methods.": "S’arrihet të kërkohet për metoda regjistrimi që mbulohen.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s e bëri dhomën publike për këdo që di lidhjen.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s e bëri dhomën vetëm me ftesa.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s ndryshoi rregullin e pjesëmarrjes në %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s ka lejuar vizitorë të marrin pjesë në dhomë.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s ka penguar vizitorë të marrin pjesë në dhomë.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s ndryshoi hyrjen për vizitorë në %(rule)s", + "Group & filter rooms by custom tags (refresh to apply changes)": "Gruponi & filtroni dhoma sipas etiketash vetjake (që të hyjnë në fuqi ndryshimet, rifreskojeni)", + "Unable to find a supported verification method.": "S’arrihet të gjendet metodë verifikimi e mbuluar.", + "Santa": "Babagjyshi i Vitit të Ri", + "Hourglass": "Klepsidër", + "Key": "Kyç", + "This device is not using key backup. Restore the backup to start using it.": "Kjo pajisje nuk përdor kopjeruajtje kyçesh. Riktheni kopjeruajtjen që të fillohet të përdoret.", + "This backup is trusted because it has been restored on this device": "Kjo kopjeruajtje është e besuar, ngaqë është rikthyer në këtë pajisje", + "Some devices in this encrypted room are not trusted": "Disa pajisje në këtë dhomë të fshehtëzuar s’janë të besuara", + "All devices in this encrypted room are trusted": "Krejt pajisjet në këtë dhomë të fshehtëzuar janë të besuara", + "Disinvite": "Hiqi ftesën", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "Për të shmangur humbje të hyrjes në mesazhet tuaj të fshehtëzuar, Kopjeruajtja e Sigurt e Kyçeve duhet të jetë aktive në krejt pajisjet tuaja.", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "Bëni një kopjeruajtje të sigurt të kyçeve tuaj të shfshehtëzimit te shërbyesi, për të garantuar se do të jeni përherë në gjendje të lexoni mesazhet tuaj të fshehtëzuar.", + "Don't risk losing your encrypted messages!": "Mos rrezikoni humbjen e mesazheve tuaj të fshehtëzuar!", + "No thanks, I'll download a copy of my decryption keys before I log out": "Jo, faleminderit, do të shkarkoj një kopje të kyçeve të mi të shfshehtëzimeve, përpara se të bëj daljen", + "Disinvite this user from community?": "T’i hiqet ftesa për në bashkësi këtij përdoruesi?", + "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "URL-ja e shërbyesit Home %(hsUrl)s s’duket të jetë URL e vlefshme. Ju lutemi, jepni një URL të vlefshme, përfshi prefiksin e protokollit.", + "A verification email will be sent to your inbox to confirm setting your new password.": "Te mesazhet tuaj do të dërgohet një email verifikimi, për të ripohuar caktimin e fjalëkalimit tuaj të ri.", + "Show recent room avatars above the room list (refresh to apply changes)": "Shfaq avatarë dhome së fundi mbi listën e dhomave (që të zbatohen ndryshimet, rifreskojeni)", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Jeni i sigurt? Do të humbni mesazhet tuaj të fshehtëzuar, nëse kopjeruajtja për kyçet tuaj nuk bëhet si duhet.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Mesazhet e fshehtëzuar sigurohen me fshehtëzim skaj-më-skaj. Vetëm ju dhe marrësi(t) kanë kyçet për të lexuar këto mesazhe.", + "Restore from Backup": "Riktheje prej Kopjeruajtje", + "This device is backing up your keys. ": "Kjo pajisje po bën kopjeruajtje të kyçeve tuaja. ", + "This device is not backing up your keys.": "Kjo pajisje nuk kopjeruan kyçe tuajt.", + "Back up your keys before signing out to avoid losing them.": "Kopjeruajini kyçet tuaj, përpara se të dilni, që të shmangni humbjen e tyre.", + "Use key backup": "Përdor kopjeruajtje kyçesh", + "Your keys are not being backed up from this device.": "Kyçet tuaj nuk po kopjeruhen nga kjo pajisje.", + "Start using Key Backup": "Fillo të përdorësh Kopjeruajtje Kyçesh", + "Use Key Backup": "Përdor Kopjeruajtje Kyçesh", + "Never lose encrypted messages": "Mos humbni kurrë mesazhe të fshehtëzuar", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Mesazhet në këtë dhomë janë të siguruar përmes fshehtëzimi skaj-më-skaj. Vetëm ju dhe marrësi(t) kanë kyçet për të lexuar këto mesazhe.", + "Securely back up your keys to avoid losing them. Learn more.": "Kopjeruajini kyçet tuaj në mënyrë të sigurt, për të shmangur humbjen e tyre. Mësoni më tepër.", + "Not now": "Jo tani", + "Don't ask me again": "Mos më pyet sërish", + "Nothing appearing? Not all clients support interactive verification yet. .": "S’duket gjë? Jo të tërë klientët mbulojnë verifikim ndërveprues ende. .", + "I don't want my encrypted messages": "Nuk i dua mesazhet e mia të fshehtëzuar", + "Manually export keys": "Eksporto dorazi kyçet", + "You'll lose access to your encrypted messages": "Do të humbni hyrje te mesazhet tuaj të fshehtëzuar", + "Are you sure you want to sign out?": "Jeni i sigurt se doni të dilni?", + "Warning: you should only set up key backup from a trusted computer.": "Kujdes: duhet të ujdisni kopjeruajtje kyçesh vetëm nga një kompjuter i besuar.", + "Hide": "Fshihe", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Do të depozitojmë në shërbyesin tonë një kopje të fshehtëzuar të kyçeve tuaj. Mbrojeni kopjeruajtjen tuaj me një frazëkalim, për ta mbajtur të parrezikuar.", + "For maximum security, this should be different from your account password.": "Për maksimumin e sigurisë, ky do të duhej të ishte i ndryshëm nga fjalëkalimi juaj për llogarinë.", + "Set up with a Recovery Key": "Rregullojeni me një Kyç Rikthimesh", + "Please enter your passphrase a second time to confirm.": "Ju lutemi, që të ripohohet, rijepeni frazëkalimin tuaj.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kyçi juaj i rikthimeve është një rrjet sigurie - mund ta përdorni për rikthim hyrjeje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Mbajeni kyçin tuaj të rikthimeve diku në një vend shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë)", + "Your keys are being backed up (the first backup could take a few minutes).": "Kyçet tuaj po kopjeruhen (kopjeruajtja e parë mund të hajë disa minuta).", + "Okay": "Në rregull", + "Secure your backup with a passphrase": "Sigurojeni kopjeruajtjen tuaj me një frazëkalim", + "Confirm your passphrase": "Ripohoni frazëkalimin tuaj", + "Recovery key": "Kyç Rikthimesh", + "Success!": "Sukses!" } diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 479f8d68bb..9dbcad953f 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -1325,5 +1325,22 @@ "Capitalization doesn't help very much": "大写字母并没有很大的作用", "All-uppercase is almost as easy to guess as all-lowercase": "全大写的密码通常比全小写的更容易猜测", "Reversed words aren't much harder to guess": "把单词倒过来不会比原来的难猜很多", - "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "您也可以自定义身份服务器,但是您将不能用电子邮箱地址邀请或被邀请。" + "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "您也可以自定义身份服务器,但是您将不能用电子邮箱地址邀请他人或被邀请。", + "Whether or not you're logged in (we don't record your username)": "您是否已经登入(我们不会记录您的用户名)", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "文件 %(fileName)s 超过主服务器的文件大小限制", + "Upgrades a room to a new version": "将聊天室升级到新版本", + "Gets or sets the room topic": "获取或设置聊天室话题", + "This room has no topic.": "此聊天室没有话题。", + "Sets the room name": "设置聊天室名称", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s 升级了此聊天室。", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s 将此聊天室对知道此聊天室链接的人公开。", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s 将此聊天室改为仅限邀请。", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s 将加入规则改为 %(rule)s", + "%(displayName)s is typing …": "%(displayName)s 正在打字…", + "%(names)s and %(count)s others are typing …|other": "%(names)s 与其他 %(count)s 位正在打字…", + "%(names)s and %(count)s others are typing …|one": "%(names)s 与另一位正在打字…", + "%(names)s and %(lastPerson)s are typing …": "%(names)s 和 %(lastPerson)s正在打字…", + "Unrecognised address": "无法识别地址", + "User %(user_id)s may or may not exist": "用户 %(user_id)s 不一定存在", + "Predictable substitutions like '@' instead of 'a' don't help very much": "可预见的替换如将 '@' 替换为 'a' 并不会有太大效果" } diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 349f156655..6a95c64ce3 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -1524,7 +1524,7 @@ "Account management": "帳號管理", "2018 theme": "2018 主題", "Deactivating your account is a permanent action - be careful!": "停用您的帳號是永久動作,請小心!", - "Close Account": "關閉帳號", + "Deactivate Account": "關閉帳號", "For help with using Riot, click here.": "對於使用 Riot 的說明,點選這裡。", "For help with using Riot, click here or start a chat with our bot using the button below.": "對於使用 Riot 的說明,點選這裡或是使用下面的按鈕開始與我們的機器人聊天。", "Chat with Riot Bot": "與 Riot 機器人聊天", @@ -1625,5 +1625,114 @@ "Recovery Method Removed": "已移除復原方法", "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "此裝置已偵測到您的安全訊息復原通關密語與金鑰被移除。", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "如果您意外執行了此動作,您可以在此裝置上設定安全訊息來使用新的復原方法重新加密此裝置上的訊息歷史。", - "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "如果您沒有移除復原方法,攻擊者可能會試圖存取您的帳號。請立刻在設定中變更您帳號的密碼並設定新的復原方式。" + "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "如果您沒有移除復原方法,攻擊者可能會試圖存取您的帳號。請立刻在設定中變更您帳號的密碼並設定新的復原方式。", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "檔案 %(fileName)s 超過家伺服器的上傳限制", + "Gets or sets the room topic": "取得或設定聊天室主題", + "This room has no topic.": "此聊天室沒有主題。", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s 讓此聊天室對知道連結的人公開。", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s 讓聊天室變成僅限邀請。", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s 變更加入規則為 %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s 已允許訪克加入聊天室。", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s 已避免訪客加入聊天室。", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s 變更了訪客存取權限為 %(rule)s", + "Group & filter rooms by custom tags (refresh to apply changes)": "透過自訂標籤過濾群組與聊天室(重新整理以套用變更)", + "Verify this user by confirming the following emoji appear on their screen.": "透過確認顯示在他們螢幕下方的顏文字來確認使用者。", + "Unable to find a supported verification method.": "找不到支援的驗證方式。", + "Dog": "狗", + "Cat": "貓", + "Lion": "獅", + "Horse": "馬", + "Unicorn": "獨角獸", + "Pig": "豬", + "Elephant": "象", + "Rabbit": "兔", + "Panda": "熊貓", + "Rooster": "公雞", + "Penguin": "企鵝", + "Turtle": "烏龜", + "Fish": "魚", + "Octopus": "章魚", + "Butterfly": "蝴蝶", + "Flower": "花", + "Tree": "樹", + "Cactus": "仙人掌", + "Mushroom": "蘑菇", + "Globe": "地球", + "Moon": "月亮", + "Cloud": "雲", + "Fire": "火", + "Banana": "香蕉", + "Apple": "蘋果", + "Strawberry": "草苺", + "Corn": "玉米", + "Pizza": "披薩", + "Cake": "蛋糕", + "Heart": "心", + "Smiley": "微笑", + "Robot": "機器人", + "Hat": "帽子", + "Glasses": "眼鏡", + "Spanner": "扳手", + "Santa": "聖誕老人", + "Thumbs up": "豎起大姆指", + "Umbrella": "雨傘", + "Hourglass": "沙漏", + "Clock": "時鐘", + "Gift": "禮物", + "Light bulb": "燈泡", + "Book": "書", + "Pencil": "鉛筆", + "Paperclip": "迴紋針", + "Scisors": "剪刀", + "Padlock": "掛鎖", + "Key": "鑰匙", + "Hammer": "鎚子", + "Telephone": "電話", + "Flag": "旗子", + "Train": "火車", + "Bicycle": "腳踏車", + "Aeroplane": "飛機", + "Rocket": "火箭", + "Trophy": "獎盃", + "Ball": "球", + "Guitar": "吉他", + "Trumpet": "喇叭", + "Bell": "鈴", + "Anchor": "錨", + "Headphones": "耳機", + "Folder": "資料夾", + "Pin": "別針", + "Your homeserver does not support device management.": "您的家伺服器不支援裝置管理。", + "This device is not using key backup. Restore the backup to start using it.": "此裝置使用金鑰備份。復原備份以開始使用它。", + "This backup is trusted because it has been restored on this device": "因為此備份已在此裝置上復原,所以其被信任", + "Some devices for this user are not trusted": "此使用者的某些裝置不被信任", + "Some devices in this encrypted room are not trusted": "此已加密的聊天室中的某些裝置不被信任", + "All devices for this user are trusted": "此使用者的所有裝置都被信任", + "All devices in this encrypted room are trusted": "此已加密的聊天室中的所有裝置都被信任", + "Secure Key Backup should be active on all of your devices to avoid losing access to your encrypted messages.": "安全金鑰備份已在您所有的裝置上啟用以避免您遺失對已加密訊息的存取權。", + "Securely back up your decryption keys to the server to make sure you'll always be able to read your encrypted messages.": "安全地備份您的解密金鑰到伺服器上以確保您永遠都可以讀取您的加密訊息。", + "Don't risk losing your encrypted messages!": "不要冒著失去您加密訊息的風險!", + "Activate Secure Key Backup": "啟用安全金鑰備份", + "No thanks, I'll download a copy of my decryption keys before I log out": "不用了,我會在登出前下載一份我的解密金鑰的副本", + "Room Settings": "聊天室設定", + "Recovery Key Mismatch": "復原金鑰不符合", + "Incorrect Recovery Passphrase": "不正確的復原通關密語", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "備份無法使用此通關密語解密:請驗證您是否輸入正確的復原通關密語。", + "This homeserver would like to make sure you are not a robot.": "此家伺服器想要確保您不是機器人。", + "Sign in to %(serverName)s": "登入到 %(serverName)s", + "Change": "變更", + "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "使用電子郵件地址來復原您的帳號。其他使用者可以使用您的聯絡資訊來邀請您加入聊天室。", + "Couldn't load page": "無法載入頁面", + "This homeserver does not support communities": "此家伺服器不支援社群", + "Failed to get protocol list from homeserver": "從家伺服器取得協定清單失敗", + "The homeserver may be too old to support third party networks": "家伺服器可能老到不支援第三方網路了", + "Your account on %(serverName)s": "您在 %(serverName)s 上的帳號", + "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "家伺服器 URL %(hsUrl)s 不是有效的 URL。請輸入包含協定前綴的有效 URL。", + "A verification email will be sent to your inbox to confirm setting your new password.": "一封驗證用的電子郵件已經傳送到您的收件匣以確認您設定了新密碼。", + "Your password has been reset.": "您的密碼已重設。", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "您已從所有裝置登出,不會再收到推播通知了。要重新啟用通知,請再次於每個裝置上登入。", + "This homeserver does not support login using email address.": "此家伺服器不支援使用電子郵件地址登入。", + "Guest access is disabled on this homeserver.": "訪客存取權已在此家伺服器上停用。", + "Registration has been disabled on this homeserver.": "註冊已在此家伺服器上停用。", + "Unable to query for supported registration methods.": "無法查詢支援的註冊方法。" } diff --git a/src/ratelimitedfunc.js b/src/ratelimitedfunc.js index 20f6db79b8..1f15f11d91 100644 --- a/src/ratelimitedfunc.js +++ b/src/ratelimitedfunc.js @@ -20,54 +20,28 @@ limitations under the License. * to update the interface once for all of them. * * Note that the function must not take arguments, since the args - * could be different for each invocarion of the function. + * could be different for each invocation of the function. * * The returned function has a 'cancelPendingCall' property which can be called * on unmount or similar to cancel any pending update. */ -module.exports = function(f, minIntervalMs) { - this.lastCall = 0; - this.scheduledCall = undefined; - const self = this; - const wrapper = function() { - const now = Date.now(); +import { throttle } from "lodash"; - if (self.lastCall < now - minIntervalMs) { - f.apply(this); - // get the time again now the function has finished, so if it - // took longer than the delay time to execute, it doesn't - // immediately become eligible to run again. - self.lastCall = Date.now(); - } else if (self.scheduledCall === undefined) { - self.scheduledCall = setTimeout( - () => { - self.scheduledCall = undefined; - f.apply(this); - // get time again as per above - self.lastCall = Date.now(); - }, - (self.lastCall + minIntervalMs) - now, - ); - } +export default function ratelimitedfunc(fn, time) { + const throttledFn = throttle(fn, time, { + leading: true, + trailing: true, + }); + const _bind = throttledFn.bind; + throttledFn.bind = function() { + const boundFn = _bind.apply(throttledFn, arguments); + boundFn.cancelPendingCall = throttledFn.cancelPendingCall; + return boundFn; }; - // add the cancelPendingCall property - wrapper.cancelPendingCall = function() { - if (self.scheduledCall) { - clearTimeout(self.scheduledCall); - self.scheduledCall = undefined; - } + throttledFn.cancelPendingCall = function() { + throttledFn.cancel(); }; - - // make sure that cancelPendingCall is copied when react rebinds the - // wrapper - const _bind = wrapper.bind; - wrapper.bind = function() { - const rebound = _bind.apply(this, arguments); - rebound.cancelPendingCall = wrapper.cancelPendingCall; - return rebound; - }; - - return wrapper; -}; + return throttledFn; +} diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 9dfbc7d51e..4108848033 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -22,6 +22,7 @@ import { NotificationsEnabledController, } from "./controllers/NotificationControllers"; import CustomStatusController from "./controllers/CustomStatusController"; +import ThemeController from './controllers/ThemeController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -99,6 +100,12 @@ export const SETTINGS = { default: false, controller: new CustomStatusController(), }, + "feature_room_breadcrumbs": { + isFeature: true, + displayName: _td("Show recent room avatars above the room list (refresh to apply changes)"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_custom_tags": { isFeature: true, displayName: _td("Group & filter rooms by custom tags (refresh to apply changes)"), @@ -225,12 +232,13 @@ export const SETTINGS = { invertedSettingName: 'TagPanel.disableTagPanel', }, "theme": { - supportedLevels: ['config'], - default: "dharma", + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: "light", + controller: new ThemeController(), }, - "webRtcForcePeerToPeer": { + "webRtcAllowPeerToPeer": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, - displayName: _td('Disable Peer-to-Peer for 1:1 calls'), + displayName: _td('Allow Peer-to-Peer for 1:1 calls'), default: true, invertedSettingName: 'webRtcForceTURN', }, diff --git a/src/settings/controllers/ThemeController.js b/src/settings/controllers/ThemeController.js new file mode 100644 index 0000000000..615fc4c192 --- /dev/null +++ b/src/settings/controllers/ThemeController.js @@ -0,0 +1,33 @@ +/* +Copyright 2019 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 SettingController from "./SettingController"; + +const SUPPORTED_THEMES = [ + "light", + "dark", +]; + +export default class ThemeController extends SettingController { + getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { + // Override in case some no longer supported theme is stored here + if (!SUPPORTED_THEMES.includes(calculatedValue)) { + return "light"; + } + + return null; // no override + } +} diff --git a/src/settings/handlers/ConfigSettingsHandler.js b/src/settings/handlers/ConfigSettingsHandler.js index 67fff51e5b..a54ad1cef6 100644 --- a/src/settings/handlers/ConfigSettingsHandler.js +++ b/src/settings/handlers/ConfigSettingsHandler.js @@ -1,5 +1,6 @@ /* Copyright 2017 Travis Ralston +Copyright 2019 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. @@ -27,7 +28,7 @@ export default class ConfigSettingsHandler extends SettingsHandler { // Special case themes if (settingName === "theme") { - return "dharma"; // config["default_theme"]; + return config["default_theme"]; } const settingsConfig = config["settingDefaults"];