diff --git a/.stylelintrc.js b/.stylelintrc.js index 1690f2186f..313102ea83 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -17,7 +17,7 @@ module.exports = { "at-rule-no-unknown": null, "no-descending-specificity": null, "scss/at-rule-no-unknown": [true, { - // https://github.com/vector-im/riot-web/issues/10544 + // https://github.com/vector-im/element-web/issues/10544 "ignoreAtRules": ["define-mixin"], }], } diff --git a/CHANGELOG.md b/CHANGELOG.md index d944d58f36..468d7d211a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,57 @@ +Changes in [3.1.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.1.0) (2020-08-05) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.1.0-rc.1...v3.1.0) + + * Upgrade JS SDK to 8.0.1 + * Fix room list scrolling in Safari + [\#5091](https://github.com/matrix-org/matrix-react-sdk/pull/5091) + * Add null guard in InviteDialog + [\#5084](https://github.com/matrix-org/matrix-react-sdk/pull/5084) + * Add null guard in InviteDialog + [\#5082](https://github.com/matrix-org/matrix-react-sdk/pull/5082) + * Handle tag changes in sticky room updates + [\#5080](https://github.com/matrix-org/matrix-react-sdk/pull/5080) + +Changes in [3.1.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.1.0-rc.1) (2020-07-31) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.0.0...v3.1.0-rc.1) + + * Upgrade JS SDK to 8.0.1-rc.1 + * Update from Weblate + [\#5071](https://github.com/matrix-org/matrix-react-sdk/pull/5071) + * Add local echo for notifications in the new room list + [\#5065](https://github.com/matrix-org/matrix-react-sdk/pull/5065) + * Fix various small regressions in the room list's behaviour + [\#5070](https://github.com/matrix-org/matrix-react-sdk/pull/5070) + * Remove redundant lint dependencies + [\#5059](https://github.com/matrix-org/matrix-react-sdk/pull/5059) + * Fix key backup warning on soft logout page + [\#5069](https://github.com/matrix-org/matrix-react-sdk/pull/5069) + * Bump elliptic from 6.5.2 to 6.5.3 + [\#5066](https://github.com/matrix-org/matrix-react-sdk/pull/5066) + * Fix crash on logging in again after soft logout + [\#5068](https://github.com/matrix-org/matrix-react-sdk/pull/5068) + * Convert right_panel to TS + [\#5036](https://github.com/matrix-org/matrix-react-sdk/pull/5036) + * Remove all unreferenced images + [\#5063](https://github.com/matrix-org/matrix-react-sdk/pull/5063) + * Provide nicer error for no known servers error when accepting an invite + [\#5061](https://github.com/matrix-org/matrix-react-sdk/pull/5061) + * add logging for keytar/pickle key + [\#5057](https://github.com/matrix-org/matrix-react-sdk/pull/5057) + * Don't speak the outgoing message if it is in the Sending state. + [\#4075](https://github.com/matrix-org/matrix-react-sdk/pull/4075) + * Remove poorly contrasted "dark style" heading in Room Preview Bar + [\#5052](https://github.com/matrix-org/matrix-react-sdk/pull/5052) + * Fix Query Matcher regression with certain unhomoglyph'd characters + [\#5050](https://github.com/matrix-org/matrix-react-sdk/pull/5050) + * Fix handlebar interaction + [\#4989](https://github.com/matrix-org/matrix-react-sdk/pull/4989) + * Minor improvements to filtering performance + [\#5054](https://github.com/matrix-org/matrix-react-sdk/pull/5054) + * Fix TextWithTooltip "leaking" tooltip wrappers + [\#5055](https://github.com/matrix-org/matrix-react-sdk/pull/5055) + Changes in [3.0.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.0.0) (2020-07-27) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.10.1...v3.0.0) diff --git a/README.md b/README.md index 5f5da9a40d..e468d272d0 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ a 'skin'. A skin provides: * The containing application * Zero or more 'modules' containing non-UI functionality -As of Aug 2018, the only skin that exists is `vector-im/riot-web`; it and +As of Aug 2018, the only skin that exists is `vector-im/element-web`; it and `matrix-org/matrix-react-sdk` should effectively be considered as a single project (for instance, matrix-react-sdk bugs -are currently filed against vector-im/riot-web rather than this project). +are currently filed against vector-im/element-web rather than this project). Translation Status ================== -[![Translation status](https://translate.riot.im/widgets/riot-web/-/multi-auto.svg)](https://translate.riot.im/engage/riot-web/?utm_source=widget) +[![Translation status](https://translate.riot.im/widgets/element-web/-/multi-auto.svg)](https://translate.riot.im/engage/element-web/?utm_source=widget) Developer Guide =============== @@ -41,10 +41,10 @@ https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md Code should be committed as follows: * All new components: https://github.com/matrix-org/matrix-react-sdk/tree/master/src/components - * Riot-specific components: https://github.com/vector-im/riot-web/tree/master/src/components + * Element-specific components: https://github.com/vector-im/element-web/tree/master/src/components * In practice, `matrix-react-sdk` is still evolving so fast that the maintenance - burden of customising and overriding these components for Riot can seriously - impede development. So right now, there should be very few (if any) customisations for Riot. + burden of customising and overriding these components for Element can seriously + impede development. So right now, there should be very few (if any) customisations for Element. * CSS: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/css * Theme specific CSS & resources: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/themes @@ -71,7 +71,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold: * The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css). CSS for matrix-react-sdk currently resides in - https://github.com/vector-im/riot-web/tree/master/src/skins/vector/css/matrix-react-sdk. + https://github.com/vector-im/element-web/tree/master/src/skins/vector/css/matrix-react-sdk. * Per-view CSS is optional - it could choose to inherit all its styling from the context of the rest of the app, although this is unusual for any but @@ -125,7 +125,7 @@ from it. Github Issues ============= -All issues should be filed under https://github.com/vector-im/riot-web/issues +All issues should be filed under https://github.com/vector-im/element-web/issues for now. Development @@ -174,5 +174,5 @@ yarn test ## End-to-End tests -Make sure you've got your Riot development server running (by doing `yarn start` in riot-web), and then in this project, run `yarn run e2etests`. +Make sure you've got your Element development server running (by doing `yarn start` in element-web), and then in this project, run `yarn run e2etests`. See `test/end-to-end-tests/README.md` for more information. diff --git a/docs/ciderEditor.md b/docs/ciderEditor.md index 00033b5b8c..f522dc2fc4 100644 --- a/docs/ciderEditor.md +++ b/docs/ciderEditor.md @@ -1,6 +1,6 @@ # The CIDER (Contenteditable-Input-Diff-Error-Reconcile) editor -The CIDER editor is a custom editor written for Riot. +The CIDER editor is a custom editor written for Element. Most of the code can be found in the `/editor/` directory of the `matrix-react-sdk` project. It is used to power the composer main composer (both to send and edit messages), and might be used for other usecases where autocomplete is desired (invite box, ...). diff --git a/docs/jitsi.md b/docs/jitsi.md index 779ef79d3a..2b63ce0f72 100644 --- a/docs/jitsi.md +++ b/docs/jitsi.md @@ -25,7 +25,7 @@ which takes several parameters: be null. The react-sdk will assume that `jitsi.html` is at the path of wherever it is currently -being served. For example, `https://riot.im/develop/jitsi.html` or `vector://webapp/jitsi.html`. +being served. For example, `https://develop.element.io/jitsi.html` or `vector://webapp/jitsi.html`. The `jitsi.html` wrapper can use the react-sdk's `WidgetApi` to communicate, making it easier to actually implement the feature. diff --git a/docs/room-list-store.md b/docs/room-list-store.md index 53f0527209..fa849e2505 100644 --- a/docs/room-list-store.md +++ b/docs/room-list-store.md @@ -55,7 +55,7 @@ timestamp contained within the event (generated server-side by the sender's serv This is the easiest of the algorithms to understand because it does essentially nothing. It imposes no behavioural changes over the tag sorting algorithm and is by far the simplest way to order a room list. -Historically, it's been the only option in Riot and extremely common in most chat applications due to +Historically, it's been the only option in Element and extremely common in most chat applications due to its relative deterministic behaviour. ### List ordering algorithm: Importance diff --git a/docs/scrolling.md b/docs/scrolling.md index 71329e5c32..a5232359a7 100644 --- a/docs/scrolling.md +++ b/docs/scrolling.md @@ -13,7 +13,7 @@ ScrollPanel supports a mode to prevent it shrinking. This is used to prevent a j BACAT scrolling implements a different way of restoring the scroll position in the timeline while tiles out of view are changing height or tiles are being added or removed. It was added in https://github.com/matrix-org/matrix-react-sdk/pull/2842. -The motivation for the changes is having noticed that setting scrollTop while scrolling tends to not work well, with it interrupting ongoing scrolling and also querying scrollTop reporting outdated values and consecutive scroll adjustments cancelling each out previous ones. This seems to be worse on macOS than other platforms, presumably because of a higher resolution in scroll events there. Also see https://github.com/vector-im/riot-web/issues/528. The BACAT approach allows to only have to change the scroll offset when adding or removing tiles. +The motivation for the changes is having noticed that setting scrollTop while scrolling tends to not work well, with it interrupting ongoing scrolling and also querying scrollTop reporting outdated values and consecutive scroll adjustments cancelling each out previous ones. This seems to be worse on macOS than other platforms, presumably because of a higher resolution in scroll events there. Also see https://github.com/vector-im/element-web/issues/528. The BACAT approach allows to only have to change the scroll offset when adding or removing tiles. The approach taken instead is to vertically align the timeline tiles to the bottom of the scroll container (using flexbox) and give the timeline inside the scroll container an explicit height, initially set to a multiple of the PAGE_SIZE (400px at time of writing) as needed by the content. When scrolled up, we can compensate for anything that grew below the viewport by changing the height of the timeline to maintain what's currently visible in the viewport without adjusting the scrollTop and hence without jumping. diff --git a/docs/usercontent.md b/docs/usercontent.md index e54851dd0d..db0e34e5fa 100644 --- a/docs/usercontent.md +++ b/docs/usercontent.md @@ -5,9 +5,9 @@ letting the browser and user interact with the resulting data may be dangerous, previously `usercontent.riot.im` was used to act as a sandbox on a different origin to close the attack surface, it is now possible to do by using a combination of a sandboxed iframe and some code written into the app which consumes this SDK. -Usercontent is an iframe sandbox target for allowing a user to safely download a decrypted attachment from a sandboxed origin where it cannot be used to XSS your riot session out from under you. +Usercontent is an iframe sandbox target for allowing a user to safely download a decrypted attachment from a sandboxed origin where it cannot be used to XSS your Element session out from under you. -Its function is to create an Object URL for the user/browser to use but bound to an origin different to that of the riot instance to protect against XSS. +Its function is to create an Object URL for the user/browser to use but bound to an origin different to that of the Element instance to protect against XSS. It exposes a function over a postMessage API, when sent an object with the matching fields to render a download link with the Object URL: @@ -24,4 +24,4 @@ It exposes a function over a postMessage API, when sent an object with the match If only imgSrc, imgStyle and style are passed then just update the existing link without overwriting other things about it. -It is expected that this target be available at `usercontent/` relative to the root of the app, this can be seen in riot-web's webpack config. +It is expected that this target be available at `usercontent/` relative to the root of the app, this can be seen in element-web's webpack config. diff --git a/package.json b/package.json index 71f48fbf3b..c6f744912c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.0.0", + "version": "3.1.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -106,6 +106,7 @@ "devDependencies": { "@babel/cli": "^7.10.5", "@babel/core": "^7.10.5", + "@babel/parser": "^7.11.0", "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-decorators": "^7.10.5", "@babel/plugin-proposal-export-default-from": "^7.10.4", @@ -118,6 +119,7 @@ "@babel/preset-react": "^7.10.4", "@babel/preset-typescript": "^7.10.4", "@babel/register": "^7.10.5", + "@babel/traverse": "^7.11.0", "@peculiar/webcrypto": "^1.1.2", "@types/classnames": "^2.2.10", "@types/counterpart": "^0.18.1", @@ -147,9 +149,7 @@ "eslint-plugin-flowtype": "^2.50.3", "eslint-plugin-react": "^7.20.3", "eslint-plugin-react-hooks": "^2.5.1", - "estree-walker": "^0.9.0", "file-loader": "^3.0.1", - "flow-parser": "0.57.3", "glob": "^5.0.15", "jest": "^24.9.0", "jest-canvas-mock": "^2.2.0", diff --git a/res/css/_common.scss b/res/css/_common.scss index f2d3a0e54b..a22d77f3d3 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -38,7 +38,7 @@ body { margin: 0px; // needed to match the designs correctly on macOS - // see https://github.com/vector-im/riot-web/issues/11425 + // see https://github.com/vector-im/element-web/issues/11425 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @@ -436,7 +436,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { // TODO: Review mx_GeneralButton usage to see if it can use a different class // These classes were brought in from the old UserSettings and are included here to avoid // breaking the app. -// Ref: https://github.com/vector-im/riot-web/issues/8420 +// Ref: https://github.com/vector-im/element-web/issues/8420 .mx_GeneralButton { @mixin mx_DialogButton; display: inline; @@ -585,93 +585,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { max-width: 120px; } -// A context menu that largely fits the | [icon] [label] | format. -.mx_IconizedContextMenu { - min-width: 146px; - - .mx_IconizedContextMenu_optionList { - & > * { - padding-left: 20px; - padding-right: 20px; - } - - // the notFirst class is for cases where the optionList might be under a header of sorts. - &:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst { - // This is a bit of a hack when we could just use a simple border-top property, - // however we have a (kinda) good reason for doing it this way: we need opacity. - // To get the right color, we need an opacity modifier which means we have to work - // around the problem. PostCSS doesn't support the opacity() function, and if we - // use something like postcss-functions we quickly run into an issue where the - // function we would define gets passed a CSS variable for custom themes, which - // can't be converted easily even when considering https://stackoverflow.com/a/41265350/7037379 - // - // Therefore, we just hack in a line and border the thing ourselves - &::before { - border-top: 1px solid $primary-fg-color; - opacity: 0.1; - content: ''; - - // Counteract the padding problems (width: 100% ignores the 40px padding, - // unless we position it absolutely then it does the right thing). - width: 100%; - position: absolute; - left: 0; - } - } - - // round the top corners of the top button for the hover effect to be bounded - &:first-child .mx_AccessibleButton:first-child { - border-radius: 8px 8px 0 0; // radius matches .mx_ContextualMenu - } - - // round the bottom corners of the bottom button for the hover effect to be bounded - &:last-child .mx_AccessibleButton:last-child { - border-radius: 0 0 8px 8px; // radius matches .mx_ContextualMenu - } - - .mx_AccessibleButton { - // pad the inside of the button so that the hover background is padded too - padding-top: 12px; - padding-bottom: 12px; - text-decoration: none; - color: $primary-fg-color; - font-size: $font-15px; - line-height: $font-24px; - - // Create a flexbox to more easily define the list items - display: flex; - align-items: center; - - &:hover { - background-color: $menu-selected-color; - } - - img, .mx_IconizedContextMenu_icon { // icons - width: 16px; - min-width: 16px; - max-width: 16px; - } - - span.mx_IconizedContextMenu_label { // labels - padding-left: 14px; - width: 100%; - flex: 1; - - // Ellipsize any text overflow - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - } - } - - &.mx_IconizedContextMenu_compact { - .mx_IconizedContextMenu_optionList > * { - padding: 8px 16px 8px 11px; - } - } -} - @define-mixin ProgressBarColour $colour { color: $colour; &::-moz-progress-bar { @@ -692,3 +605,15 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { border-radius: $radius; } } + +@define-mixin unreal-focus { + outline-width: 2px; + outline-style: solid; + outline-color: Highlight; + + /* WebKit gets its native focus styles. */ + @media (-webkit-min-device-pixel-ratio: 0) { + outline-color: -webkit-focus-ring-color; + outline-style: auto; + } +} diff --git a/res/css/_components.scss b/res/css/_components.scss index fcc87e2061..7dd8a2034d 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -50,6 +50,7 @@ @import "./views/avatars/_DecoratedRoomAvatar.scss"; @import "./views/avatars/_MemberStatusMessageAvatar.scss"; @import "./views/avatars/_PulsedAvatar.scss"; +@import "./views/context_menus/_IconizedContextMenu.scss"; @import "./views/context_menus/_MessageContextMenu.scss"; @import "./views/context_menus/_RoomTileContextMenu.scss"; @import "./views/context_menus/_StatusMessageContextMenu.scss"; @@ -71,7 +72,6 @@ @import "./views/dialogs/_KeyboardShortcutsDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_NewSessionReviewDialog.scss"; -@import "./views/dialogs/_RebrandDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomSettingsDialogBridges.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; diff --git a/res/css/structures/_FilePanel.scss b/res/css/structures/_FilePanel.scss index 859ee28035..21b30d804a 100644 --- a/res/css/structures/_FilePanel.scss +++ b/res/css/structures/_FilePanel.scss @@ -41,13 +41,19 @@ limitations under the License. .mx_FilePanel .mx_EventTile { word-break: break-word; + margin-top: 32px; } .mx_FilePanel .mx_EventTile .mx_MImageBody { margin-right: 0px; } +.mx_FilePanel .mx_EventTile .mx_MFileBody { + line-height: 2.4rem; +} + .mx_FilePanel .mx_EventTile .mx_MFileBody_download { + padding-top: 8px; display: flex; font-size: $font-14px; color: $event-timestamp-color; @@ -60,7 +66,7 @@ limitations under the License. .mx_FilePanel .mx_EventTile .mx_MImageBody_size { flex: 1 0 0; - font-size: $font-11px; + font-size: $font-14px; text-align: right; white-space: nowrap; } @@ -80,7 +86,7 @@ limitations under the License. flex: 1 1 auto; line-height: initial; padding: 0px; - font-size: $font-11px; + font-size: $font-14px; opacity: 1.0; color: $event-timestamp-color; } @@ -90,7 +96,7 @@ limitations under the License. text-align: right; visibility: visible; position: initial; - font-size: $font-11px; + font-size: $font-14px; opacity: 1.0; color: $event-timestamp-color; } @@ -109,3 +115,7 @@ limitations under the License. .mx_FilePanel .mx_EventTile:hover .mx_EventTile_line { background-color: $primary-bg-color; } + +.mx_FilePanel_empty::before { + mask-image: url('$(res)/img/element-icons/room/files.svg'); +} diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index db531cf088..354dc87b8f 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -137,6 +137,7 @@ $tagPanelWidth: 56px; // only applies in this file, used for calculations .mx_LeftPanel_roomListWrapper { overflow: hidden; margin-top: 10px; // so we're not up against the search/filter + flex: 1 0 0; // needed in Safari to properly set flex-basis &.mx_LeftPanel_roomListWrapper_stickyBottom { padding-bottom: 32px; diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index af6f6c79e9..f4e46a8e94 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -72,7 +72,7 @@ limitations under the License. flex: 1 1 0; min-width: 0; - /* To fix https://github.com/vector-im/riot-web/issues/3298 where Safari + /* To fix https://github.com/vector-im/element-web/issues/3298 where Safari needed height 100% all the way down to the HomePage. Height does not have to be auto, empirically. */ diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index 561ab1446f..715a94fe2c 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -99,3 +99,7 @@ limitations under the License. .mx_NotificationPanel .mx_EventTile_content { margin-right: 0px; } + +.mx_NotificationPanel_empty::before { + mask-image: url('$(res)/img/element-icons/notifications.svg'); +} diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index cab32476ff..c7c0d6fac4 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -64,7 +64,7 @@ limitations under the License. left: 4px; // center with parent of 32px height: 24px; width: 24px; - background-color: $rightpanel-button-color; + background-color: $icon-button-color; mask-repeat: no-repeat; mask-size: contain; } @@ -99,7 +99,7 @@ limitations under the License. background: rgba($accent-color, 0.25); // make the icon the accent color too &::before { - background-color: $accent-color; + background-color: $accent-color !important; } } @@ -144,3 +144,28 @@ limitations under the License. order: 2; margin: auto; } + +.mx_RightPanel_empty { + margin-right: -42px; + + h2 { + font-weight: 700; + margin: 16px 0; + } + + h2, p { + font-size: $font-14px; + } + + &::before { + content: ''; + display: block; + margin: 11px auto 29px auto; + height: 42px; + width: 42px; + background-color: $rightpanel-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } +} diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index e798e4ac52..544dcbc180 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -80,10 +80,6 @@ limitations under the License. } } - &.mx_Toast_icon_element_logo::after { - background-image: url("$(res)/img/element-logo.svg"); - } - .mx_Toast_title, .mx_Toast_body { grid-column: 2; } diff --git a/res/css/structures/_UserMenu.scss b/res/css/structures/_UserMenu.scss index 81a10ee1d0..78795c85a2 100644 --- a/res/css/structures/_UserMenu.scss +++ b/res/css/structures/_UserMenu.scss @@ -89,15 +89,10 @@ limitations under the License. .mx_UserMenu_contextMenu { width: 247px; - .mx_UserMenu_contextMenu_redRow { + &.mx_IconizedContextMenu .mx_IconizedContextMenu_optionList_red { .mx_AccessibleButton { padding-top: 16px; padding-bottom: 16px; - color: $warning-color !important; // !important to override styles from context menu - } - - .mx_IconizedContextMenu_icon::before { - background-color: $warning-color; } } diff --git a/res/css/views/avatars/_BaseAvatar.scss b/res/css/views/avatars/_BaseAvatar.scss index e59598278f..1a1e14e7ac 100644 --- a/res/css/views/avatars/_BaseAvatar.scss +++ b/res/css/views/avatars/_BaseAvatar.scss @@ -22,7 +22,7 @@ limitations under the License. // different results during full reflow of the page vs. incremental reflow // of small portions. While that's surely a browser bug, we can avoid it by // using `inline-block` instead of the default `inline`. - // https://github.com/vector-im/riot-web/issues/5594 + // https://github.com/vector-im/element-web/issues/5594 // https://bugzilla.mozilla.org/show_bug.cgi?id=1535053 // https://bugzilla.mozilla.org/show_bug.cgi?id=255139 display: inline-block; diff --git a/res/css/views/context_menus/_IconizedContextMenu.scss b/res/css/views/context_menus/_IconizedContextMenu.scss new file mode 100644 index 0000000000..7913058995 --- /dev/null +++ b/res/css/views/context_menus/_IconizedContextMenu.scss @@ -0,0 +1,148 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// A context menu that largely fits the | [icon] [label] | format. +.mx_IconizedContextMenu { + min-width: 146px; + + .mx_IconizedContextMenu_optionList { + & > * { + padding-left: 20px; + padding-right: 20px; + } + + // the notFirst class is for cases where the optionList might be under a header of sorts. + &:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst { + // This is a bit of a hack when we could just use a simple border-top property, + // however we have a (kinda) good reason for doing it this way: we need opacity. + // To get the right color, we need an opacity modifier which means we have to work + // around the problem. PostCSS doesn't support the opacity() function, and if we + // use something like postcss-functions we quickly run into an issue where the + // function we would define gets passed a CSS variable for custom themes, which + // can't be converted easily even when considering https://stackoverflow.com/a/41265350/7037379 + // + // Therefore, we just hack in a line and border the thing ourselves + &::before { + border-top: 1px solid $primary-fg-color; + opacity: 0.1; + content: ''; + + // Counteract the padding problems (width: 100% ignores the 40px padding, + // unless we position it absolutely then it does the right thing). + width: 100%; + position: absolute; + left: 0; + } + } + + // round the top corners of the top button for the hover effect to be bounded + &:first-child .mx_AccessibleButton:first-child { + border-radius: 8px 8px 0 0; // radius matches .mx_ContextualMenu + } + + // round the bottom corners of the bottom button for the hover effect to be bounded + &:last-child .mx_AccessibleButton:last-child { + border-radius: 0 0 8px 8px; // radius matches .mx_ContextualMenu + } + + .mx_AccessibleButton { + // pad the inside of the button so that the hover background is padded too + padding-top: 12px; + padding-bottom: 12px; + text-decoration: none; + color: $primary-fg-color; + font-size: $font-15px; + line-height: $font-24px; + + // Create a flexbox to more easily define the list items + display: flex; + align-items: center; + + &:hover { + background-color: $menu-selected-color; + } + + img, .mx_IconizedContextMenu_icon { // icons + width: 16px; + min-width: 16px; + max-width: 16px; + } + + span.mx_IconizedContextMenu_label { // labels + padding-left: 14px; + width: 100%; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + + .mx_IconizedContextMenu_icon { + position: relative; + width: 16px; + height: 16px; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + + .mx_IconizedContextMenu_optionList_red { + .mx_AccessibleButton { + color: $warning-color !important; + } + + .mx_IconizedContextMenu_icon::before { + background-color: $warning-color; + } + } + + .mx_IconizedContextMenu_active { + &.mx_AccessibleButton, .mx_AccessibleButton { + color: $accent-color !important; + } + + .mx_IconizedContextMenu_icon::before { + background-color: $accent-color; + } + } + + &.mx_IconizedContextMenu_compact { + .mx_IconizedContextMenu_optionList > * { + padding: 8px 16px 8px 11px; + } + } + + .mx_IconizedContextMenu_checked { + margin-left: 16px; + margin-right: -5px; + + &::before { + mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg'); + } + } +} diff --git a/res/css/views/dialogs/_RebrandDialog.scss b/res/css/views/dialogs/_RebrandDialog.scss deleted file mode 100644 index 6c916e0f1d..0000000000 --- a/res/css/views/dialogs/_RebrandDialog.scss +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_RebrandDialog { - text-align: center; - - a:link, - a:hover, - a:visited { - @mixin mx_Dialog_link; - } - - .mx_Dialog_buttons { - margin-top: 43px; - text-align: center; - } -} - -.mx_RebrandDialog_body { - width: 550px; - margin-left: auto; - margin-right: auto; -} - -.mx_RebrandDialog_logoContainer { - margin-top: 35px; - margin-bottom: 20px; - display: flex; - align-items: center; - justify-content: center; -} - -.mx_RebrandDialog_logo { - margin-left: 28px; - margin-right: 28px; - width: 64px; - height: 64px; -} - -.mx_RebrandDialog_chevron::after { - content: ''; - display: inline-block; - width: 24px; - height: 24px; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background-color: $muted-fg-color; - mask-image: url('$(res)/img/feather-customised/chevron-right.svg'); -} diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index d2fe98e8f9..c343b872fd 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -51,7 +51,8 @@ limitations under the License. display: inherit; } .mx_ShareDialog_matrixto_copy > div { - background-image: url($copy-button-url); + mask-image: url($copy-button-url); + background-color: $message-action-bar-fg-color; margin-left: 5px; width: 20px; height: 20px; diff --git a/res/css/views/directory/_NetworkDropdown.scss b/res/css/views/directory/_NetworkDropdown.scss index bd5c67c7ed..ae0927386a 100644 --- a/res/css/views/directory/_NetworkDropdown.scss +++ b/res/css/views/directory/_NetworkDropdown.scss @@ -145,13 +145,14 @@ limitations under the License. &::after { content: ""; position: absolute; - width: 24px; - height: 24px; - right: -28px; // - (24 + 4) + width: 26px; + height: 26px; + right: -27.5px; // - (width: 26 + spacing to align with X above: 1.5) + top: -3px; mask-repeat: no-repeat; mask-position: center; mask-size: contain; - mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + mask-image: url('$(res)/img/feather-customised/chevron-down-thin.svg'); background-color: $primary-fg-color; } diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index 60f1bf0277..e2d61c033b 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -80,5 +80,11 @@ limitations under the License. background-color: $accent-color; border-color: $accent-color; } + + &.focus-visible { + & + label .mx_Checkbox_background { + @mixin unreal-focus; + } + } } } diff --git a/res/css/views/elements/_StyledRadioButton.scss b/res/css/views/elements/_StyledRadioButton.scss index ffa1337ebb..62fb5c5512 100644 --- a/res/css/views/elements/_StyledRadioButton.scss +++ b/res/css/views/elements/_StyledRadioButton.scss @@ -63,6 +63,7 @@ limitations under the License. box-sizing: border-box; height: $font-16px; width: $font-16px; + margin-left: 2px; // For the highlight on focus border: $font-1-5px solid $radio-circle-color; border-radius: $font-16px; @@ -77,6 +78,12 @@ limitations under the License. } } + &.focus-visible { + & + div { + @mixin unreal-focus; + } + } + &:checked { & + div { border-color: $active-radio-circle-color; diff --git a/res/css/views/messages/_MImageBody.scss b/res/css/views/messages/_MImageBody.scss index 547b16e9ad..2faea41709 100644 --- a/res/css/views/messages/_MImageBody.scss +++ b/res/css/views/messages/_MImageBody.scss @@ -34,6 +34,8 @@ limitations under the License. // Make sure the _thumbnail is positioned relative to the _container position: relative; + + border-radius: 4px; } .mx_MImageBody_thumbnail_spinner { diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index e3ccd99611..d2ff551668 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -24,7 +24,7 @@ limitations under the License. line-height: $font-24px; border-radius: 4px; background: $message-action-bar-bg-color; - top: -18px; + top: -26px; right: 8px; user-select: none; // Ensure the action bar appears above over things, like the read marker. diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 2a2191b799..eb0e1dd7b0 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -536,11 +536,12 @@ $left-gutter: 64px; display: inline-block; visibility: hidden; cursor: pointer; - top: 8px; + top: 6px; right: 6px; width: 19px; height: 19px; - background-image: url($copy-button-url); + mask-image: url($copy-button-url); + background-color: $message-action-bar-fg-color; } .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton, diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index ed60c220e7..958d718b11 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -54,7 +54,7 @@ $irc-line-height: $font-18px; flex-shrink: 0; width: var(--name-width); text-overflow: ellipsis; - text-align: right; + text-align: left; display: flex; align-items: center; overflow: visible; diff --git a/res/css/views/rooms/_JumpToBottomButton.scss b/res/css/views/rooms/_JumpToBottomButton.scss index 4659442d9b..6cb3b6bce9 100644 --- a/res/css/views/rooms/_JumpToBottomButton.scss +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -67,9 +67,8 @@ limitations under the License. bottom: 0; left: 0; right: 0; - mask: url('$(res)/img/icon-jump-to-bottom.svg'); + mask-image: url('$(res)/img/feather-customised/chevron-down-thin.svg'); mask-repeat: no-repeat; - mask-position: center; - mask-size: 50%; + mask-size: contain; background: $muted-fg-color; } diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index ec95403262..a403a8dc4c 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -21,6 +21,7 @@ limitations under the License. border-top: 1px solid $primary-hairline-color; position: relative; padding-left: 82px; + padding-right: 6px; } .mx_MessageComposer_replaced_wrapper { @@ -178,25 +179,44 @@ limitations under the License. color: $accent-color; } +.mx_MessageComposer_button_highlight { + background: rgba($accent-color, 0.25); + // make the icon the accent color too + &::before { + background-color: $accent-color !important; + } +} + .mx_MessageComposer_button { position: relative; - margin-right: 12px; + margin-right: 6px; cursor: pointer; - height: 20px; - width: 20px; + height: 26px; + width: 26px; + border-radius: 100%; &::before { content: ''; position: absolute; + top: 3px; + left: 3px; height: 20px; width: 20px; - background-color: $composer-button-color; + background-color: $icon-button-color; mask-repeat: no-repeat; mask-size: contain; mask-position: center; } + &:hover { + background: rgba($accent-color, 0.1); + + &::before { + background-color: $accent-color; + } + } + &.mx_MessageComposer_hangup::before { background-color: $warning-color; } @@ -288,7 +308,7 @@ limitations under the License. mask-size: contain; mask-position: center; mask-repeat: no-repeat; - background-color: $composer-button-color; + background-color: $icon-button-color; &.mx_MessageComposer_markdownDisabled { opacity: 0.2; diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index ba46100ea6..a880a7bee2 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -222,7 +222,7 @@ limitations under the License. left: 4px; // center with parent of 32px height: 24px; width: 24px; - background-color: $roomheader-button-color; + background-color: $icon-button-color; mask-repeat: no-repeat; mask-size: contain; } diff --git a/res/css/views/rooms/_RoomSublist.scss b/res/css/views/rooms/_RoomSublist.scss index b907d06d36..fe80dfca22 100644 --- a/res/css/views/rooms/_RoomSublist.scss +++ b/res/css/views/rooms/_RoomSublist.scss @@ -142,26 +142,24 @@ limitations under the License. .mx_RoomSublist_collapseBtn { display: inline-block; position: relative; - width: 12px; - height: 12px; - margin-right: 8px; + width: 14px; + height: 14px; + margin-right: 6px; &::before { content: ''; - width: 12px; - height: 12px; + width: 18px; + height: 18px; position: absolute; - top: 1px; - left: 1px; mask-position: center; mask-size: contain; mask-repeat: no-repeat; - background-color: $primary-fg-color; + background-color: $roomlist-header-color; mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } &.mx_RoomSublist_collapseBtn_collapsed::before { - mask-image: url('$(res)/img/feather-customised/chevron-right.svg'); + transform: rotate(-90deg); } } } @@ -171,7 +169,7 @@ limitations under the License. // that the sublists below them do not jump. However, that leaves a gap // when scrolled to the top above the first sublist (whose header can only // ever stick to top), so we force height to 0 for only that first header. - // See also https://github.com/vector-im/riot-web/issues/14429. + // See also https://github.com/vector-im/element-web/issues/14429. &:first-child .mx_RoomSublist_headerContainer { height: 0; padding-bottom: 4px; @@ -251,22 +249,24 @@ limitations under the License. .mx_RoomSublist_showNButtonChevron { position: relative; - width: 16px; - height: 16px; + width: 18px; + height: 18px; margin-left: 12px; - margin-right: 18px; + margin-right: 16px; mask-position: center; mask-size: contain; mask-repeat: no-repeat; - background: $roomtile-preview-color; + background: $roomlist-header-color; + left: -1px; // adjust for image position } - .mx_RoomSublist_showMoreButtonChevron { + .mx_RoomSublist_showMoreButtonChevron, + .mx_RoomSublist_showLessButtonChevron { mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } .mx_RoomSublist_showLessButtonChevron { - mask-image: url('$(res)/img/feather-customised/chevron-up.svg'); + transform: rotate(180deg); } } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index f22228602d..8eca3f1efa 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -175,48 +175,8 @@ limitations under the License. .mx_RoomTile_iconBellMentions::before { mask-image: url('$(res)/img/element-icons/roomlist/notifications-dm.svg'); } -.mx_RoomTile_iconCheck::before { - mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg'); -} .mx_RoomTile_contextMenu { - .mx_RoomTile_contextMenu_redRow { - .mx_AccessibleButton { - color: $warning-color !important; // !important to override styles from context menu - } - - .mx_IconizedContextMenu_icon::before { - background-color: $warning-color; - } - } - - .mx_RoomTile_contextMenu_activeRow { - &.mx_AccessibleButton, .mx_AccessibleButton { - color: $accent-color !important; // !important to override styles from context menu - } - - .mx_IconizedContextMenu_icon::before { - background-color: $accent-color; - } - } - - .mx_IconizedContextMenu_icon { - position: relative; - width: 16px; - height: 16px; - - &::before { - content: ''; - width: 16px; - height: 16px; - position: absolute; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $primary-fg-color; - } - } - .mx_RoomTile_iconStar::before { mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg'); } diff --git a/res/css/views/rooms/_TopUnreadMessagesBar.scss b/res/css/views/rooms/_TopUnreadMessagesBar.scss index d9970ef037..8841b042a0 100644 --- a/res/css/views/rooms/_TopUnreadMessagesBar.scss +++ b/res/css/views/rooms/_TopUnreadMessagesBar.scss @@ -51,11 +51,11 @@ limitations under the License. position: absolute; width: 36px; height: 36px; - mask-image: url('$(res)/img/icon-jump-to-first-unread.svg'); + mask-image: url('$(res)/img/feather-customised/chevron-down-thin.svg'); mask-repeat: no-repeat; - mask-position: center; - mask-size: 50%; + mask-size: contain; background: $muted-fg-color; + transform: rotate(180deg); } .mx_TopUnreadMessagesBar_markAsRead { diff --git a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss index b5a57dfefb..23dcc532b2 100644 --- a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss @@ -14,10 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_SecurityRoomSettingsTab label { - display: block; -} - .mx_SecurityRoomSettingsTab_warning { display: block; diff --git a/res/img/element-logo.svg b/res/img/element-logo.svg deleted file mode 100644 index 2cd11ed193..0000000000 --- a/res/img/element-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/feather-customised/chevron-down-thin.svg b/res/img/feather-customised/chevron-down-thin.svg new file mode 100644 index 0000000000..109c83def6 --- /dev/null +++ b/res/img/feather-customised/chevron-down-thin.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/feather-customised/chevron-down.svg b/res/img/feather-customised/chevron-down.svg index bcb185ede7..a091913b42 100644 --- a/res/img/feather-customised/chevron-down.svg +++ b/res/img/feather-customised/chevron-down.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/feather-customised/chevron-right.svg b/res/img/feather-customised/chevron-right.svg deleted file mode 100644 index 258de414a1..0000000000 --- a/res/img/feather-customised/chevron-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/img/feather-customised/chevron-up.svg b/res/img/feather-customised/chevron-up.svg deleted file mode 100644 index 4eb5ecc33e..0000000000 --- a/res/img/feather-customised/chevron-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/img/feather-customised/clipboard.svg b/res/img/feather-customised/clipboard.svg new file mode 100644 index 0000000000..b25b97176c --- /dev/null +++ b/res/img/feather-customised/clipboard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/icon-jump-to-bottom.svg b/res/img/icon-jump-to-bottom.svg deleted file mode 100644 index c4210b4ebe..0000000000 --- a/res/img/icon-jump-to-bottom.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/res/img/icon-jump-to-first-unread.svg b/res/img/icon-jump-to-first-unread.svg deleted file mode 100644 index 652ccec20d..0000000000 --- a/res/img/icon-jump-to-first-unread.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/res/img/icon_copy_message.svg b/res/img/icon_copy_message.svg deleted file mode 100644 index 8d8887bb22..0000000000 --- a/res/img/icon_copy_message.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - image/svg+xml - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - - - - - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - Created with sketchtool. - - - - - - - - - diff --git a/res/img/riot-logo.svg b/res/img/riot-logo.svg deleted file mode 100644 index ac1e547234..0000000000 --- a/res/img/riot-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 15155ba854..8175e7d33d 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -18,6 +18,10 @@ $primary-fg-color: $text-primary-color; $primary-bg-color: $bg-color; $muted-fg-color: $header-panel-text-primary-color; +// additional text colors +$secondary-fg-color: #A9B2BC; +$tertiary-fg-color: #8E99A4; + // used for dialog box text $light-fg-color: $header-panel-text-secondary-color; @@ -42,7 +46,7 @@ $inverted-bg-color: $base-color; $selected-color: $room-highlight-color; // selected for hoverover & selected event tiles -$event-selected-color: $header-panel-bg-color; +$event-selected-color: #21262c; // used for the hairline dividers in RoomView $primary-hairline-color: transparent; @@ -96,10 +100,9 @@ $roomheader-bg-color: $bg-color; $roomheader-addroom-bg-color: rgba(92, 100, 112, 0.3); $roomheader-addroom-fg-color: $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; +$icon-button-color: #8E99A4; $roomtopic-color: $text-secondary-color; $eventtile-meta-color: $roomtopic-color; @@ -112,10 +115,10 @@ $theme-button-bg-color: #e3e8f0; $roomlist-button-bg-color: rgba(141, 151, 165, 0.2); // Buttons include the filter box, explore button, and sublist buttons $roomlist-bg-color: rgba(33, 38, 44, 0.90); -$roomlist-header-color: #8E99A4; +$roomlist-header-color: $tertiary-fg-color; $roomsublist-divider-color: $primary-fg-color; -$roomtile-preview-color: #A9B2BC; +$roomtile-preview-color: $secondary-fg-color; $roomtile-default-badge-bg-color: #61708b; $roomtile-selected-bg-color: rgba(141, 151, 165, 0.2); diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index 7ecfcf13d9..4268fad030 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -95,10 +95,9 @@ $roomheader-color: $text-primary-color; $roomheader-addroom-bg-color: #3c4556; // $search-placeholder-color at 0.5 opacity $roomheader-addroom-fg-color: $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; +$icon-button-color: $header-panel-text-primary-color; $roomtopic-color: $text-secondary-color; $eventtile-meta-color: $roomtopic-color; diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 3465aa307e..5ebb4ccc02 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -162,10 +162,9 @@ $roomheader-bg-color: $primary-bg-color; $roomheader-addroom-bg-color: #91a1c0; $roomheader-addroom-fg-color: $accent-fg-color; $tagpanel-button-color: #91a1c0; -$roomheader-button-color: #91a1c0; $groupheader-button-color: #91a1c0; $rightpanel-button-color: #91a1c0; -$composer-button-color: #91a1c0; +$icon-button-color: #91a1c0; $roomtopic-color: #9e9e9e; $eventtile-meta-color: $roomtopic-color; @@ -228,7 +227,8 @@ $event-redacted-border-color: #cccccc; // event timestamp $event-timestamp-color: #acacac; -$copy-button-url: "$(res)/img/icon_copy_message.svg"; +$copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; + // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/res/themes/legacy-light/css/_paths.scss b/res/themes/legacy-light/css/_paths.scss index 0744347826..3944076004 100644 --- a/res/themes/legacy-light/css/_paths.scss +++ b/res/themes/legacy-light/css/_paths.scss @@ -1,3 +1,3 @@ // Path from root SCSS file (such as `light.scss`) to `res` dir in the source tree -// This value is overridden by external themes in `riot-web`. +// This value is overridden by external themes in `element-web`. $res: ../../..; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index e317683963..e9ade7eb97 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -19,8 +19,8 @@ $accent-bg-color: rgba(3, 179, 129, 0.16); $notice-primary-color: #ff4b55; $notice-primary-bg-color: rgba(255, 75, 85, 0.16); $primary-fg-color: #2e2f32; -$roomlist-header-color: $primary-fg-color; -$notice-secondary-color: $roomlist-header-color; +$secondary-fg-color: #737D8C; +$tertiary-fg-color: #8D99A5; $header-panel-bg-color: #f3f8fd; // typical text (dark-on-white in light skin) @@ -52,10 +52,6 @@ $info-bg-color: #2A9EDF; $mention-user-pill-bg-color: $warning-color; $other-user-pill-bg-color: rgba(0, 0, 0, 0.1); -// pinned events indicator -$pinned-unread-color: $notice-primary-color; -$pinned-color: $notice-secondary-color; - // informational plinth $info-plinth-bg-color: #f7f7f7; $info-plinth-fg-color: #888; @@ -78,7 +74,7 @@ $droptarget-bg-color: rgba(255,255,255,0.5); $selected-color: $secondary-accent-color; // selected for hoverover & selected event tiles -$event-selected-color: $header-panel-bg-color; +$event-selected-color: #f6f7f8; // used for the hairline dividers in RoomView $primary-hairline-color: transparent; @@ -162,10 +158,9 @@ $roomheader-bg-color: $primary-bg-color; $roomheader-addroom-bg-color: rgba(92, 100, 112, 0.2); $roomheader-addroom-fg-color: #5c6470; $tagpanel-button-color: #91A1C0; -$roomheader-button-color: #91A1C0; $groupheader-button-color: #91A1C0; $rightpanel-button-color: #91A1C0; -$composer-button-color: #91A1C0; +$icon-button-color: #C1C6CD; $roomtopic-color: #9e9e9e; $eventtile-meta-color: $roomtopic-color; @@ -178,9 +173,10 @@ $theme-button-bg-color: #e3e8f0; $roomlist-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons $roomlist-bg-color: rgba(245, 245, 245, 0.90); +$roomlist-header-color: $tertiary-fg-color; $roomsublist-divider-color: $primary-fg-color; -$roomtile-preview-color: #737D8C; +$roomtile-preview-color: $secondary-fg-color; $roomtile-default-badge-bg-color: #61708b; $roomtile-selected-bg-color: #FFF; @@ -199,8 +195,14 @@ $username-variant6-color: #2dc2c5; $username-variant7-color: #5c56f5; $username-variant8-color: #74d12c; +$notice-secondary-color: $roomlist-header-color; + $panel-divider-color: transparent; +// pinned events indicator +$pinned-unread-color: $notice-primary-color; +$pinned-color: $notice-secondary-color; + // ******************** $widget-menu-bar-bg-color: $secondary-accent-color; @@ -227,7 +229,7 @@ $event-redacted-border-color: #cccccc; // event timestamp $event-timestamp-color: #acacac; -$copy-button-url: "$(res)/img/icon_copy_message.svg"; +$copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/res/themes/light/css/_paths.scss b/res/themes/light/css/_paths.scss index 0744347826..3944076004 100644 --- a/res/themes/light/css/_paths.scss +++ b/res/themes/light/css/_paths.scss @@ -1,3 +1,3 @@ // Path from root SCSS file (such as `light.scss`) to `res` dir in the source tree -// This value is overridden by external themes in `riot-web`. +// This value is overridden by external themes in `element-web`. $res: ../../..; diff --git a/scripts/ci/end-to-end-tests.sh b/scripts/ci/end-to-end-tests.sh index 1233677db4..7a62c03b12 100755 --- a/scripts/ci/end-to-end-tests.sh +++ b/scripts/ci/end-to-end-tests.sh @@ -13,7 +13,7 @@ handle_error() { trap 'handle_error' ERR -echo "--- Building Riot" +echo "--- Building Element" scripts/ci/layered-riot-web.sh cd ../riot-web riot_web_dir=`pwd` diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index a1823cdf50..91733469f7 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -18,7 +18,7 @@ limitations under the License. /** * Regenerates the translations en_EN file by walking the source tree and - * parsing each file with flow-parser. Emits a JSON file with the + * parsing each file with the appropriate parser. Emits a JSON file with the * translatable strings mapped to themselves in the order they appeared * in the files and grouped by the file they appeared in. * @@ -29,8 +29,8 @@ const path = require('path'); const walk = require('walk'); -const flowParser = require('flow-parser'); -const estreeWalker = require('estree-walker'); +const parser = require("@babel/parser"); +const traverse = require("@babel/traverse"); const TRANSLATIONS_FUNCS = ['_t', '_td']; @@ -44,17 +44,9 @@ const OUTPUT_FILE = 'src/i18n/strings/en_EN.json'; // to a project that's actively maintained. const SEARCH_PATHS = ['src', 'res']; -const FLOW_PARSER_OPTS = { - esproposal_class_instance_fields: true, - esproposal_class_static_fields: true, - esproposal_decorators: true, - esproposal_export_star_as: true, - types: true, -}; - function getObjectValue(obj, key) { for (const prop of obj.properties) { - if (prop.key.type == 'Identifier' && prop.key.name == key) { + if (prop.key.type === 'Identifier' && prop.key.name === key) { return prop.value; } } @@ -62,11 +54,11 @@ function getObjectValue(obj, key) { } function getTKey(arg) { - if (arg.type == 'Literal') { + if (arg.type === 'Literal' || arg.type === "StringLiteral") { return arg.value; - } else if (arg.type == 'BinaryExpression' && arg.operator == '+') { + } else if (arg.type === 'BinaryExpression' && arg.operator === '+') { return getTKey(arg.left) + getTKey(arg.right); - } else if (arg.type == 'TemplateLiteral') { + } else if (arg.type === 'TemplateLiteral') { return arg.quasis.map((q) => { return q.value.raw; }).join(''); @@ -110,81 +102,112 @@ function getFormatStrings(str) { } function getTranslationsJs(file) { - const tree = flowParser.parse(fs.readFileSync(file, { encoding: 'utf8' }), FLOW_PARSER_OPTS); + const contents = fs.readFileSync(file, { encoding: 'utf8' }); const trs = new Set(); - estreeWalker.walk(tree, { - enter: function(node, parent) { - if ( - node.type == 'CallExpression' && - TRANSLATIONS_FUNCS.includes(node.callee.name) - ) { - const tKey = getTKey(node.arguments[0]); - // This happens whenever we call _t with non-literals (ie. whenever we've - // had to use a _td to compensate) so is expected. - if (tKey === null) return; + try { + const plugins = [ + // https://babeljs.io/docs/en/babel-parser#plugins + "classProperties", + "objectRestSpread", + "throwExpressions", + "exportDefaultFrom", + "decorators-legacy", + ]; - // check the format string against the args - // We only check _t: _td has no args - if (node.callee.name === '_t') { - try { - const placeholders = getFormatStrings(tKey); - for (const placeholder of placeholders) { - if (node.arguments.length < 2) { - throw new Error(`Placeholder found ('${placeholder}') but no substitutions given`); - } - const value = getObjectValue(node.arguments[1], placeholder); - if (value === null) { - throw new Error(`No value found for placeholder '${placeholder}'`); - } - } + if (file.endsWith(".js") || file.endsWith(".jsx")) { + // all JS is assumed to be flow or react + plugins.push("flow", "jsx"); + } else if (file.endsWith(".ts")) { + // TS can't use JSX unless it's a TSX file (otherwise angle casts fail) + plugins.push("typescript"); + } else if (file.endsWith(".tsx")) { + // When the file is a TSX file though, enable JSX parsing + plugins.push("typescript", "jsx"); + } - // Validate tag replacements - if (node.arguments.length > 2) { - const tagMap = node.arguments[2]; - for (const prop of tagMap.properties || []) { - if (prop.key.type === 'Literal') { - const tag = prop.key.value; - // RegExp same as in src/languageHandler.js - const regexp = new RegExp(`(<${tag}>(.*?)<\\/${tag}>|<${tag}>|<${tag}\\s*\\/>)`); - if (!tKey.match(regexp)) { - throw new Error(`No match for ${regexp} in ${tKey}`); + const babelParsed = parser.parse(contents, { + allowImportExportEverywhere: true, + errorRecovery: true, + sourceFilename: file, + tokens: true, + plugins, + }); + traverse.default(babelParsed, { + enter: (p) => { + const node = p.node; + if (p.isCallExpression() && node.callee && TRANSLATIONS_FUNCS.includes(node.callee.name)) { + const tKey = getTKey(node.arguments[0]); + + // This happens whenever we call _t with non-literals (ie. whenever we've + // had to use a _td to compensate) so is expected. + if (tKey === null) return; + + // check the format string against the args + // We only check _t: _td has no args + if (node.callee.name === '_t') { + try { + const placeholders = getFormatStrings(tKey); + for (const placeholder of placeholders) { + if (node.arguments.length < 2) { + throw new Error(`Placeholder found ('${placeholder}') but no substitutions given`); + } + const value = getObjectValue(node.arguments[1], placeholder); + if (value === null) { + throw new Error(`No value found for placeholder '${placeholder}'`); + } + } + + // Validate tag replacements + if (node.arguments.length > 2) { + const tagMap = node.arguments[2]; + for (const prop of tagMap.properties || []) { + if (prop.key.type === 'Literal') { + const tag = prop.key.value; + // RegExp same as in src/languageHandler.js + const regexp = new RegExp(`(<${tag}>(.*?)<\\/${tag}>|<${tag}>|<${tag}\\s*\\/>)`); + if (!tKey.match(regexp)) { + throw new Error(`No match for ${regexp} in ${tKey}`); + } } } } - } - } catch (e) { - console.log(); - console.error(`ERROR: ${file}:${node.loc.start.line} ${tKey}`); - console.error(e); - process.exit(1); - } - } - - let isPlural = false; - if (node.arguments.length > 1 && node.arguments[1].type == 'ObjectExpression') { - const countVal = getObjectValue(node.arguments[1], 'count'); - if (countVal) { - isPlural = true; - } - } - - if (isPlural) { - trs.add(tKey + "|other"); - const plurals = enPlurals[tKey]; - if (plurals) { - for (const pluralType of Object.keys(plurals)) { - trs.add(tKey + "|" + pluralType); + } catch (e) { + console.log(); + console.error(`ERROR: ${file}:${node.loc.start.line} ${tKey}`); + console.error(e); + process.exit(1); } } - } else { - trs.add(tKey); + + let isPlural = false; + if (node.arguments.length > 1 && node.arguments[1].type === 'ObjectExpression') { + const countVal = getObjectValue(node.arguments[1], 'count'); + if (countVal) { + isPlural = true; + } + } + + if (isPlural) { + trs.add(tKey + "|other"); + const plurals = enPlurals[tKey]; + if (plurals) { + for (const pluralType of Object.keys(plurals)) { + trs.add(tKey + "|" + pluralType); + } + } + } else { + trs.add(tKey); + } } - } - } - }); + }, + }); + } catch (e) { + console.error(e); + process.exit(1); + } return trs; } @@ -194,7 +217,7 @@ function getTranslationsOther(file) { const trs = new Set(); - // Taken from riot-web src/components/structures/HomePage.js + // Taken from element-web src/components/structures/HomePage.js const translationsRegex = /_t\(['"]([\s\S]*?)['"]\)/mg; let matches; while (matches = translationsRegex.exec(contents)) { diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 6510c02160..13520e218d 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -19,13 +19,14 @@ import ContentMessages from "../ContentMessages"; import { IMatrixClientPeg } from "../MatrixClientPeg"; import ToastStore from "../stores/ToastStore"; import DeviceListener from "../DeviceListener"; -import RebrandListener from "../RebrandListener"; import { RoomListStoreClass } from "../stores/room-list/RoomListStore"; import { PlatformPeg } from "../PlatformPeg"; import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; import {IntegrationManagers} from "../integrations/IntegrationManagers"; import {ModalManager} from "../Modal"; import SettingsStore from "../settings/SettingsStore"; +import {ActiveRoomObserver} from "../ActiveRoomObserver"; +import {Notifier} from "../Notifier"; declare global { interface Window { @@ -38,13 +39,14 @@ declare global { mxContentMessages: ContentMessages; mxToastStore: ToastStore; mxDeviceListener: DeviceListener; - mxRebrandListener: RebrandListener; mxRoomListStore: RoomListStoreClass; mxRoomListLayoutStore: RoomListLayoutStore; + mxActiveRoomObserver: ActiveRoomObserver; mxPlatformPeg: PlatformPeg; mxIntegrationManagers: typeof IntegrationManagers; singletonModalManager: ModalManager; mxSettingsStore: SettingsStore; + mxNotifier: typeof Notifier; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 @@ -77,4 +79,8 @@ declare global { interface PromiseConstructor { allSettled(promises: Promise[]): Promise | ISettledRejected>>; } + + interface HTMLAudioElement { + type?: string; + } } diff --git a/src/ActiveRoomObserver.js b/src/ActiveRoomObserver.ts similarity index 54% rename from src/ActiveRoomObserver.js rename to src/ActiveRoomObserver.ts index b7695d401d..1126dc9496 100644 --- a/src/ActiveRoomObserver.js +++ b/src/ActiveRoomObserver.ts @@ -16,6 +16,8 @@ limitations under the License. import RoomViewStore from './stores/RoomViewStore'; +type Listener = (isActive: boolean) => void; + /** * Consumes changes from the RoomViewStore and notifies specific things * about when the active room changes. Unlike listening for RoomViewStore @@ -25,57 +27,57 @@ import RoomViewStore from './stores/RoomViewStore'; * TODO: If we introduce an observer for something else, factor out * the adding / removing of listeners & emitting into a common class. */ -class ActiveRoomObserver { - constructor() { - this._listeners = {}; // key=roomId, value=function(isActive:boolean) +export class ActiveRoomObserver { + private listeners: {[key: string]: Listener[]} = {}; + private _activeRoomId = RoomViewStore.getRoomId(); + private readonly roomStoreToken: string; - this._activeRoomId = RoomViewStore.getRoomId(); - // TODO: We could self-destruct when the last listener goes away, or at least - // stop listening. - this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this)); + constructor() { + // TODO: We could self-destruct when the last listener goes away, or at least stop listening. + this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); } - get activeRoomId(): string { + public get activeRoomId(): string { return this._activeRoomId; } - addListener(roomId, listener) { - if (!this._listeners[roomId]) this._listeners[roomId] = []; - this._listeners[roomId].push(listener); + public addListener(roomId, listener) { + if (!this.listeners[roomId]) this.listeners[roomId] = []; + this.listeners[roomId].push(listener); } - removeListener(roomId, listener) { - if (this._listeners[roomId]) { - const i = this._listeners[roomId].indexOf(listener); + public removeListener(roomId, listener) { + if (this.listeners[roomId]) { + const i = this.listeners[roomId].indexOf(listener); if (i > -1) { - this._listeners[roomId].splice(i, 1); + this.listeners[roomId].splice(i, 1); } } else { console.warn("Unregistering unrecognised listener (roomId=" + roomId + ")"); } } - _emit(roomId, isActive: boolean) { - if (!this._listeners[roomId]) return; + private emit(roomId, isActive: boolean) { + if (!this.listeners[roomId]) return; - for (const l of this._listeners[roomId]) { + for (const l of this.listeners[roomId]) { l.call(null, isActive); } } - _onRoomViewStoreUpdate() { + private onRoomViewStoreUpdate = () => { // emit for the old room ID - if (this._activeRoomId) this._emit(this._activeRoomId, false); + if (this._activeRoomId) this.emit(this._activeRoomId, false); // update our cache this._activeRoomId = RoomViewStore.getRoomId(); // and emit for the new one - if (this._activeRoomId) this._emit(this._activeRoomId, true); - } + if (this._activeRoomId) this.emit(this._activeRoomId, true); + }; } -if (global.mx_ActiveRoomObserver === undefined) { - global.mx_ActiveRoomObserver = new ActiveRoomObserver(); +if (window.mxActiveRoomObserver === undefined) { + window.mxActiveRoomObserver = new ActiveRoomObserver(); } -export default global.mx_ActiveRoomObserver; +export default window.mxActiveRoomObserver; diff --git a/src/AsyncWrapper.js b/src/AsyncWrapper.js index 05054cf63a..94de5df214 100644 --- a/src/AsyncWrapper.js +++ b/src/AsyncWrapper.js @@ -41,7 +41,7 @@ export default createReactClass({ componentDidMount: function() { this._unmounted = false; // XXX: temporary logging to try to diagnose - // https://github.com/vector-im/riot-web/issues/3148 + // https://github.com/vector-im/element-web/issues/3148 console.log('Starting load of AsyncWrapper for modal'); this.props.prom.then((result) => { if (this._unmounted) { diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index acf72a986c..4d06c5df73 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -156,6 +156,14 @@ export default abstract class BasePlatform { loudNotification(ev: Event, room: Object) { } + clearNotification(notif: Notification) { + // Some browsers don't support this, e.g Safari on iOS + // https://developer.mozilla.org/en-US/docs/Web/API/Notification/close + if (notif.close) { + notif.close(); + } + } + /** * Returns a promise that resolves to a string representing the current version of the application. */ diff --git a/src/CallHandler.js b/src/CallHandler.js index d5e058ef1e..18f6aeb98a 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -90,7 +90,7 @@ function play(audioId) { // This is usually because the user hasn't interacted with the document, // or chrome doesn't think so and is denying the request. Not sure what // we can really do here... - // https://github.com/vector-im/riot-web/issues/7657 + // https://github.com/vector-im/element-web/issues/7657 console.log("Unable to play audio clip", e); } }; @@ -474,15 +474,15 @@ const callHandler = { /** * The conference handler is a module that deals with implementation-specific - * multi-party calling implementations. Riot passes in its own which creates + * multi-party calling implementations. Element passes in its own which creates * a one-to-one call with a freeswitch conference bridge. As of July 2018, * the de-facto way of conference calling is a Jitsi widget, so this is * deprecated. It reamins here for two reasons: - * 1. So Riot still supports joining existing freeswitch conference calls + * 1. So Element still supports joining existing freeswitch conference calls * (but doesn't support creating them). After a transition period, we can * remove support for joining them too. * 2. To hide the one-to-one rooms that old-style conferencing creates. This - * is much harder to remove: probably either we make Riot leave & forget these + * is much harder to remove: probably either we make Element leave & forget these * rooms after we remove support for joining freeswitch conferences, or we * accept that random rooms with cryptic users will suddently appear for * anyone who's ever used conference calling, or we are stuck with this diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index a584a69d35..676c41d7d7 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -129,27 +129,21 @@ const onSecretRequested = async function({ console.log(`CrossSigningManager: Ignoring request from untrusted device ${deviceId}`); return; } - if (name.startsWith("m.cross_signing")) { + if ( + name === "m.cross_signing.master" || + name === "m.cross_signing.self_signing" || + name === "m.cross_signing.user_signing" + ) { const callbacks = client.getCrossSigningCacheCallbacks(); if (!callbacks.getCrossSigningKeyCache) return; - /* Explicit enumeration here is deliberate – never share the master key! */ - if (name === "m.cross_signing.self_signing") { - const key = await callbacks.getCrossSigningKeyCache("self_signing"); - if (!key) { - console.log( - `self_signing requested by ${deviceId}, but not found in cache`, - ); - } - return key && encodeBase64(key); - } else if (name === "m.cross_signing.user_signing") { - const key = await callbacks.getCrossSigningKeyCache("user_signing"); - if (!key) { - console.log( - `user_signing requested by ${deviceId}, but not found in cache`, - ); - } - return key && encodeBase64(key); + const keyId = name.replace("m.cross_signing.", ""); + const key = await callbacks.getCrossSigningKeyCache(keyId); + if (!key) { + console.log( + `${keyId} requested by ${deviceId}, but not found in cache`, + ); } + return key && encodeBase64(key); } else if (name === "m.megolm_backup.v1") { const key = await client._crypto.getSessionBackupPrivateKey(); if (!key) { diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 77a9579f2c..5d33645bb7 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -421,7 +421,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts } let formattedBody = typeof content.formatted_body === 'string' ? content.formatted_body : null; - const plainBody = typeof content.body === 'string' ? content.body : null; + const plainBody = typeof content.body === 'string' ? content.body : ""; if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody); strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(plainBody) : plainBody; diff --git a/src/IdentityAuthClient.js b/src/IdentityAuthClient.js index 4a830d6506..fbdb6812ee 100644 --- a/src/IdentityAuthClient.js +++ b/src/IdentityAuthClient.js @@ -177,7 +177,7 @@ export default class IdentityAuthClient { // appropriately. We already clear storage on sign out, but we'll need // additional clearing when changing ISes in settings as part of future // privacy work. - // See also https://github.com/vector-im/riot-web/issues/10455. + // See also https://github.com/vector-im/element-web/issues/10455. } async registerForToken(check=true) { diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 2bebe22f14..d2de31eb80 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -40,7 +40,6 @@ import ToastStore from "./stores/ToastStore"; import {IntegrationManagers} from "./integrations/IntegrationManagers"; import {Mjolnir} from "./mjolnir/Mjolnir"; import DeviceListener from "./DeviceListener"; -import RebrandListener from "./RebrandListener"; import {Jitsi} from "./widgets/Jitsi"; import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; @@ -647,8 +646,6 @@ async function startMatrixClient(startSyncing=true) { // Now that we have a MatrixClientPeg, update the Jitsi info await Jitsi.getInstance().start(); - RebrandListener.sharedInstance().start(); - // dispatch that we finished starting up to wire up any other bits // of the matrix client that cannot be set prior to starting up. dis.dispatch({action: 'client_started'}); @@ -710,7 +707,6 @@ export function stopMatrixClient(unsetClient=true) { IntegrationManagers.sharedInstance().stopWatching(); Mjolnir.sharedInstance().stop(); DeviceListener.sharedInstance().stop(); - RebrandListener.sharedInstance().stop(); if (DMRoomMap.shared()) DMRoomMap.shared().stop(); EventIndexPeg.stop(); const cli = MatrixClientPeg.get(); diff --git a/src/Markdown.js b/src/Markdown.js index d312b7c5bd..e57507b4de 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -99,7 +99,7 @@ export default class Markdown { // puts softbreaks in for multiple lines in a blockquote, // so if these are just newline characters then the // block quote ends up all on one line - // (https://github.com/vector-im/riot-web/issues/3154) + // (https://github.com/vector-im/element-web/issues/3154) softbreak: '
', }); @@ -166,7 +166,7 @@ export default class Markdown { * Render the markdown message to plain text. That is, essentially * just remove any backslashes escaping what would otherwise be * markdown syntax - * (to fix https://github.com/vector-im/riot-web/issues/2870). + * (to fix https://github.com/vector-im/element-web/issues/2870). * * N.B. this does **NOT** render arbitrary MD to plain text - only MD * which has no formatting. Otherwise it emits HTML(!). diff --git a/src/Modal.tsx b/src/Modal.tsx index b744dbacf4..82ed33b794 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -319,7 +319,7 @@ export class ModalManager { private reRender() { if (this.modals.length === 0 && !this.priorityModal && !this.staticModal) { - // If there is no modal to render, make all of Riot available + // If there is no modal to render, make all of Element available // to screen reader users again dis.dispatch({ action: 'aria_unhide_main_app', diff --git a/src/Notifier.js b/src/Notifier.ts similarity index 91% rename from src/Notifier.js rename to src/Notifier.ts index 2ed302267e..473de6c161 100644 --- a/src/Notifier.js +++ b/src/Notifier.ts @@ -17,6 +17,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { Room } from "matrix-js-sdk/src/models/room"; + import { MatrixClientPeg } from './MatrixClientPeg'; import SdkConfig from './SdkConfig'; import PlatformPeg from './PlatformPeg'; @@ -28,9 +31,7 @@ import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore from "./settings/SettingsStore"; -import { - hideToast as hideNotificationsToast, -} from "./toasts/DesktopNotificationsToast"; +import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast"; import {SettingLevel} from "./settings/SettingLevel"; /* @@ -55,7 +56,7 @@ const typehandlers = { }, }; -const Notifier = { +export const Notifier = { notifsByRoom: {}, // A list of event IDs that we've received but need to wait until @@ -63,14 +64,14 @@ const Notifier = { // or not pendingEncryptedEventIds: [], - notificationMessageForEvent: function(ev) { + notificationMessageForEvent: function(ev: MatrixEvent) { if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) { return typehandlers[ev.getContent().msgtype](ev); } return TextForEvent.textForEvent(ev); }, - _displayPopupNotification: function(ev, room) { + _displayPopupNotification: function(ev: MatrixEvent, room: Room) { const plaf = PlatformPeg.get(); if (!plaf) { return; @@ -125,7 +126,7 @@ const Notifier = { } }, - getSoundForRoom: function(roomId) { + getSoundForRoom: function(roomId: string) { // We do no caching here because the SDK caches setting // and the browser will cache the sound. const content = SettingsStore.getValue("notificationSound", roomId); @@ -153,12 +154,13 @@ const Notifier = { }; }, - _playAudioNotification: async function(ev, room) { + _playAudioNotification: async function(ev: MatrixEvent, room: Room) { const sound = this.getSoundForRoom(room.roomId); console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`); try { - const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); + const selector = + document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); let audioElement = selector; if (!selector) { if (!sound) { @@ -207,7 +209,7 @@ const Notifier = { return plaf && plaf.supportsNotifications(); }, - setEnabled: function(enable, callback) { + setEnabled: function(enable: boolean, callback?: () => void) { const plaf = PlatformPeg.get(); if (!plaf) return; @@ -277,10 +279,11 @@ const Notifier = { }, isAudioEnabled: function() { - return this.isEnabled() && SettingsStore.getValue("audioNotificationsEnabled"); + // We don't route Audio via the HTML Notifications API so it is possible regardless of other things + return SettingsStore.getValue("audioNotificationsEnabled"); }, - setToolbarHidden: function(hidden, persistent = true) { + setToolbarHidden: function(hidden: boolean, persistent = true) { this.toolbarHidden = hidden; Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden); @@ -289,7 +292,7 @@ const Notifier = { // update the info to localStorage for persistent settings if (persistent && global.localStorage) { - global.localStorage.setItem("notifications_hidden", hidden); + global.localStorage.setItem("notifications_hidden", String(hidden)); } }, @@ -312,7 +315,7 @@ const Notifier = { return this.toolbarHidden; }, - onSyncStateChange: function(state) { + onSyncStateChange: function(state: string) { if (state === "SYNCING") { this.isSyncing = true; } else if (state === "STOPPED" || state === "ERROR") { @@ -320,7 +323,7 @@ const Notifier = { } }, - onEvent: function(ev) { + onEvent: function(ev: MatrixEvent) { if (!this.isSyncing) return; // don't alert for any messages initially if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return; @@ -338,7 +341,7 @@ const Notifier = { this._evaluateEvent(ev); }, - onEventDecrypted: function(ev) { + onEventDecrypted: function(ev: MatrixEvent) { // 'decrypted' means the decryption process has finished: it may have failed, // in which case it might decrypt soon if the keys arrive if (ev.isDecryptionFailure()) return; @@ -350,7 +353,7 @@ const Notifier = { this._evaluateEvent(ev); }, - onRoomReceipt: function(ev, room) { + onRoomReceipt: function(ev: MatrixEvent, room: Room) { if (room.getUnreadNotificationCount() === 0) { // ideally we would clear each notification when it was read, // but we have no way, given a read receipt, to know whether @@ -383,8 +386,8 @@ const Notifier = { }, }; -if (!global.mxNotifier) { - global.mxNotifier = Notifier; +if (!window.mxNotifier) { + window.mxNotifier = Notifier; } -export default global.mxNotifier; +export default window.mxNotifier; diff --git a/src/RebrandListener.tsx b/src/RebrandListener.tsx deleted file mode 100644 index 47b883cf35..0000000000 --- a/src/RebrandListener.tsx +++ /dev/null @@ -1,184 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import SdkConfig from "./SdkConfig"; -import ToastStore from "./stores/ToastStore"; -import GenericToast from "./components/views/toasts/GenericToast"; -import RebrandDialog from "./components/views/dialogs/RebrandDialog"; -import { RebrandDialogKind } from "./components/views/dialogs/RebrandDialog"; -import Modal from './Modal'; -import { _t } from './languageHandler'; - -const TOAST_KEY = 'rebrand'; -const NAG_INTERVAL = 24 * 60 * 60 * 1000; - -function getRedirectUrl(url): string { - const redirectUrl = new URL(url); - redirectUrl.hash = ''; - - if (SdkConfig.get()['redirectToNewBrandUrl']) { - const newUrl = new URL(SdkConfig.get()['redirectToNewBrandUrl']); - if (url.hostname !== newUrl.hostname || url.pathname !== newUrl.pathname) { - redirectUrl.hostname = newUrl.hostname; - redirectUrl.pathname = newUrl.pathname; - return redirectUrl.toString(); - } - return null; - } else if (url.hostname === 'riot.im') { - if (url.pathname.startsWith('/app')) { - redirectUrl.hostname = 'app.element.io'; - redirectUrl.pathname = '/'; - } else if (url.pathname.startsWith('/staging')) { - redirectUrl.hostname = 'staging.element.io'; - redirectUrl.pathname = '/'; - } else if (url.pathname.startsWith('/develop')) { - redirectUrl.hostname = 'develop.element.io'; - redirectUrl.pathname = '/'; - } - - return redirectUrl.href; - } else if (url.hostname.endsWith('.riot.im')) { - redirectUrl.hostname = url.hostname.substr(0, url.hostname.length - '.riot.im'.length) + '.element.io'; - return redirectUrl.href; - } else { - return null; - } -} - -/** - * Shows toasts informing the user that the name of the app has changed and, - * potentially, that they should head to a different URL and log in there - */ -export default class RebrandListener { - private _reshowTimer?: number; - private nagAgainAt?: number = null; - - static sharedInstance() { - if (!window.mxRebrandListener) window.mxRebrandListener = new RebrandListener(); - return window.mxRebrandListener; - } - - constructor() { - this._reshowTimer = null; - } - - start() { - this.recheck(); - } - - stop() { - if (this._reshowTimer) { - clearTimeout(this._reshowTimer); - this._reshowTimer = null; - } - } - - onNagToastLearnMore = async () => { - const [doneClicked] = await Modal.createDialog(RebrandDialog, { - kind: RebrandDialogKind.NAG, - targetUrl: getRedirectUrl(window.location), - }).finished; - if (doneClicked) { - // open in new tab: they should come back here & log out - window.open(getRedirectUrl(window.location), '_blank'); - } - - // whatever the user clicks, we go away & nag again after however long: - // If they went to the new URL, we want to nag them to log out if they - // come back to this tab, and if they clicked, 'remind me later' we want - // to, well, remind them later. - this.nagAgainAt = Date.now() + NAG_INTERVAL; - this.recheck(); - }; - - onOneTimeToastLearnMore = async () => { - const [doneClicked] = await Modal.createDialog(RebrandDialog, { - kind: RebrandDialogKind.ONE_TIME, - }).finished; - if (doneClicked) { - localStorage.setItem('mx_rename_dialog_dismissed', 'true'); - this.recheck(); - } - }; - - onOneTimeToastDismiss = async () => { - localStorage.setItem('mx_rename_dialog_dismissed', 'true'); - this.recheck(); - }; - - onNagTimerFired = () => { - this._reshowTimer = null; - this.nagAgainAt = null; - this.recheck(); - }; - - private async recheck() { - // There are two types of toast/dialog we show: a 'one time' informing the user that - // the app is now called a different thing but no action is required from them (they - // may need to look for a different name name/icon to launch the app but don't need to - // log in again) and a nag toast where they need to log in to the app on a different domain. - let nagToast = false; - let oneTimeToast = false; - - if (getRedirectUrl(window.location)) { - if (!this.nagAgainAt) { - // if we have redirectUrl, show the nag toast - nagToast = true; - } - } else { - // otherwise we show the 'one time' toast / dialog - const renameDialogDismissed = localStorage.getItem('mx_rename_dialog_dismissed'); - if (renameDialogDismissed !== 'true') { - oneTimeToast = true; - } - } - - if (nagToast || oneTimeToast) { - let description; - let rejectLabel = null; - let onReject = null; - if (nagToast) { - description = _t("Use your account to sign in to the latest version"); - } else { - description = _t("We’re excited to announce Riot is now Element"); - rejectLabel = _t("Dismiss"); - onReject = this.onOneTimeToastDismiss; - } - - ToastStore.sharedInstance().addOrReplaceToast({ - key: TOAST_KEY, - title: _t("Riot is now Element!"), - icon: 'element_logo', - props: { - description, - acceptLabel: _t("Learn More"), - onAccept: nagToast ? this.onNagToastLearnMore : this.onOneTimeToastLearnMore, - rejectLabel, - onReject, - }, - component: GenericToast, - priority: 20, - }); - } else { - ToastStore.sharedInstance().dismissToast(TOAST_KEY); - } - - if (!this._reshowTimer && this.nagAgainAt) { - // XXX: Our build system picks up NodeJS bindings when we need browser bindings. - this._reshowTimer = setTimeout(this.onNagTimerFired, (this.nagAgainAt - Date.now()) + 100) as any as number; - } - } -} diff --git a/src/Registration.js b/src/Registration.js index 32c3d9cc35..9c0264c067 100644 --- a/src/Registration.js +++ b/src/Registration.js @@ -52,7 +52,7 @@ export async function startAnyRegistrationFlow(options) { // caution though. // XXX: ILAG is disabled for now, - // see https://github.com/vector-im/riot-web/issues/8222 + // see https://github.com/vector-im/element-web/issues/8222 // const flows = await _getRegistrationFlows(); // const hasIlagFlow = flows.some((flow) => { diff --git a/src/Resend.js b/src/Resend.js index f5f24bffa5..5638313306 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -45,7 +45,7 @@ export default class Resend { }); }, function(err) { // XXX: temporary logging to try to diagnose - // https://github.com/vector-im/riot-web/issues/3148 + // https://github.com/vector-im/element-web/issues/3148 console.log('Resend got send failure: ' + err.name + '(' + err + ')'); dis.dispatch({ diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index b33aa57359..896e27d92c 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -174,7 +174,7 @@ Request: Response: [ { - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) type: "im.vector.modular.widgets", state_key: "wid1", content: { @@ -193,7 +193,7 @@ Example: room_id: "!foo:bar", response: [ { - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) type: "im.vector.modular.widgets", state_key: "wid1", content: { diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index ad3dc7002a..2063ad3149 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -479,7 +479,7 @@ export const Commands = [ const parsedUrl = new URL(params[0]); const hostname = parsedUrl.host || parsedUrl.hostname; // takes first non-falsey value - // if we're using a Riot permalink handler, this will catch it before we get much further. + // if we're using a Element permalink handler, this will catch it before we get much further. // see below where we make assumptions about parsing the URL. if (isPermalinkHost(hostname)) { isPermalink = true; diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 3607d7a676..c55380bd9b 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -345,7 +345,7 @@ function textForCallHangupEvent(event) { } else if (eventContent.reason === "invite_timeout") { reason = _t('(no answer)'); } else if (eventContent.reason === "user hangup") { - // workaround for https://github.com/vector-im/riot-web/issues/5178 + // workaround for https://github.com/vector-im/element-web/issues/5178 // it seems Android randomly sets a reason of "user hangup" which is // interpreted as an error code :( // https://github.com/vector-im/riot-android/issues/2623 @@ -603,7 +603,7 @@ const stateHandlers = { 'm.room.guest_access': textForGuestAccessEvent, 'm.room.related_groups': textForRelatedGroupsEvent, - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) 'im.vector.modular.widgets': textForWidgetEvent, }; diff --git a/src/Tinter.js b/src/Tinter.js index 24a4d25a00..ca5a460e16 100644 --- a/src/Tinter.js +++ b/src/Tinter.js @@ -327,7 +327,7 @@ class Tinter { // Vector Green as its primary color? // --richvdh - // Yes, tinting assumes that you are using the Riot skin for now. + // Yes, tinting assumes that you are using the Element skin for now. // The right solution will be to move the CSS over to react-sdk. // And yes, the default assets for the base skin might as well use // Vector Green as any other colour. diff --git a/src/Unread.js b/src/Unread.js index ca713b05e4..cf131cac00 100644 --- a/src/Unread.js +++ b/src/Unread.js @@ -52,10 +52,10 @@ export function doesRoomHaveUnreadMessages(room) { // as we don't send RRs for our own messages, make sure we special case that // if *we* sent the last message into the room, we consider it not unread! - // Should fix: https://github.com/vector-im/riot-web/issues/3263 - // https://github.com/vector-im/riot-web/issues/2427 + // Should fix: https://github.com/vector-im/element-web/issues/3263 + // https://github.com/vector-im/element-web/issues/2427 // ...and possibly some of the others at - // https://github.com/vector-im/riot-web/issues/3363 + // https://github.com/vector-im/element-web/issues/3363 if (room.timeline.length && room.timeline[room.timeline.length - 1].sender && room.timeline[room.timeline.length - 1].sender.userId === myUserId) { diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index 180dad876b..c10bc659ae 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -19,13 +19,13 @@ import {createNewMatrixCall as jsCreateNewMatrixCall, Room} from "matrix-js-sdk" import CallHandler from './CallHandler'; import {MatrixClientPeg} from "./MatrixClientPeg"; -// FIXME: this is Riot (Vector) specific code, but will be removed shortly when -// we switch over to jitsi entirely for video conferencing. +// FIXME: this is Element specific code, but will be removed shortly when we +// switch over to Jitsi entirely for video conferencing. -// FIXME: This currently forces Vector to try to hit the matrix.org AS for conferencing. -// This is bad because it prevents people running their own ASes from being used. -// This isn't permanent and will be customisable in the future: see the proposal -// at docs/conferencing.md for more info. +// FIXME: This currently forces Element to try to hit the matrix.org AS for +// conferencing. This is bad because it prevents people running their own ASes +// from being used. This isn't permanent and will be customisable in the future: +// see the proposal at docs/conferencing.md for more info. const USER_PREFIX = "fs_"; const DOMAIN = "matrix.org"; diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index c89a0ceeeb..6aed08c39d 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -76,7 +76,7 @@ export default class WidgetMessaging { console.error(err._error); } // Potential XSS attack if 'msg' is not appropriately sanitized, - // as it is untrusted input by our parent window (which we assume is Riot). + // as it is untrusted input by our parent window (which we assume is Element). // We can't aggressively sanitize [A-z0-9] since it might be a translation. throw new Error(msg); } diff --git a/src/async-components/views/dialogs/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/ExportE2eKeysDialog.js index 7ec9da39de..a92578a547 100644 --- a/src/async-components/views/dialogs/ExportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ExportE2eKeysDialog.js @@ -84,7 +84,7 @@ export default createReactClass({ const blob = new Blob([f], { type: 'text/plain;charset=us-ascii', }); - FileSaver.saveAs(blob, 'riot-keys.txt'); + FileSaver.saveAs(blob, 'element-keys.txt'); this.props.onFinished(true); }).catch((e) => { console.error("Error exporting e2e keys:", e); diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 79fbb98c7b..c3aef9109a 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -286,7 +286,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { changeText = _t("Use a different passphrase?"); } else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) { // only tell them they're wrong if they've actually gone wrong. - // Security concious readers will note that if you left riot-web unattended + // Security concious readers will note that if you left element-web unattended // on this screen, this would make it easy for a malicious person to guess // your passphrase one letter at a time, but they could get this faster by // just opening the browser's developer tools and reading it. diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 4cef817a38..53b3033330 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -480,7 +480,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { // click the button are aware they're making a change to their account. // Once we're confident enough in this (and it's supported enough) we can do // it automatically. - // https://github.com/vector-im/riot-web/issues/11696 + // https://github.com/vector-im/element-web/issues/11696 const Field = sdk.getComponent('views.elements.Field'); let authPrompt; @@ -575,7 +575,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { changeText = _t("Use a different passphrase?"); } else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) { // only tell them they're wrong if they've actually gone wrong. - // Security concious readers will note that if you left riot-web unattended + // Security concious readers will note that if you left element-web unattended // on this screen, this would make it easy for a malicious person to guess // your passphrase one letter at a time, but they could get this faster by // just opening the browser's developer tools and reading it. diff --git a/src/autocomplete/CommunityProvider.tsx b/src/autocomplete/CommunityProvider.tsx index d7eac59f91..f34fee890e 100644 --- a/src/autocomplete/CommunityProvider.tsx +++ b/src/autocomplete/CommunityProvider.tsx @@ -53,7 +53,7 @@ export default class CommunityProvider extends AutocompleteProvider { const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar'); // Disable autocompletions when composing commands because of various issues - // (see https://github.com/vector-im/riot-web/issues/4762) + // (see https://github.com/vector-im/element-web/issues/4762) if (/^(\/join|\/leave)/.test(query)) { return []; } diff --git a/src/autocomplete/RoomProvider.tsx b/src/autocomplete/RoomProvider.tsx index f14fa3bbfa..b18b2d132c 100644 --- a/src/autocomplete/RoomProvider.tsx +++ b/src/autocomplete/RoomProvider.tsx @@ -118,7 +118,7 @@ export default class RoomProvider extends AutocompleteProvider { } getName() { - return '💬 ' + _t('Rooms'); + return _t('Rooms'); } renderCompletions(completions: React.ReactNode[]): React.ReactNode { diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx index eeb6c7a522..c957b5e597 100644 --- a/src/autocomplete/UserProvider.tsx +++ b/src/autocomplete/UserProvider.tsx @@ -137,7 +137,7 @@ export default class UserProvider extends AutocompleteProvider { } getName(): string { - return '👥 ' + _t('Users'); + return _t('Users'); } _makeUsers() { diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index f1bd297730..587ae2cb6b 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -58,7 +58,7 @@ export enum ChevronFace { None = "none", } -interface IProps extends IPosition { +export interface IProps extends IPosition { menuWidth?: number; menuHeight?: number; diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index f8c03be864..d873dd4094 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -210,6 +210,11 @@ const FilePanel = createReactClass({ const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const Loader = sdk.getComponent("elements.Spinner"); + const emptyState = (
+

{_t('No files visible in this room')}

+

{_t('Attach files from chat or just drag and drop them anywhere in a room.')}

+
); + if (this.state.timelineSet) { // console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " + // "(" + this.state.timelineSet._timelines.join(", ") + ")" + " with key " + this.props.roomId); @@ -223,7 +228,7 @@ const FilePanel = createReactClass({ onPaginationRequest={this.onPaginationRequest} tileShape="file_grid" resizeNotifier={this.props.resizeNotifier} - empty={_t('There are no visible files in this room')} + empty={emptyState} /> ); diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 7414a44f11..a42032c9fe 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -46,8 +46,8 @@ const HomePage = () => { const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); return
- {config.brand -

{ _t("Welcome to %(appName)s", { appName: config.brand || "Riot" }) }

+ {config.brand +

{ _t("Welcome to %(appName)s", { appName: config.brand || "Element" }) }

{ _t("Liberate your communication") }

diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index 27f7fbb301..cd5510de9d 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -158,7 +158,7 @@ export default class IndicatorScrollbar extends React.Component { } // don't mess with the horizontal scroll for trackpad users - // See https://github.com/vector-im/riot-web/issues/10005 + // See https://github.com/vector-im/element-web/issues/10005 if (this._likelyTrackpadUser) { return; } diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index 351e3bbad0..fa7860ccef 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -203,7 +203,7 @@ export default createReactClass({ // the UI layer, so we ignore this signal and show a spinner until // there's a new screen to show the user. This is implemented by setting // `busy: false` in `_authStateUpdated`. - // See also https://github.com/vector-im/riot-web/issues/12546 + // See also https://github.com/vector-im/element-web/issues/12546 }, _setFocus: function() { diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 48669a3721..d7f2c73a0b 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -596,7 +596,7 @@ class LoggedInView extends React.Component { const maxRadius = 5; // People shouldn't be straying too far, hopefully // Note: we track how far the user moved their mouse to help - // combat against https://github.com/vector-im/riot-web/issues/7158 + // combat against https://github.com/vector-im/element-web/issues/7158 if (distance < maxRadius) { // This is probably a real click, and not a drag diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a66d4c043f..ce96847d28 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -415,7 +415,7 @@ export default class MatrixChat extends React.PureComponent { return; } this.pageChanging = true; - performance.mark('riot_MatrixChat_page_change_start'); + performance.mark('element_MatrixChat_page_change_start'); } stopPageChangeTimer() { @@ -427,15 +427,15 @@ export default class MatrixChat extends React.PureComponent { return; } this.pageChanging = false; - performance.mark('riot_MatrixChat_page_change_stop'); + performance.mark('element_MatrixChat_page_change_stop'); performance.measure( - 'riot_MatrixChat_page_change_delta', - 'riot_MatrixChat_page_change_start', - 'riot_MatrixChat_page_change_stop', + 'element_MatrixChat_page_change_delta', + 'element_MatrixChat_page_change_start', + 'element_MatrixChat_page_change_stop', ); - performance.clearMarks('riot_MatrixChat_page_change_start'); - performance.clearMarks('riot_MatrixChat_page_change_stop'); - const measurement = performance.getEntriesByName('riot_MatrixChat_page_change_delta').pop(); + performance.clearMarks('element_MatrixChat_page_change_start'); + performance.clearMarks('element_MatrixChat_page_change_stop'); + const measurement = performance.getEntriesByName('element_MatrixChat_page_change_delta').pop(); // In practice, sometimes the entries list is empty, so we get no measurement if (!measurement) return null; @@ -1323,7 +1323,7 @@ export default class MatrixChat extends React.PureComponent { // state (each of which can be 10s of MBs) for each DISJOINT timeline. This is // particularly noticeable when there are lots of 'limited' /sync responses // such as when laptops unsleep. - // https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 + // https://github.com/vector-im/element-web/issues/3307#issuecomment-282895568 cli.setCanResetTimelineCallback((roomId) => { console.log("Request to reset timeline in room ", roomId, " viewing:", this.state.currentRoomId); if (roomId !== this.state.currentRoomId) { @@ -1661,7 +1661,7 @@ export default class MatrixChat extends React.PureComponent { // of the app, we coerce the eventId to be undefined where applicable. if (!eventId) eventId = undefined; - // TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149 + // TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149 // FIXME: sort_out caseConsistency const thirdPartyInvite = { @@ -1935,7 +1935,7 @@ export default class MatrixChat extends React.PureComponent { let fragmentAfterLogin = ""; const initialScreenAfterLogin = this.props.initialScreenAfterLogin; if (initialScreenAfterLogin && - // XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop + // XXX: workaround for https://github.com/vector-im/element-web/issues/11643 causing a login-loop !["welcome", "login", "register", "start_sso", "start_cas"].includes(initialScreenAfterLogin.screen) ) { fragmentAfterLogin = `/${initialScreenAfterLogin.screen}`; diff --git a/src/components/structures/NotificationPanel.js b/src/components/structures/NotificationPanel.js index c1a0ec9c4b..c1f78cffda 100644 --- a/src/components/structures/NotificationPanel.js +++ b/src/components/structures/NotificationPanel.js @@ -36,6 +36,11 @@ const NotificationPanel = createReactClass({ const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const Loader = sdk.getComponent("elements.Spinner"); + const emptyState = (
+

{_t('You’re all caught up')}

+

{_t('You have no visible notifications in this room.')}

+
); + const timelineSet = MatrixClientPeg.get().getNotifTimelineSet(); if (timelineSet) { return ( @@ -46,7 +51,7 @@ const NotificationPanel = createReactClass({ timelineSet={timelineSet} showUrlPreview={false} tileShape="notif" - empty={_t('You have no visible notifications')} + empty={emptyState} />
); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f585a97fde..9a61523941 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -251,7 +251,7 @@ export default createReactClass({ this.context.stopPeeking(); } - // Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307 + // Temporary logging to diagnose https://github.com/vector-im/element-web/issues/4307 console.log( 'RVS update:', newState.roomId, @@ -1078,7 +1078,7 @@ export default createReactClass({ // Do this by indicating our intention to join // XXX: ILAG is disabled for now, - // see https://github.com/vector-im/riot-web/issues/8222 + // see https://github.com/vector-im/element-web/issues/8222 dis.dispatch({action: 'require_registration'}); // dis.dispatch({ // action: 'will_join', diff --git a/src/components/structures/TagPanel.js b/src/components/structures/TagPanel.js index 713ed004b0..4f8a051e62 100644 --- a/src/components/structures/TagPanel.js +++ b/src/components/structures/TagPanel.js @@ -141,7 +141,7 @@ const TagPanel = createReactClass({ = ({iconClassName, label, onClick}) => { - return - - {label} - ; -}; - export default class UserMenu extends React.Component { private dispatcherRef: string; private themeWatcherRef: string; @@ -170,7 +161,7 @@ export default class UserMenu extends React.Component { ev.preventDefault(); ev.stopPropagation(); - // TODO: Archived room view: https://github.com/vector-im/riot-web/issues/14038 + // TODO: Archived room view: https://github.com/vector-im/element-web/issues/14038 // Note: You'll need to uncomment the button too. console.log("TODO: Show archived rooms"); }; @@ -226,7 +217,7 @@ export default class UserMenu extends React.Component { let homeButton = null; if (this.hasHomePage) { homeButton = ( - { ); } - return ( - -
-
-
- - {OwnProfileStore.instance.displayName} - - - {MatrixClientPeg.get().getUserId()} - -
- - {_t("Switch - -
- {hostingLink} -
- {homeButton} - this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)} - /> - this.onSettingsOpen(e, USER_SECURITY_TAB)} - /> - this.onSettingsOpen(e, null)} - /> - {/* */} - -
-
- -
+ return +
+
+ + {OwnProfileStore.instance.displayName} + + + {MatrixClientPeg.get().getUserId()} +
- - ); + + {_t("Switch + +
+ {hostingLink} + + {homeButton} + this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)} + /> + this.onSettingsOpen(e, USER_SECURITY_TAB)} + /> + this.onSettingsOpen(e, null)} + /> + {/* */} + + + + + +
; }; public render() { diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index e65a223bd6..3bc363f863 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -285,7 +285,7 @@ export default createReactClass({ // We'd like to rely on new props coming in via `onServerConfigChange` // so that we know the servers have definitely updated before clearing // the busy state. In the case of a full MXID that resolves to the same - // HS as Riot's default HS though, there may not be any server change. + // HS as Element's default HS though, there may not be any server change. // To avoid this trap, we clear busy here. For cases where the server // actually has changed, `_initLoginLogic` will be called and manages // busy state for its own liveness check. @@ -615,8 +615,8 @@ export default createReactClass({ } // XXX: This link does *not* have a target="_blank" because single sign-on relies on // redirecting the user back to a URI once they're logged in. On the web, this means - // we use the same window and redirect back to riot. On electron, this actually - // opens the SSO page in the electron app itself due to + // we use the same window and redirect back to Element. On Electron, this actually + // opens the SSO page in the Electron app itself due to // https://github.com/electron/electron/issues/8841 and so happens to work. // If this bug gets fixed, it will break SSO since it will open the SSO page in the // user's browser, let them log into their SSO provider, then redirect their browser diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 3b5f5676dc..13e48f6287 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -262,7 +262,7 @@ export default createReactClass({ // the user off to the login page to figure their account out. try { const loginLogic = new Login(hsUrl, isUrl, null, { - defaultDeviceDisplayName: "riot login check", // We shouldn't ever be used + defaultDeviceDisplayName: "Element login check", // We shouldn't ever be used }); const flows = await loginLogic.getFlows(); const hasSsoFlow = flows.find(f => f.type === 'm.login.sso' || f.type === 'm.login.cas'); diff --git a/src/components/structures/auth/SetupEncryptionBody.js b/src/components/structures/auth/SetupEncryptionBody.js index 5b1f025dfb..6d090936e5 100644 --- a/src/components/structures/auth/SetupEncryptionBody.js +++ b/src/components/structures/auth/SetupEncryptionBody.js @@ -152,7 +152,7 @@ export default class SetupEncryptionBody extends React.Component {
{_t("%(brand)s iOS", { brand })}
-
{_t("%(brand)s X for Android", { brand })}
+
{_t("%(brand)s Android", { brand })}

{_t("or another cross-signing capable Matrix client")}

diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index f6bc1b8ae7..7080eb3602 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -109,7 +109,7 @@ export const PasswordAuthEntry = createReactClass({ this.props.submitAuthDict({ type: PasswordAuthEntry.LOGIN_TYPE, // TODO: Remove `user` once servers support proper UIA - // See https://github.com/vector-im/riot-web/issues/10312 + // See https://github.com/vector-im/element-web/issues/10312 user: this.props.matrixClient.credentials.userId, identifier: { type: "m.id.user", @@ -538,7 +538,7 @@ export const MsisdnAuthEntry = createReactClass({ this.props.submitAuthDict({ type: MsisdnAuthEntry.LOGIN_TYPE, // TODO: Remove `threepid_creds` once servers support proper UIA - // See https://github.com/vector-im/riot-web/issues/10312 + // See https://github.com/vector-im/element-web/issues/10312 // See https://github.com/matrix-org/matrix-doc/issues/2220 threepid_creds: creds, threepidCreds: creds, diff --git a/src/components/views/context_menus/IconizedContextMenu.tsx b/src/components/views/context_menus/IconizedContextMenu.tsx new file mode 100644 index 0000000000..b3ca9fde6f --- /dev/null +++ b/src/components/views/context_menus/IconizedContextMenu.tsx @@ -0,0 +1,124 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import classNames from "classnames"; + +import { + ChevronFace, + ContextMenu, + IProps as IContextMenuProps, + MenuItem, + MenuItemCheckbox, MenuItemRadio, +} from "../../structures/ContextMenu"; + +interface IProps extends IContextMenuProps { + className?: string; + compact?: boolean; +} + +interface IOptionListProps { + first?: boolean; + red?: boolean; + className?: string; +} + +interface IOptionProps extends React.ComponentProps { + iconClassName: string; +} + +interface ICheckboxProps extends React.ComponentProps { + iconClassName: string; +} + +interface IRadioProps extends React.ComponentProps { + iconClassName: string; +} + +export const IconizedContextMenuRadio: React.FC = ({ + label, + iconClassName, + active, + className, + ...props +}) => { + return + + {label} + {active && } + ; +}; + +export const IconizedContextMenuCheckbox: React.FC = ({ + label, + iconClassName, + active, + className, + ...props +}) => { + return + + {label} + {active && } + ; +}; + +export const IconizedContextMenuOption: React.FC = ({label, iconClassName, ...props}) => { + return + + {label} + ; +}; + +export const IconizedContextMenuOptionList: React.FC = ({first, red, className, children}) => { + const classes = classNames("mx_IconizedContextMenu_optionList", className, { + mx_IconizedContextMenu_optionList_notFirst: !first, + mx_IconizedContextMenu_optionList_red: red, + }); + + return
+ {children} +
; +}; + +const IconizedContextMenu: React.FC = ({className, children, compact, ...props}) => { + const classes = classNames("mx_IconizedContextMenu", className, { + mx_IconizedContextMenu_compact: compact, + }); + + return +
+ { children } +
+
; +}; + +export default IconizedContextMenu; + diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js index 53dfb5d62e..ffef88e8be 100644 --- a/src/components/views/dialogs/BugReportDialog.js +++ b/src/components/views/dialogs/BugReportDialog.js @@ -193,7 +193,7 @@ export default class BugReportDialog extends React.Component { { a: (sub) => { sub } , @@ -211,7 +211,7 @@ export default class BugReportDialog extends React.Component { label={_t("GitHub issue")} onChange={this._onIssueUrlChange} value={this.state.issueUrl} - placeholder="https://github.com/vector-im/riot-web/issues/..." + placeholder="https://github.com/vector-im/element-web/issues/..." /> u.userId !== myUserId); diff --git a/src/components/views/dialogs/RebrandDialog.tsx b/src/components/views/dialogs/RebrandDialog.tsx deleted file mode 100644 index 79b4b69a4a..0000000000 --- a/src/components/views/dialogs/RebrandDialog.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as React from 'react'; -import * as PropTypes from 'prop-types'; -import BaseDialog from './BaseDialog'; -import { _t } from '../../../languageHandler'; -import DialogButtons from '../elements/DialogButtons'; - -export enum RebrandDialogKind { - NAG, - ONE_TIME, -} - -interface IProps { - onFinished: (bool) => void; - kind: RebrandDialogKind; - targetUrl?: string; -} - -export default class RebrandDialog extends React.PureComponent { - private onDoneClick = () => { - this.props.onFinished(true); - }; - - private onGoToElementClick = () => { - this.props.onFinished(true); - }; - - private onRemindMeLaterClick = () => { - this.props.onFinished(false); - }; - - private getPrettyTargetUrl() { - const u = new URL(this.props.targetUrl); - let ret = u.host; - if (u.pathname !== '/') ret += u.pathname; - return ret; - } - - getBodyText() { - if (this.props.kind === RebrandDialogKind.NAG) { - return _t( - "Use your account to sign in to the latest version of the app at ", {}, - { - a: sub => {this.getPrettyTargetUrl()}, - }, - ); - } else { - return _t( - "You’re already signed in and good to go here, but you can also grab the latest " + - "versions of the app on all platforms at element.io/get-started.", {}, - { - a: sub => {sub}, - }, - ); - } - } - - getDialogButtons() { - if (this.props.kind === RebrandDialogKind.NAG) { - return ; - } else { - return ; - } - } - - render() { - return -
{this.getBodyText()}
-
- Riot Logo - - Element Logo -
-
- {_t( - "Learn more at element.io/previously-riot", {}, { - a: sub => {sub}, - } - )} -
- {this.getDialogButtons()} -
; - } -} diff --git a/src/components/views/dialogs/RedesignFeedbackDialog.js b/src/components/views/dialogs/RedesignFeedbackDialog.js index 8f345bdd92..51b71ac6cb 100644 --- a/src/components/views/dialogs/RedesignFeedbackDialog.js +++ b/src/components/views/dialogs/RedesignFeedbackDialog.js @@ -19,9 +19,9 @@ import QuestionDialog from './QuestionDialog'; import { _t } from '../../../languageHandler'; export default (props) => { - const existingIssuesUrl = "https://github.com/vector-im/riot-web/issues" + + const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" + "?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc"; - const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; + const newIssueUrl = "https://github.com/vector-im/element-web/issues/new"; const description1 = _t("If you run into any bugs or have feedback you'd like to share, " + diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx index 2e1529cbf1..dc2a987f13 100644 --- a/src/components/views/dialogs/ShareDialog.tsx +++ b/src/components/views/dialogs/ShareDialog.tsx @@ -30,6 +30,7 @@ import * as ContextMenu from "../../structures/ContextMenu"; import {toRightOf} from "../../structures/ContextMenu"; import {copyPlaintext, selectText} from "../../../utils/strings"; import StyledCheckbox from '../elements/StyledCheckbox'; +import AccessibleTooltipButton from '../elements/AccessibleTooltipButton'; const socials = [ { @@ -210,10 +211,11 @@ export default class ShareDialog extends React.PureComponent { > { matrixToUrl } - - { _t('COPY') } -
 
-
+ { checkbox }
diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index d0fc56743f..75946f19c1 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -314,13 +314,13 @@ export default class AppTile extends React.Component { if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) { IntegrationManagers.sharedInstance().openAll( this.props.room, - 'type_' + this.props.type, + 'type_' + this.props.app.type, this.props.app.id, ); } else { IntegrationManagers.sharedInstance().getPrimaryManager().open( this.props.room, - 'type_' + this.props.type, + 'type_' + this.props.app.type, this.props.app.id, ); } @@ -361,14 +361,14 @@ export default class AppTile extends React.Component { return terminationPromise.finally(() => { // HACK: This is a really dirty way to ensure that Jitsi cleans up // its hold on the webcam. Without this, the widget holds a media - // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 + // stream open, even after death. See https://github.com/vector-im/element-web/issues/7351 if (this._appFrame.current) { // In practice we could just do `+= ''` to trick the browser // into thinking the URL changed, however I can foresee this // being optimized out by a browser. Instead, we'll just point // the iframe at a page that is reasonably safe to use in the // event the iframe doesn't wink away. - // This is relative to where the Riot instance is located. + // This is relative to where the Element instance is located. this._appFrame.current.src = 'about:blank'; } @@ -727,7 +727,7 @@ export default class AppTile extends React.Component { // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin // because that would allow the iframe to programmatically remove the sandbox attribute, but - // this would only be for content hosted on the same origin as the riot client: anything + // this would only be for content hosted on the same origin as the element client: anything // hosted on the same origin as the client will get the same access as if you clicked // a link to it. const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+ @@ -924,7 +924,7 @@ AppTile.propTypes = { // Optionally show the reload widget icon // This is not currently intended for use with production widgets. However // it can be useful when developing persistent widgets in order to avoid - // having to reload all of riot to get new widget content. + // having to reload all of Element to get new widget content. showReload: PropTypes.bool, // Widget capabilities to allow by default (without user confirmation) // NOTE -- Use with caution. This is intended to aid better integration / UX diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index a043b350ab..68bec667d8 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -72,7 +72,7 @@ export default class ErrorBoundary extends React.PureComponent { render() { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; + const newIssueUrl = "https://github.com/vector-im/element-web/issues/new"; return

{_t("Something went wrong!")}

diff --git a/src/components/views/elements/StyledRadioGroup.tsx b/src/components/views/elements/StyledRadioGroup.tsx index ea8f65d12b..6b9e992f92 100644 --- a/src/components/views/elements/StyledRadioGroup.tsx +++ b/src/components/views/elements/StyledRadioGroup.tsx @@ -25,6 +25,7 @@ interface IDefinition { disabled?: boolean; label: React.ReactChild; description?: React.ReactChild; + checked?: boolean; // If provided it will override the value comparison done in the group } interface IProps { @@ -33,7 +34,7 @@ interface IProps { definitions: IDefinition[]; value?: T; // if not provided no options will be selected outlined?: boolean; - onChange(newValue: T); + onChange(newValue: T): void; } function StyledRadioGroup({name, definitions, value, className, outlined, onChange}: IProps) { @@ -46,7 +47,7 @@ function StyledRadioGroup({name, definitions, value, className { avatarElement }
diff --git a/src/components/views/right_panel/HeaderButton.tsx b/src/components/views/right_panel/HeaderButton.tsx index c7cd064184..ff092ca060 100644 --- a/src/components/views/right_panel/HeaderButton.tsx +++ b/src/components/views/right_panel/HeaderButton.tsx @@ -39,6 +39,8 @@ interface IProps { title: string; } +// TODO: replace this, the composer buttons and the right panel buttons with a unified +// representation export default class HeaderButton extends React.Component { constructor(props: IProps) { super(props); diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 20168faede..b52792b3d1 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -550,7 +550,9 @@ const RedactMessagesButton = ({member}) => { let eventsToRedact = []; while (timeline) { eventsToRedact = timeline.getEvents().reduce((events, event) => { - if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) { + if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction() && + event.getType() !== "m.room.create" + ) { return events.concat(event); } else { return events; diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 9cb7f54dd8..1f0703839f 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -208,7 +208,7 @@ export default class VerificationPanel extends React.PureComponent { const alias = this.state.localAliases[index]; // TODO: In future, we should probably be making sure that the alias actually belongs - // to this room. See https://github.com/vector-im/riot-web/issues/7353 + // to this room. See https://github.com/vector-im/element-web/issues/7353 MatrixClientPeg.get().deleteAlias(alias).then(() => { const localAliases = this.state.localAliases.filter(a => a !== alias); this.setState({localAliases}); diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 4e8b6ba42f..2179bd905e 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -39,7 +39,7 @@ const ROOM_COLORS = [ // Dev note: this component is not attached anywhere, but is left here as it // has a high possibility of being used in the nearish future. -// Ref: https://github.com/vector-im/riot-web/issues/8421 +// Ref: https://github.com/vector-im/element-web/issues/8421 export default createReactClass({ displayName: 'ColorSettings', diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 2a909bdd26..58dd82341e 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -242,7 +242,7 @@ export default class BasicMessageEditor extends React.Component // so trigger a model update after the composition is done by calling the input handler. // however, modifying the DOM (caused by the editor model update) from the compositionend handler seems - // to confuse the IME in Chrome, likely causing https://github.com/vector-im/riot-web/issues/10913 , + // to confuse the IME in Chrome, likely causing https://github.com/vector-im/element-web/issues/10913 , // so we do it async // however, doing this async seems to break things in Safari for some reason, so browser sniff. @@ -273,7 +273,7 @@ export default class BasicMessageEditor extends React.Component const {model} = this.props; const range = getRangeForSelection(this.editorRef.current, model, selection); const selectedParts = range.parts.map(p => p.serialize()); - event.clipboardData.setData("application/x-riot-composer", JSON.stringify(selectedParts)); + event.clipboardData.setData("application/x-element-composer", JSON.stringify(selectedParts)); event.clipboardData.setData("text/plain", text); // so plain copy/paste works if (type === "cut") { // Remove the text, updating the model as appropriate @@ -301,7 +301,7 @@ export default class BasicMessageEditor extends React.Component const {model} = this.props; const {partCreator} = model; - const partsText = event.clipboardData.getData("application/x-riot-composer"); + const partsText = event.clipboardData.getData("application/x-element-composer"); let parts; if (partsText) { const serializedTextParts = JSON.parse(partsText); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 13554eb3d6..2f00447287 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -60,7 +60,7 @@ const stateEventTileTypes = { 'm.room.power_levels': 'messages.TextualEvent', 'm.room.pinned_events': 'messages.TextualEvent', 'm.room.server_acl': 'messages.TextualEvent', - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) 'im.vector.modular.widgets': 'messages.TextualEvent', 'm.room.tombstone': 'messages.TextualEvent', 'm.room.join_rules': 'messages.TextualEvent', @@ -519,7 +519,7 @@ export default createReactClass({ onPermalinkClicked: function(e) { // This allows the permalink to be opened in a new tab/window or copied as - // matrix.to, but also for it to enable routing within Riot when clicked. + // matrix.to, but also for it to enable routing within Element when clicked. e.preventDefault(); dis.dispatch({ action: 'view_room', @@ -595,11 +595,11 @@ export default createReactClass({ } const eventId = this.props.mxEvent.getId(); if (!eventId) { - // XXX: Temporary diagnostic logging for https://github.com/vector-im/riot-web/issues/11120 + // XXX: Temporary diagnostic logging for https://github.com/vector-im/element-web/issues/11120 console.error("EventTile attempted to get relations for an event without an ID"); // Use event's special `toJSON` method to log key data. console.log(JSON.stringify(this.props.mxEvent, null, 4)); - console.trace("Stacktrace for https://github.com/vector-im/riot-web/issues/11120"); + console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120"); } return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction"); }, diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index bf4700ed97..c4f400c872 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -15,6 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import React, {createRef} from 'react'; +import classNames from 'classnames'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import CallHandler from '../../../CallHandler'; @@ -29,7 +30,6 @@ import E2EIcon from './E2EIcon'; import SettingsStore from "../../../settings/SettingsStore"; import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; - function ComposerAvatar(props) { const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); return
@@ -117,9 +117,19 @@ const EmojiButton = ({addEmoji}) => { ; } + const className = classNames( + "mx_MessageComposer_button", + "mx_MessageComposer_emoji", + { + "mx_MessageComposer_button_highlight": menuDisplayed, + }, + ); + + // TODO: replace ContextMenuTooltipButton with a unified representation of + // the header buttons and the right panel buttons return
- { '💬 ' + _t('Replying') } + { _t('Replying') }
{ private updateLists = () => { const newLists = RoomListStore.instance.orderedLists; if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log("new lists", newLists); } @@ -245,6 +245,7 @@ export default class RoomList extends React.PureComponent { if (doUpdate) { // We have to break our reference to the room list store if we want to be able to // diff the object for changes, so do that. + // @ts-ignore - ITagMap is ts-ignored so this will have to be too const newSublists = objectWithOnly(newLists, newListIds); const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v)); @@ -256,7 +257,7 @@ export default class RoomList extends React.PureComponent { private renderCommunityInvites(): TemporaryTile[] { // TODO: Put community invites in a more sensible place (not in the room list) - // See https://github.com/vector-im/riot-web/issues/14456 + // See https://github.com/vector-im/element-web/issues/14456 return MatrixClientPeg.get().getGroups().filter(g => { return g.myMembership === 'invite'; }).map(g => { diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 9aa78fbbfd..d52bbbb0d0 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -511,7 +511,7 @@ export default createReactClass({ "If you think you're seeing this message in error, please " + "submit a bug report.", { errcode: this.props.error.errcode }, - { issueLink: label => { label } }, ), ]; diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index f6d0d1c22e..b2337c8c22 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -74,7 +74,7 @@ interface IProps { // You should feel bad if you use this. extraBadTilesThatShouldntExist?: TemporaryTile[]; - // TODO: Account for https://github.com/vector-im/riot-web/issues/14179 + // TODO: Account for https://github.com/vector-im/element-web/issues/14179 } // TODO: Use re-resizer's NumberSize when it is exposed as the type @@ -703,7 +703,7 @@ export default class RoomSublist extends React.Component { private onScrollPrevent(e: React.UIEvent) { // the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable - // this fixes https://github.com/vector-im/riot-web/issues/14413 + // this fixes https://github.com/vector-im/element-web/issues/14413 (e.target as HTMLDivElement).scrollTop = 0; } @@ -738,14 +738,20 @@ export default class RoomSublist extends React.Component { const nonPaddedHeight = this.state.height - RESIZE_HANDLE_HEIGHT - SHOW_N_BUTTON_HEIGHT; const amountFullyShown = Math.floor(nonPaddedHeight / this.layout.tileHeight); const numMissing = this.numTiles - amountFullyShown; + const label = _t("Show %(count)s more", {count: numMissing}); let showMoreText = ( - {_t("Show %(count)s more", {count: numMissing})} + {label} ); if (this.props.isMinimized) showMoreText = null; showNButton = ( - + {/* set by CSS masking */} @@ -754,14 +760,20 @@ export default class RoomSublist extends React.Component { ); } else if (this.numTiles > this.layout.defaultVisibleTiles) { // we have all tiles visible - add a button to show less + const label = _t("Show less"); let showLessText = ( - {_t("Show less")} + {label} ); if (this.props.isMinimized) showLessText = null; showNButton = ( - + {/* set by CSS masking */} diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 02aa915fa5..09f201e3ef 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -27,14 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; import ActiveRoomObserver from "../../../ActiveRoomObserver"; import { _t } from "../../../languageHandler"; -import { - ChevronFace, - ContextMenu, - ContextMenuTooltipButton, - MenuItem, - MenuItemCheckbox, - MenuItemRadio, -} from "../../structures/ContextMenu"; +import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; @@ -51,6 +44,11 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { EchoChamber } from "../../../stores/local-echo/EchoChamber"; import { CachedRoomKey, RoomEchoChamber } from "../../../stores/local-echo/RoomEchoChamber"; import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber"; +import IconizedContextMenu, { + IconizedContextMenuCheckbox, + IconizedContextMenuOption, + IconizedContextMenuOptionList, IconizedContextMenuRadio +} from "../context_menus/IconizedContextMenu"; interface IProps { room: Room; @@ -78,32 +76,6 @@ const contextMenuBelow = (elementRect: PartialDOMRect) => { return {left, top, chevronFace}; }; -interface INotifOptionProps { - active: boolean; - iconClassName: string; - label: string; - onClick(ev: ButtonEvent); -} - -const NotifOption: React.FC = ({active, onClick, iconClassName, label}) => { - const classes = classNames({ - mx_RoomTile_contextMenu_activeRow: active, - }); - - let activeIcon; - if (active) { - activeIcon = ; - } - - return ( - - - { label } - { activeIcon } - - ); -}; - export default class RoomTile extends React.PureComponent { private dispatcherRef: string; private roomTileRef = createRef(); @@ -148,6 +120,12 @@ export default class RoomTile extends React.PureComponent { return !this.props.isMinimized && this.props.showMessagePreview; } + public componentDidUpdate(prevProps: Readonly, prevState: Readonly) { + if (prevProps.showMessagePreview !== this.props.showMessagePreview && this.showMessagePreview) { + this.setState({messagePreview: this.generatePreview()}); + } + } + public componentDidMount() { // when we're first rendered (or our sublist is expanded) make sure we are visible if we're active if (this.state.selected) { @@ -335,38 +313,39 @@ export default class RoomTile extends React.PureComponent { let contextMenu = null; if (this.state.notificationsMenuPosition) { - contextMenu = ( - -
-
- - - - -
-
-
- ); + contextMenu = + + + + + + + ; } const classes = classNames("mx_RoomTile_notificationsButton", { @@ -400,18 +379,20 @@ export default class RoomTile extends React.PureComponent { let contextMenu = null; if (this.state.generalMenuPosition && this.props.tag === DefaultTagID.Archived) { - contextMenu = ( - -
-
- - - {_t("Forget Room")} - -
-
-
- ); + contextMenu = + + + + ; } else if (this.state.generalMenuPosition) { const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room); @@ -421,42 +402,40 @@ export default class RoomTile extends React.PureComponent { const isLowPriority = roomTags.includes(DefaultTagID.LowPriority); const lowPriorityLabel = _t("Low Priority"); - contextMenu = ( - -
-
- this.onTagRoom(e, DefaultTagID.Favourite)} - active={isFavorite} - label={favouriteLabel} - > - - {favouriteLabel} - - this.onTagRoom(e, DefaultTagID.LowPriority)} - active={isLowPriority} - label={lowPriorityLabel} - > - - {lowPriorityLabel} - - - - {_t("Settings")} - -
-
- - - {_t("Leave Room")} - -
-
-
- ); + contextMenu = + + this.onTagRoom(e, DefaultTagID.Favourite)} + active={isFavorite} + label={favouriteLabel} + iconClassName="mx_RoomTile_iconStar" + /> + this.onTagRoom(e, DefaultTagID.LowPriority)} + active={isLowPriority} + label={lowPriorityLabel} + iconClassName="mx_RoomTile_iconArrowDown" + /> + + + + + + + ; } return ( diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index b73f03a9c5..b48790a9cf 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import React from 'react'; +import classNames from 'classnames'; import {_t, _td} from '../../../languageHandler'; import AppTile from '../elements/AppTile'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; @@ -380,14 +381,21 @@ export default class Stickerpicker extends React.Component { render() { let stickerPicker; let stickersButton; + const className = classNames( + "mx_MessageComposer_button", + "mx_MessageComposer_stickers", + "mx_Stickers_hideStickers", + "mx_MessageComposer_button_highlight", + ); if (this.state.showStickers) { // Show hide-stickers button stickersButton = ; diff --git a/src/components/views/rooms/TemporaryTile.tsx b/src/components/views/rooms/TemporaryTile.tsx index fefcb654ba..a61ff6e1da 100644 --- a/src/components/views/rooms/TemporaryTile.tsx +++ b/src/components/views/rooms/TemporaryTile.tsx @@ -38,7 +38,7 @@ interface IState { hover: boolean; } -// TODO: Remove with community invites in the room list: https://github.com/vector-im/riot-web/issues/14456 +// TODO: Remove with community invites in the room list: https://github.com/vector-im/element-web/issues/14456 export default class TemporaryTile extends React.Component { constructor(props: IProps) { super(props); diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index a4ff65d8f9..47fcae5c6f 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -119,8 +119,8 @@ export default createReactClass({ 'In future this will be improved.', ) } {' '} - - https://github.com/vector-im/riot-web/issues/2671 + + https://github.com/vector-im/element-web/issues/2671
, button: _t("Continue"), diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index aa512d4365..1c6baee9af 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -32,6 +32,7 @@ export default class CrossSigningPanel extends React.PureComponent { error: null, crossSigningPublicKeysOnDevice: false, crossSigningPrivateKeysInStorage: false, + masterPrivateKeyCached: false, selfSigningPrivateKeyCached: false, userSigningPrivateKeyCached: false, sessionBackupKeyCached: false, @@ -78,6 +79,7 @@ export default class CrossSigningPanel extends React.PureComponent { const secretStorage = cli._crypto._secretStorage; const crossSigningPublicKeysOnDevice = crossSigning.getId(); const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage); + const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master")); const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")); const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")); const sessionBackupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey(); @@ -91,6 +93,7 @@ export default class CrossSigningPanel extends React.PureComponent { this.setState({ crossSigningPublicKeysOnDevice, crossSigningPrivateKeysInStorage, + masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, sessionBackupKeyCached, @@ -140,6 +143,7 @@ export default class CrossSigningPanel extends React.PureComponent { error, crossSigningPublicKeysOnDevice, crossSigningPrivateKeysInStorage, + masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, sessionBackupKeyCached, @@ -235,6 +239,10 @@ export default class CrossSigningPanel extends React.PureComponent { {_t("Cross-signing private keys:")} {crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")} + + {_t("Master private key:")} + {masterPrivateKeyCached ? _t("cached locally") : _t("not found locally")} + {_t("Self signing private key:")} {selfSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")} diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 35c751d91e..8598a2a966 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -160,7 +160,7 @@ export default class EventIndexPanel extends React.Component { ); } else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) { const nativeLink = ( - "https://github.com/vector-im/riot-web/blob/develop/" + + "https://github.com/vector-im/element-web/blob/develop/" + "docs/native-node-modules.md#" + "adding-seshat-for-search-in-e2e-encrypted-rooms" ); @@ -194,7 +194,7 @@ export default class EventIndexPanel extends React.Component { brand, }, { - 'desktopLink': (sub) => {sub}, }, ) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx similarity index 78% rename from src/components/views/settings/tabs/room/BridgeSettingsTab.js rename to src/components/views/settings/tabs/room/BridgeSettingsTab.tsx index a5d20eae33..8638105cd9 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,8 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; +import {Room} from "matrix-js-sdk/src/models/room"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; + import {_t} from "../../../../../languageHandler"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import BridgeTile from "../../BridgeTile"; @@ -27,28 +29,26 @@ const BRIDGE_EVENT_TYPES = [ const BRIDGES_LINK = "https://matrix.org/bridges/"; -export default class BridgeSettingsTab extends React.Component { - static propTypes = { - roomId: PropTypes.string.isRequired, - }; +interface IProps { + roomId: string; +} - _renderBridgeCard(event, room) { +export default class BridgeSettingsTab extends React.Component { + private renderBridgeCard(event: MatrixEvent, room: Room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { return null; } - return ; + return ; } - static getBridgeStateEvents(roomId) { + static getBridgeStateEvents(roomId: string) { const client = MatrixClientPeg.get(); - const roomState = (client.getRoom(roomId)).currentState; + const roomState = client.getRoom(roomId).currentState; - const bridgeEvents = [].concat(...BRIDGE_EVENT_TYPES.map((typeName) => - Object.values(roomState.events[typeName] || {}), + return [].concat(...BRIDGE_EVENT_TYPES.map((typeName) => + Array.from(roomState.events.get(typeName).values()), )); - - return bridgeEvents; } render() { @@ -58,8 +58,7 @@ export default class BridgeSettingsTab extends React.Component { const client = MatrixClientPeg.get(); const room = client.getRoom(this.props.roomId); - let content = null; - + let content: JSX.Element; if (bridgeEvents.length > 0) { content =

{_t( @@ -72,7 +71,7 @@ export default class BridgeSettingsTab extends React.Component { }, )}

    - { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } + { bridgeEvents.map((event) => this.renderBridgeCard(event, room)) }
; } else { diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js index b812bb6b68..99352a452e 100644 --- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js @@ -33,7 +33,7 @@ const plEventsToLabels = { "m.room.tombstone": _td("Upgrade the room"), "m.room.encryption": _td("Enable room encryption"), - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) "im.vector.modular.widgets": _td("Modify widgets"), }; @@ -48,7 +48,7 @@ const plEventsToShow = { "m.room.tombstone": {isState: true}, "m.room.encryption": {isState: true}, - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) "im.vector.modular.widgets": {isState: true}, }; diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js index 596344d7cd..48115146f1 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js @@ -22,6 +22,7 @@ import * as sdk from "../../../../.."; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import Modal from "../../../../../Modal"; import QuestionDialog from "../../../dialogs/QuestionDialog"; +import StyledRadioGroup from '../../../elements/StyledRadioGroup'; import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class SecurityRoomSettingsTab extends React.Component { @@ -99,7 +100,7 @@ export default class SecurityRoomSettingsTab extends React.Component { { 'a': (sub) => { return {sub}; + href='https://element.io/help#encryption'>{sub}; }, }, ), @@ -144,7 +145,7 @@ export default class SecurityRoomSettingsTab extends React.Component { }); }; - _onRoomAccessRadioToggle = (ev) => { + _onRoomAccessRadioToggle = (roomAccess) => { // join_rule // INVITE | PUBLIC // ----------------------+---------------- @@ -161,7 +162,7 @@ export default class SecurityRoomSettingsTab extends React.Component { let joinRule = "invite"; let guestAccess = "can_join"; - switch (ev.target.value) { + switch (roomAccess) { case "invite_only": // no change - use defaults above break; @@ -190,11 +191,11 @@ export default class SecurityRoomSettingsTab extends React.Component { }); }; - _onHistoryRadioToggle = (ev) => { + _onHistoryRadioToggle = (history) => { const beforeHistory = this.state.history; - this.setState({history: ev.target.value}); + this.setState({history: history}); MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", { - history_visibility: ev.target.value, + history_visibility: history, }, "").catch((e) => { console.error(e); this.setState({history: beforeHistory}); @@ -257,27 +258,31 @@ export default class SecurityRoomSettingsTab extends React.Component {
{guestWarning} {aliasWarning} - - - +
); } @@ -294,34 +299,33 @@ export default class SecurityRoomSettingsTab extends React.Component { {_t('Changes to who can read history will only apply to future messages in this room. ' + 'The visibility of existing history will be unchanged.')}
- - - - +
); } diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js index 25dfd9e100..ef00b7136e 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js @@ -55,7 +55,7 @@ export default class LabsUserSettingsTab extends React.Component { _t('Customise your experience with experimental labs features. ' + 'Learn more.', {}, { 'a': (sub) => { - return {sub}; }, }) diff --git a/src/editor/caret.ts b/src/editor/caret.ts index 4864f9aa95..cb4978caf4 100644 --- a/src/editor/caret.ts +++ b/src/editor/caret.ts @@ -59,7 +59,7 @@ export function setCaretPosition(editor: HTMLDivElement, model: EditorModel, car // If the selection matches, it's important to leave it alone. // Recreating the selection state in at least Chrome can cause // strange side effects, like touch bar flickering on every key. - // See https://github.com/vector-im/riot-web/issues/9299 + // See https://github.com/vector-im/element-web/issues/9299 return; } } diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index 28543137be..ec697b193c 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -158,7 +158,7 @@ function checkDescendInto(node) { function checkIgnored(n) { if (n.nodeType === Node.TEXT_NODE) { - // riot adds \n text nodes in a lot of places, + // Element adds \n text nodes in a lot of places, // which should be ignored return n.nodeValue === "\n"; } else if (n.nodeType === Node.ELEMENT_NODE) { diff --git a/src/hooks/useAsyncMemo.js b/src/hooks/useAsyncMemo.ts similarity index 81% rename from src/hooks/useAsyncMemo.js rename to src/hooks/useAsyncMemo.ts index ef7d256b04..11c7aca7f1 100644 --- a/src/hooks/useAsyncMemo.js +++ b/src/hooks/useAsyncMemo.ts @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useState, useEffect } from 'react'; +import {useState, useEffect, DependencyList} from 'react'; -export const useAsyncMemo = (fn, deps, initialValue) => { +type Fn = () => Promise; + +export const useAsyncMemo = (fn: Fn, deps: DependencyList, initialValue?: T) => { const [value, setValue] = useState(initialValue); useEffect(() => { fn().then(setValue); diff --git a/src/hooks/useEventEmitter.js b/src/hooks/useEventEmitter.ts similarity index 86% rename from src/hooks/useEventEmitter.js rename to src/hooks/useEventEmitter.ts index 6a758fb108..6b5693ef95 100644 --- a/src/hooks/useEventEmitter.js +++ b/src/hooks/useEventEmitter.ts @@ -15,11 +15,14 @@ limitations under the License. */ import {useRef, useEffect} from "react"; +import type {EventEmitter} from "events"; + +type Handler = (...args: any[]) => void; // Hook to wrap event emitter on and removeListener in hook lifecycle -export const useEventEmitter = (emitter, eventName, handler) => { +export const useEventEmitter = (emitter: EventEmitter, eventName: string | symbol, handler: Handler) => { // Create a ref that stores handler - const savedHandler = useRef(); + const savedHandler = useRef(handler); // Update ref.current value if handler changes. useEffect(() => { diff --git a/src/hooks/useSettings.js b/src/hooks/useSettings.ts similarity index 90% rename from src/hooks/useSettings.js rename to src/hooks/useSettings.ts index 151a6369de..4d1e1d5bad 100644 --- a/src/hooks/useSettings.js +++ b/src/hooks/useSettings.ts @@ -18,7 +18,7 @@ import {useEffect, useState} from "react"; import SettingsStore from '../settings/SettingsStore'; // Hook to fetch the value of a setting and dynamically update when it changes -export const useSettingValue = (settingName, roomId = null, excludeDefault = false) => { +export const useSettingValue = (settingName: string, roomId: string = null, excludeDefault = false) => { const [value, setValue] = useState(SettingsStore.getValue(settingName, roomId, excludeDefault)); useEffect(() => { @@ -35,7 +35,7 @@ export const useSettingValue = (settingName, roomId = null, excludeDefault = fal }; // Hook to fetch whether a feature is enabled and dynamically update when that changes -export const useFeatureEnabled = (featureName, roomId = null) => { +export const useFeatureEnabled = (featureName: string, roomId: string = null) => { const [enabled, setEnabled] = useState(SettingsStore.isFeatureEnabled(featureName, roomId)); useEffect(() => { diff --git a/src/hooks/useStateToggle.js b/src/hooks/useStateToggle.ts similarity index 85% rename from src/hooks/useStateToggle.js rename to src/hooks/useStateToggle.ts index 58cf123bfb..85441df328 100644 --- a/src/hooks/useStateToggle.js +++ b/src/hooks/useStateToggle.ts @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {useState} from 'react'; +import {useState} from "react"; // Hook to simplify toggling of a boolean state value // Returns value, method to toggle boolean value and method to set the boolean value -export const useStateToggle = (initialValue) => { - const [value, setValue] = useState(Boolean(initialValue)); +export const useStateToggle = (initialValue: boolean) => { + const [value, setValue] = useState(initialValue); const toggleValue = () => { setValue(!value); }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 547a43f603..28028f401e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -117,10 +117,6 @@ "Unable to enable Notifications": "Unable to enable Notifications", "This email address was not found": "This email address was not found", "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", - "Use your account to sign in to the latest version": "Use your account to sign in to the latest version", - "We’re excited to announce Riot is now Element": "We’re excited to announce Riot is now Element", - "Riot is now Element!": "Riot is now Element!", - "Learn More": "Learn More", "Sign In or Create Account": "Sign In or Create Account", "Use your account or create a new one to continue.": "Use your account or create a new one to continue.", "Create Account": "Create Account", @@ -654,9 +650,10 @@ "not found": "not found", "Cross-signing private keys:": "Cross-signing private keys:", "in secret storage": "in secret storage", - "Self signing private key:": "Self signing private key:", + "Master private key:": "Master private key:", "cached locally": "cached locally", "not found locally": "not found locally", + "Self signing private key:": "Self signing private key:", "User signing private key:": "User signing private key:", "Session backup key:": "Session backup key:", "Secret storage public key:": "Secret storage public key:", @@ -1179,11 +1176,11 @@ "All messages": "All messages", "Mentions & Keywords": "Mentions & Keywords", "Notification options": "Notification options", - "Leave Room": "Leave Room", "Forget Room": "Forget Room", "Favourited": "Favourited", "Favourite": "Favourite", "Low Priority": "Low Priority", + "Leave Room": "Leave Room", "Room options": "Room options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", "%(count)s unread messages including mentions.|one": "1 unread mention.", @@ -1722,11 +1719,6 @@ "Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:", "If you didn’t sign in to this session, your account may be compromised.": "If you didn’t sign in to this session, your account may be compromised.", "This wasn't me": "This wasn't me", - "Use your account to sign in to the latest version of the app at ": "Use your account to sign in to the latest version of the app at ", - "You’re already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at element.io/get-started.": "You’re already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at element.io/get-started.", - "Go to Element": "Go to Element", - "We’re excited to announce Riot is now Element!": "We’re excited to announce Riot is now Element!", - "Learn more at element.io/previously-riot": "Learn more at element.io/previously-riot", "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, 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", @@ -1798,7 +1790,7 @@ "Share Community": "Share Community", "Share Room Message": "Share Room Message", "Link to selected message": "Link to selected message", - "COPY": "COPY", + "Copy": "Copy", "Command Help": "Command Help", "To help us prevent this in future, please send us logs.": "To help us prevent this in future, please send us logs.", "Missing session data": "Missing session data", @@ -1977,7 +1969,8 @@ "Couldn't load page": "Couldn't load page", "You must register to use this functionality": "You must register to use this functionality", "You must join the room to see its files": "You must join the room to see its files", - "There are no visible files in this room": "There are no visible files in this room", + "No files visible in this room": "No files visible in this room", + "Attach files from chat or just drag and drop them anywhere in a room.": "Attach files from chat or just drag and drop them anywhere in a room.", "

HTML for your community's page

\n

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

\n

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

\n": "

HTML for your community's page

\n

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

\n

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

\n", "Add rooms to the community summary": "Add rooms to the community summary", "Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?", @@ -2050,7 +2043,8 @@ "Communities": "Communities", "Create a new community": "Create a new community", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.", - "You have no visible notifications": "You have no visible notifications", + "You’re all caught up": "You’re all caught up", + "You have no visible notifications in this room.": "You have no visible notifications in this room.", "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.", "%(brand)s failed to get the public room list.": "%(brand)s failed to get the public room list.", "The homeserver may be unavailable or overloaded.": "The homeserver may be unavailable or overloaded.", @@ -2172,7 +2166,7 @@ "%(brand)s Web": "%(brand)s Web", "%(brand)s Desktop": "%(brand)s Desktop", "%(brand)s iOS": "%(brand)s iOS", - "%(brand)s X for Android": "%(brand)s X for Android", + "%(brand)s Android": "%(brand)s Android", "or another cross-signing capable Matrix client": "or another cross-signing capable Matrix client", "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.", "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.", @@ -2237,7 +2231,6 @@ "Confirm your recovery passphrase": "Confirm your recovery passphrase", "Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.": "Store your Security Key somewhere safe, like a password manager or a safe, as it’s used to safeguard your encrypted data.", "Download": "Download", - "Copy": "Copy", "Unable to query secret storage status": "Unable to query secret storage status", "Retry": "Retry", "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.", diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index d5abb3f2d2..fa263a2a55 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -145,7 +145,7 @@ export default class EventIndex extends EventEmitter { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (prevState === "PREPARED" && state === "SYNCING") { - // If our indexer is empty we're most likely running Riot the + // If our indexer is empty we're most likely running Element the // first time with indexing support or running it with an // initial sync. Add checkpoints to crawl our encrypted rooms. const eventIndexWasEmpty = await indexManager.isEventIndexEmpty(); diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 9d28b7d07d..59edc8766c 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -64,7 +64,7 @@ export function _td(s: string): string { // Wrapper for counterpart's translation function so that it handles nulls and undefineds properly // Takes the same arguments as counterpart.translate() function safeCounterpartTranslate(text: string, options?: object) { - // Horrible hack to avoid https://github.com/vector-im/riot-web/issues/4191 + // Horrible hack to avoid https://github.com/vector-im/element-web/issues/4191 // The interpolation library that counterpart uses does not support undefined/null // values and instead will throw an error. This is a problem since everywhere else // in JS land passing undefined/null will simply stringify instead, and when converting @@ -295,7 +295,7 @@ export function replaceByRegexes(text: string, mapping: IVariables | Tags): stri // Allow overriding the text displayed when no translation exists // Currently only used in unit tests to avoid having to load -// the translations in riot-web +// the translations in element-web export function setMissingEntryGenerator(f: (value: string) => void) { counterpart.setMissingEntryGenerator(f); } diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index e0e5570d99..fbcce4cef9 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -72,7 +72,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) { const body = new FormData(); body.append('text', opts.userText || "User did not supply any additional text."); - body.append('app', 'riot-web'); + body.append('app', 'element-web'); body.append('version', version); body.append('user_agent', userAgent); body.append('installed_pwa', installedPWA); @@ -102,6 +102,8 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) { body.append("ssss_key_in_account", String(!!(await secretStorage.hasKey()))); const pkCache = client.getCrossSigningCacheCallbacks(); + body.append("master_pk_cached", + String(!!(pkCache && await pkCache.getCrossSigningKeyCache("master")))); body.append("self_signing_pk_cached", String(!!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")))); body.append("user_signing_pk_cached", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 1989bd7a34..714d80f983 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -19,7 +19,6 @@ import { MatrixClient } from 'matrix-js-sdk/src/client'; import { _td } from '../languageHandler'; import { - AudioNotificationsEnabledController, NotificationBodyEnabledController, NotificationsEnabledController, } from "./controllers/NotificationControllers"; @@ -160,7 +159,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: false, }, "advancedRoomListLogging": { - // TODO: Remove flag before launch: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231 displayName: _td("Enable advanced debugging for the room list"), supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: false, @@ -460,7 +459,6 @@ export const SETTINGS: {[setting: string]: ISetting} = { "audioNotificationsEnabled": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: true, - controller: new AudioNotificationsEnabledController(), }, "enableWidgetScreenshots": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, @@ -488,13 +486,13 @@ export const SETTINGS: {[setting: string]: ISetting} = { deny: [], }, }, - // TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14373 + // TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373 "RoomList.orderAlphabetically": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("Order rooms by name"), default: false, }, - // TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14373 + // TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373 "RoomList.orderByImportance": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("Show rooms with unread notifications first"), diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index fc50af6096..bf5971d02e 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -79,12 +79,3 @@ export class NotificationBodyEnabledController extends SettingController { return calculatedValue; } } - -export class AudioNotificationsEnabledController extends SettingController { - public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any { - if (!getNotifier().isPossible()) return false; - - // Note: Audio notifications are *not* enabled by default. - return calculatedValue; - } -} diff --git a/src/settings/handlers/AccountSettingsHandler.ts b/src/settings/handlers/AccountSettingsHandler.ts index 53180aeba8..d609fd3231 100644 --- a/src/settings/handlers/AccountSettingsHandler.ts +++ b/src/settings/handlers/AccountSettingsHandler.ts @@ -59,7 +59,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; - const changedSettings = objectKeyChanges(prevContent, event.getContent()); + const changedSettings = objectKeyChanges>(prevContent, event.getContent()); for (const settingName of changedSettings) { const val = event.getContent()[settingName]; this.watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val); diff --git a/src/settings/handlers/RoomAccountSettingsHandler.ts b/src/settings/handlers/RoomAccountSettingsHandler.ts index e3449e76c3..53e29653f8 100644 --- a/src/settings/handlers/RoomAccountSettingsHandler.ts +++ b/src/settings/handlers/RoomAccountSettingsHandler.ts @@ -59,7 +59,7 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; - const changedSettings = objectKeyChanges(prevContent, event.getContent()); + const changedSettings = objectKeyChanges>(prevContent, event.getContent()); for (const settingName of changedSettings) { const val = event.getContent()[settingName]; this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_ACCOUNT, val); diff --git a/src/settings/handlers/RoomSettingsHandler.ts b/src/settings/handlers/RoomSettingsHandler.ts index 4b4d4c4ad9..3315e40a65 100644 --- a/src/settings/handlers/RoomSettingsHandler.ts +++ b/src/settings/handlers/RoomSettingsHandler.ts @@ -65,9 +65,10 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; - const changedSettings = objectKeyChanges(prevContent, event.getContent()); + const changedSettings = objectKeyChanges>(prevContent, event.getContent()); for (const settingName of changedSettings) { - this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM, event.getContent()[settingName]); + this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM, + event.getContent()[settingName]); } } }; diff --git a/src/settings/watchers/ThemeWatcher.ts b/src/settings/watchers/ThemeWatcher.ts index 4330a15f57..53d72c7849 100644 --- a/src/settings/watchers/ThemeWatcher.ts +++ b/src/settings/watchers/ThemeWatcher.ts @@ -78,7 +78,7 @@ export default class ThemeWatcher { }; // XXX: forceTheme param added here as local echo appears to be unreliable - // https://github.com/vector-im/riot-web/issues/11443 + // https://github.com/vector-im/element-web/issues/11443 public recheck(forceTheme?: string) { const oldTheme = this.currentTheme; this.currentTheme = forceTheme === undefined ? this.getEffectiveTheme() : forceTheme; diff --git a/src/stores/ActiveWidgetStore.js b/src/stores/ActiveWidgetStore.js index c6f8dc69b7..bf9ae3586c 100644 --- a/src/stores/ActiveWidgetStore.js +++ b/src/stores/ActiveWidgetStore.js @@ -64,7 +64,7 @@ class ActiveWidgetStore extends EventEmitter { // Everything else relies on views listening for events and calling setters // on this class which is terrible. This store should just listen for events // and keep itself up to date. - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) if (ev.getType() !== 'im.vector.modular.widgets') return; if (ev.getStateKey() === this._persistentWidgetId) { diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.tsx similarity index 82% rename from src/stores/RoomViewStore.js rename to src/stores/RoomViewStore.tsx index 3c37a31174..a0f0fb8f68 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.tsx @@ -15,13 +15,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher/dispatcher'; + +import React from "react"; import {Store} from 'flux/utils'; + +import dis from '../dispatcher/dispatcher'; import {MatrixClientPeg} from '../MatrixClientPeg'; import * as sdk from '../index'; import Modal from '../Modal'; import { _t } from '../languageHandler'; import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache'; +import {ActionPayload} from "../dispatcher/payloads"; const INITIAL_STATE = { // Whether we're joining the currently viewed room (see isJoining()) @@ -33,6 +37,7 @@ const INITIAL_STATE = { // The event to scroll to when the room is first viewed initialEventId: null, + initialEventPixelOffset: null, // Whether to highlight the initial event isInitialEventHighlighted: false, @@ -46,6 +51,10 @@ const INITIAL_STATE = { forwardingEvent: null, quotingEvent: null, + + replyingToEvent: null, + + shouldPeek: false, }; /** @@ -53,21 +62,20 @@ const INITIAL_STATE = { * with a subset of the js-sdk. * ``` */ -class RoomViewStore extends Store { +class RoomViewStore extends Store { + private state = INITIAL_STATE; // initialize state + constructor() { super(dis); - - // Initialise state - this._state = INITIAL_STATE; } - _setState(newState) { + setState(newState: Partial) { // If values haven't changed, there's nothing to do. // This only tries a shallow comparison, so unchanged objects will slip // through, but that's probably okay for now. let stateChanged = false; for (const key of Object.keys(newState)) { - if (this._state[key] !== newState[key]) { + if (this.state[key] !== newState[key]) { stateChanged = true; break; } @@ -76,7 +84,7 @@ class RoomViewStore extends Store { return; } - this._state = Object.assign(this._state, newState); + this.state = Object.assign(this.state, newState); this.__emitChange(); } @@ -89,59 +97,63 @@ class RoomViewStore extends Store { // - event_offset: 100 // - highlighted: true case 'view_room': - this._viewRoom(payload); + this.viewRoom(payload); break; + // for these events blank out the roomId as we are no longer in the RoomView + case 'view_create_group': + case 'view_welcome_page': + case 'view_home_page': case 'view_my_groups': case 'view_group': - this._setState({ + this.setState({ roomId: null, roomAlias: null, }); break; case 'view_room_error': - this._viewRoomError(payload); + this.viewRoomError(payload); break; case 'will_join': - this._setState({ + this.setState({ joining: true, }); break; case 'cancel_join': - this._setState({ + this.setState({ joining: false, }); break; // join_room: // - opts: options for joinRoom case 'join_room': - this._joinRoom(payload); + this.joinRoom(payload); break; case 'join_room_error': - this._joinRoomError(payload); + this.joinRoomError(payload); break; case 'join_room_ready': - this._setState({ shouldPeek: false }); + this.setState({ shouldPeek: false }); break; case 'on_client_not_viable': case 'on_logged_out': this.reset(); break; case 'forward_event': - this._setState({ + this.setState({ forwardingEvent: payload.event, }); break; case 'reply_to_event': // If currently viewed room does not match the room in which we wish to reply then change rooms // this can happen when performing a search across all rooms - if (payload.event && payload.event.getRoomId() !== this._state.roomId) { + if (payload.event && payload.event.getRoomId() !== this.state.roomId) { dis.dispatch({ action: 'view_room', room_id: payload.event.getRoomId(), replyingToEvent: payload.event, }); } else { - this._setState({ + this.setState({ replyingToEvent: payload.event, }); } @@ -149,14 +161,14 @@ class RoomViewStore extends Store { case 'open_room_settings': { const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog"); Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, { - roomId: payload.room_id || this._state.roomId, + roomId: payload.room_id || this.state.roomId, }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); break; } } } - async _viewRoom(payload) { + private async viewRoom(payload: ActionPayload) { if (payload.room_id) { const newState = { roomId: payload.room_id, @@ -181,18 +193,18 @@ class RoomViewStore extends Store { newState.replyingToEvent = payload.replyingToEvent; } - if (this._state.forwardingEvent) { + if (this.state.forwardingEvent) { dis.dispatch({ action: 'send_event', room_id: newState.roomId, - event: this._state.forwardingEvent, + event: this.state.forwardingEvent, }); } - this._setState(newState); + this.setState(newState); if (payload.auto_join) { - this._joinRoom(payload); + this.joinRoom(payload); } } else if (payload.room_alias) { // Try the room alias to room ID navigation cache first to avoid @@ -201,7 +213,7 @@ class RoomViewStore extends Store { if (!roomId) { // Room alias cache miss, so let's ask the homeserver. Resolve the alias // and then do a second dispatch with the room ID acquired. - this._setState({ + this.setState({ roomId: null, initialEventId: null, initialEventPixelOffset: null, @@ -238,8 +250,8 @@ class RoomViewStore extends Store { } } - _viewRoomError(payload) { - this._setState({ + private viewRoomError(payload: ActionPayload) { + this.setState({ roomId: payload.room_id, roomAlias: payload.room_alias, roomLoading: false, @@ -247,12 +259,12 @@ class RoomViewStore extends Store { }); } - _joinRoom(payload) { - this._setState({ + private joinRoom(payload: ActionPayload) { + this.setState({ joining: true, }); MatrixClientPeg.get().joinRoom( - this._state.roomAlias || this._state.roomId, payload.opts, + this.state.roomAlias || this.state.roomId, payload.opts, ).then(() => { // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the @@ -273,7 +285,7 @@ class RoomViewStore extends Store { {_t("Please contact your homeserver administrator.")}
; } else if (err.httpStatus === 404) { - const invitingUserId = this._getInvitingUserId(this._state.roomId); + const invitingUserId = this.getInvitingUserId(this.state.roomId); // only provide a better error message for invites if (invitingUserId) { // if the inviting user is on the same HS, there can only be one cause: they left. @@ -292,7 +304,7 @@ class RoomViewStore extends Store { }); } - _getInvitingUserId(roomId) { + private getInvitingUserId(roomId: string): string { const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); if (room && room.getMyMembership() === "invite") { @@ -302,45 +314,45 @@ class RoomViewStore extends Store { } } - _joinRoomError(payload) { - this._setState({ + private joinRoomError(payload: ActionPayload) { + this.setState({ joining: false, joinError: payload.err, }); } - reset() { - this._state = Object.assign({}, INITIAL_STATE); + public reset() { + this.state = Object.assign({}, INITIAL_STATE); } // The room ID of the room currently being viewed - getRoomId() { - return this._state.roomId; + public getRoomId() { + return this.state.roomId; } // The event to scroll to when the room is first viewed - getInitialEventId() { - return this._state.initialEventId; + public getInitialEventId() { + return this.state.initialEventId; } // Whether to highlight the initial event - isInitialEventHighlighted() { - return this._state.isInitialEventHighlighted; + public isInitialEventHighlighted() { + return this.state.isInitialEventHighlighted; } // The room alias of the room (or null if not originally specified in view_room) - getRoomAlias() { - return this._state.roomAlias; + public getRoomAlias() { + return this.state.roomAlias; } // Whether the current room is loading (true whilst resolving an alias) - isRoomLoading() { - return this._state.roomLoading; + public isRoomLoading() { + return this.state.roomLoading; } // Any error that has occurred during loading - getRoomLoadError() { - return this._state.roomLoadError; + public getRoomLoadError() { + return this.state.roomLoadError; } // True if we're expecting the user to be joined to the room currently being @@ -366,27 +378,27 @@ class RoomViewStore extends Store { // // show join prompt // } // } - isJoining() { - return this._state.joining; + public isJoining() { + return this.state.joining; } // Any error that has occurred during joining - getJoinError() { - return this._state.joinError; + public getJoinError() { + return this.state.joinError; } // The mxEvent if one is about to be forwarded - getForwardingEvent() { - return this._state.forwardingEvent; + public getForwardingEvent() { + return this.state.forwardingEvent; } // The mxEvent if one is currently being replied to/quoted - getQuotingEvent() { - return this._state.replyingToEvent; + public getQuotingEvent() { + return this.state.replyingToEvent; } - shouldPeek() { - return this._state.shouldPeek; + public shouldPeek() { + return this.state.shouldPeek; } } diff --git a/src/stores/notifications/NotificationColor.ts b/src/stores/notifications/NotificationColor.ts index aa2384b3df..b12f2b7c00 100644 --- a/src/stores/notifications/NotificationColor.ts +++ b/src/stores/notifications/NotificationColor.ts @@ -17,7 +17,7 @@ limitations under the License. export enum NotificationColor { // Inverted (None -> Red) because we do integer comparisons on this None, // nothing special - // TODO: Remove bold with notifications: https://github.com/vector-im/riot-web/issues/14227 + // TODO: Remove bold with notifications: https://github.com/vector-im/element-web/issues/14227 Bold, // no badge, show as unread Grey, // unread notified messages Red, // unread pings diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index c3e7fd5c51..f4c2d5050e 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -65,7 +65,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { private readonly watchedSettings = [ 'feature_custom_tags', - 'advancedRoomListLogging', // TODO: Remove watch: https://github.com/vector-im/riot-web/issues/14602 + 'advancedRoomListLogging', // TODO: Remove watch: https://github.com/vector-im/element-web/issues/14602 ]; constructor() { @@ -161,7 +161,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { await this.algorithm.setStickyRoom(null); } else if (activeRoom !== this.algorithm.stickyRoom) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Changing sticky room to ${activeRoomId}`); } await this.algorithm.setStickyRoom(activeRoom); @@ -205,7 +205,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { if (payload.action === 'setting_updated') { if (this.watchedSettings.includes(payload.settingName)) { - // TODO: Remove with https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove with https://github.com/vector-im/element-web/issues/14602 if (payload.settingName === "advancedRoomListLogging") { // Log when the setting changes so we know when it was turned on in the rageshake const enabled = SettingsStore.getValue("advancedRoomListLogging"); @@ -236,7 +236,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { return; } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Got own read receipt in ${room.roomId}`); } await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt); @@ -246,7 +246,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } else if (payload.action === 'MatrixActions.Room.tags') { const roomPayload = (payload); // TODO: Type out the dispatcher types if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`); } await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange); @@ -261,13 +261,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const room = this.matrixClient.getRoom(roomId); const tryUpdate = async (updatedRoom: Room) => { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()}` + ` in ${updatedRoom.roomId}`); } if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`); } const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']); @@ -300,7 +300,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { return; } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`); } await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); @@ -308,7 +308,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { const eventPayload = (payload); // TODO: Type out the dispatcher types if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Received updated DM map`); } const dmMap = eventPayload.event.getContent(); @@ -335,7 +335,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const newMembership = getEffectiveMembership(membershipPayload.membership); if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`); } @@ -344,7 +344,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", ""); if (createEvent && createEvent.getContent()['predecessor']) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Room has a predecessor`); } const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']); @@ -352,7 +352,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const isSticky = this.algorithm.stickyRoom === prevRoom; if (isSticky) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`); } await this.algorithm.setStickyRoom(null); @@ -361,7 +361,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // Note: we hit the algorithm instead of our handleRoomUpdate() function to // avoid redundant updates. if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Removing previous room from room list`); } await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved); @@ -369,7 +369,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Adding new room to room list`); } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom); @@ -379,7 +379,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`); } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom); @@ -390,7 +390,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // If it's not a join, it's transitioning into a different list (possibly historical) if (oldMembership !== newMembership) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`); } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange); @@ -404,7 +404,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); if (shouldUpdate) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`); } this.updateFn.mark(); @@ -418,7 +418,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { private async setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) { await this.algorithm.setTagSorting(tagId, sort); - // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 + // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 localStorage.setItem(`mx_tagSort_${tagId}`, sort); } @@ -428,7 +428,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // noinspection JSMethodCanBeStatic private getStoredTagSorting(tagId: TagID): SortAlgorithm { - // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 + // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 return localStorage.getItem(`mx_tagSort_${tagId}`); } @@ -462,7 +462,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { private async setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) { await this.algorithm.setListOrdering(tagId, order); - // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 + // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 localStorage.setItem(`mx_listOrder_${tagId}`, order); } @@ -472,7 +472,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // noinspection JSMethodCanBeStatic private getStoredListOrder(tagId: TagID): ListAlgorithm { - // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 + // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 return localStorage.getItem(`mx_listOrder_${tagId}`); } @@ -521,7 +521,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { private onAlgorithmListUpdated = () => { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log("Underlying algorithm has triggered a list update - marking"); } this.updateFn.mark(); @@ -574,7 +574,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { public addFilter(filter: IFilterCondition): void { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log("Adding filter condition:", filter); } this.filterConditions.push(filter); @@ -586,7 +586,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { public removeFilter(filter: IFilterCondition): void { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log("Removing filter condition:", filter); } const idx = this.filterConditions.indexOf(filter); diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index f1a7ab1613..439141edb4 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -334,7 +334,7 @@ export class Algorithm extends EventEmitter { newMap[tagId] = allowedRoomsInThisTag; if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[DEBUG] ${newMap[tagId].length}/${rooms.length} rooms filtered into ${tagId}`); } } @@ -349,7 +349,7 @@ export class Algorithm extends EventEmitter { if (!this.hasFilters) return; // don't bother doing work if there's nothing to do if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Recalculating filtered rooms for ${tagId}`); } delete this.filteredRooms[tagId]; @@ -361,7 +361,7 @@ export class Algorithm extends EventEmitter { } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[DEBUG] ${filteredRooms.length}/${rooms.length} rooms filtered into ${tagId}`); } } @@ -403,7 +403,7 @@ export class Algorithm extends EventEmitter { if (!this._cachedStickyRooms || !updatedTag) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Generating clone of cached rooms for sticky room handling`); } const stickiedTagMap: ITagMap = {}; @@ -417,7 +417,7 @@ export class Algorithm extends EventEmitter { // Update the tag indicated by the caller, if possible. This is mostly to ensure // our cache is up to date. if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Replacing cached sticky rooms for ${updatedTag}`); } this._cachedStickyRooms[updatedTag] = this.cachedRooms[updatedTag].map(r => r); // shallow clone @@ -429,7 +429,7 @@ export class Algorithm extends EventEmitter { const sticky = this._stickyRoom; if (!updatedTag || updatedTag === sticky.tag) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log( `Inserting sticky room ${sticky.room.roomId} at position ${sticky.position} in ${sticky.tag}`, ); @@ -660,7 +660,7 @@ export class Algorithm extends EventEmitter { */ public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Handle room update for ${room.roomId} called with cause ${cause}`); } if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from"); @@ -720,7 +720,7 @@ export class Algorithm extends EventEmitter { if (diff.removed.length > 0 || diff.added.length > 0) { for (const rmTag of diff.removed) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Removing ${room.roomId} from ${rmTag}`); } const algorithm: OrderingAlgorithm = this.algorithms[rmTag]; @@ -732,7 +732,7 @@ export class Algorithm extends EventEmitter { } for (const addTag of diff.added) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Adding ${room.roomId} to ${addTag}`); } const algorithm: OrderingAlgorithm = this.algorithms[addTag]; @@ -745,14 +745,14 @@ export class Algorithm extends EventEmitter { this.roomIdsToTags[room.roomId] = newTags; if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Changing update cause for ${room.roomId} to Timeline to sort rooms`); } cause = RoomUpdateCause.Timeline; didTagChange = true; } else { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Received no-op update for ${room.roomId} - changing to Timeline update`); } cause = RoomUpdateCause.Timeline; @@ -781,7 +781,7 @@ export class Algorithm extends EventEmitter { if (cause !== RoomUpdateCause.NewRoom && cause !== RoomUpdateCause.RoomRemoved) { if (this.stickyRoom === room) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.warn(`[RoomListDebug] Received ${cause} update for sticky room ${room.roomId} - ignoring`); } return false; @@ -791,14 +791,14 @@ export class Algorithm extends EventEmitter { if (!this.roomIdsToTags[room.roomId]) { if (CAUSES_REQUIRING_ROOM.includes(cause)) { if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.warn(`Skipping tag update for ${room.roomId} because we don't know about the room`); } return false; } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`); } @@ -812,13 +812,13 @@ export class Algorithm extends EventEmitter { this.roomIdsToTags[room.roomId] = roomTags; if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Updated tags for ${room.roomId}:`, roomTags); } } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Reached algorithmic handling for ${room.roomId} and cause ${cause}`); } @@ -843,7 +843,7 @@ export class Algorithm extends EventEmitter { } if (SettingsStore.getValue("advancedRoomListLogging")) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602 + // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Finished handling ${room.roomId} with cause ${cause} (changed=${changed})`); } return changed; diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index e4aa5ff06f..80bdf74afb 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -270,7 +270,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { `!! Room list index corruption: ${lastCat} (i:${indices[lastCat]}) is greater ` + `than ${thisCat} (i:${indices[thisCat]}) - category indices are likely desynced from reality`); - // TODO: Regenerate index when this happens: https://github.com/vector-im/riot-web/issues/14234 + // TODO: Regenerate index when this happens: https://github.com/vector-im/element-web/issues/14234 } } } diff --git a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts index 5759f0c32d..cc2a28d892 100644 --- a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts @@ -54,7 +54,7 @@ export class NaturalAlgorithm extends OrderingAlgorithm { } } - // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14457 + // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/element-web/issues/14457 // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags this.cachedOrderedRooms = await sortRoomsWithAlgorithm( this.cachedOrderedRooms, diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 09182f3bfb..7c8c879cf6 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -33,13 +33,13 @@ export class RecentAlgorithm implements IAlgorithm { // of the rooms to each other. // TODO: We could probably improve the sorting algorithm here by finding changes. - // See https://github.com/vector-im/riot-web/issues/14459 + // See https://github.com/vector-im/element-web/issues/14459 // For example, if we spent a little bit of time to determine which elements have // actually changed (probably needs to be done higher up?) then we could do an // insertion sort or similar on the limited set of changes. // TODO: Don't assume we're using the same client as the peg - // See https://github.com/vector-im/riot-web/issues/14458 + // See https://github.com/vector-im/element-web/issues/14458 let myUserId = ''; if (MatrixClientPeg.get()) { myUserId = MatrixClientPeg.get().getUserId(); diff --git a/src/theme.js b/src/theme.js index c79e466933..a413ae74af 100644 --- a/src/theme.js +++ b/src/theme.js @@ -202,7 +202,7 @@ export async function setTheme(theme) { return new Promise((resolve) => { const switchTheme = function() { // we re-enable our theme here just in case we raced with another - // theme set request as per https://github.com/vector-im/riot-web/issues/5601. + // theme set request as per https://github.com/vector-im/element-web/issues/5601. // We could alternatively lock or similar to stop the race, but // this is probably good enough for now. styleElements[stylesheetName].disabled = false; diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index 595f3a35d6..dfd06cf3a0 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -29,7 +29,7 @@ const TOAST_KEY = "update"; /* * Check a version string is compatible with the Changelog - * dialog ([riot-version]-react-[react-sdk-version]-js-[js-sdk-version]) + * dialog ([element-version]-react-[react-sdk-version]-js-[js-sdk-version]) */ function checkVersion(ver) { const parts = ver.split('-'); diff --git a/src/usercontent/index.html b/src/usercontent/index.html index 90a0fe7c16..9abbdb5289 100644 --- a/src/usercontent/index.html +++ b/src/usercontent/index.html @@ -2,9 +2,9 @@ diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js index 7452ff31a5..94aa0c3544 100644 --- a/src/utils/AutoDiscoveryUtils.js +++ b/src/utils/AutoDiscoveryUtils.js @@ -80,7 +80,7 @@ export default class AutoDiscoveryUtils { { a: (sub) => { return {sub}; @@ -203,7 +203,7 @@ export default class AutoDiscoveryUtils { // Note: In the cases where we rely on the default IS from the config (namely // lack of identity server provided by the discovery method), we intentionally do not // validate it. This has already been validated and this helps some off-the-grid usage - // of Riot. + // of Element. let preferredIdentityUrl = defaultConfig && defaultConfig['isUrl']; if (isResult && isResult.state === AutoDiscovery.SUCCESS) { preferredIdentityUrl = isResult["base_url"]; diff --git a/src/utils/DecryptFile.js b/src/utils/DecryptFile.js index f5a1b0aa62..dcdc2f9fdb 100644 --- a/src/utils/DecryptFile.js +++ b/src/utils/DecryptFile.js @@ -28,7 +28,7 @@ import {MatrixClientPeg} from '../MatrixClientPeg'; // called createObjectURL(), and so if the content contains any scripting then it // will pose a XSS vulnerability when the browser renders it. This is particularly // bad if the user right-clicks the URI and pastes it into a new window or tab, -// as the blob will then execute with access to Riot's full JS environment(!) +// as the blob will then execute with access to Element's full JS environment(!) // // See https://github.com/matrix-org/matrix-react-sdk/pull/1820#issuecomment-385210647 // for details. diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index f7f4be202b..645953210d 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -69,7 +69,7 @@ export default class WidgetUtils { return false; } - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) return room.currentState.maySendStateEvent('im.vector.modular.widgets', me); } @@ -185,7 +185,7 @@ export default class WidgetUtils { } const room = MatrixClientPeg.get().getRoom(roomId); - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) const startingWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); if (eventsInIntendedState(startingWidgetEvents)) { resolve(); @@ -195,7 +195,7 @@ export default class WidgetUtils { function onRoomStateEvents(ev) { if (ev.getRoomId() !== roomId) return; - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) const currentWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); if (eventsInIntendedState(currentWidgetEvents)) { @@ -263,8 +263,8 @@ export default class WidgetUtils { if (addingWidget) { content = { - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) - // For now we'll send the legacy event type for compatibility with older apps/riots + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) + // For now we'll send the legacy event type for compatibility with older apps/elements type: widgetType.legacy, url: widgetUrl, name: widgetName, @@ -277,7 +277,7 @@ export default class WidgetUtils { WidgetEchoStore.setRoomWidgetEcho(roomId, widgetId, content); const client = MatrixClientPeg.get(); - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) return client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => { return WidgetUtils.waitForRoomWidget(widgetId, roomId, addingWidget); }).finally(() => { @@ -291,7 +291,7 @@ export default class WidgetUtils { * @return {[object]} Array containing current / active room widgets */ static getRoomWidgets(room: Room) { - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); if (!appsStateEvents) { return []; @@ -466,7 +466,7 @@ export default class WidgetUtils { // safe to send. // We'll end up using a local render URL when we see a Jitsi widget anyways, so this is // really just for backwards compatibility and to appease the spec. - baseUrl = "https://riot.im/app/"; + baseUrl = "https://app.element.io/"; } const url = new URL("jitsi.html#" + queryString, baseUrl); // this strips hash fragment from baseUrl return url.href; diff --git a/src/utils/maps.ts b/src/utils/maps.ts new file mode 100644 index 0000000000..96832094f0 --- /dev/null +++ b/src/utils/maps.ts @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { arrayDiff, arrayMerge, arrayUnion } from "./arrays"; + +/** + * Determines the keys added, changed, and removed between two Maps. + * For changes, simple triple equal comparisons are done, not in-depth tree checking. + * @param a The first Map. Must be defined. + * @param b The second Map. Must be defined. + * @returns The difference between the keys of each Map. + */ +export function mapDiff(a: Map, b: Map): { changed: K[], added: K[], removed: K[] } { + const aKeys = [...a.keys()]; + const bKeys = [...b.keys()]; + const keyDiff = arrayDiff(aKeys, bKeys); + const possibleChanges = arrayUnion(aKeys, bKeys); + const changes = possibleChanges.filter(k => a.get(k) !== b.get(k)); + + return {changed: changes, added: keyDiff.added, removed: keyDiff.removed}; +} + +/** + * Gets all the key changes (added, removed, or value difference) between two Maps. + * Triple equals is used to compare values, not in-depth tree checking. + * @param a The first Map. Must be defined. + * @param b The second Map. Must be defined. + * @returns The keys which have been added, removed, or changed between the two Maps. + */ +export function mapKeyChanges(a: Map, b: Map): K[] { + const diff = mapDiff(a, b); + return arrayMerge(diff.removed, diff.added, diff.changed); +} diff --git a/src/utils/membership.ts b/src/utils/membership.ts index 9f1c5b7b41..9534623d62 100644 --- a/src/utils/membership.ts +++ b/src/utils/membership.ts @@ -63,7 +63,7 @@ export function getEffectiveMembership(membership: string): EffectiveMembership if (membership === 'invite') { return EffectiveMembership.Invite; } else if (membership === 'join') { - // TODO: Include knocks? Update docs as needed in the enum. https://github.com/vector-im/riot-web/issues/14237 + // TODO: Include knocks? Update docs as needed in the enum. https://github.com/vector-im/element-web/issues/14237 return EffectiveMembership.Join; } else { // Probably a leave, kick, or ban diff --git a/src/utils/objects.ts b/src/utils/objects.ts index ddd9830832..bc74ab9ee0 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -16,15 +16,17 @@ limitations under the License. import { arrayDiff, arrayHasDiff, arrayMerge, arrayUnion } from "./arrays"; +type ObjectExcluding = {[k in Exclude]: O[k]}; + /** * Gets a new object which represents the provided object, excluding some properties. * @param a The object to strip properties of. Must be defined. * @param props The property names to remove. * @returns The new object without the provided properties. */ -export function objectExcluding(a: any, props: string[]): any { +export function objectExcluding>(a: O, props: P): ObjectExcluding { // We use a Map to avoid hammering the `delete` keyword, which is slow and painful. - const tempMap = new Map(Object.entries(a)); + const tempMap = new Map(Object.entries(a) as [keyof O, any][]); for (const prop of props) { tempMap.delete(prop); } @@ -33,7 +35,7 @@ export function objectExcluding(a: any, props: string[]): any { return Array.from(tempMap.entries()).reduce((c, [k, v]) => { c[k] = v; return c; - }, {}); + }, {} as O); } /** @@ -43,13 +45,13 @@ export function objectExcluding(a: any, props: string[]): any { * @param props The property names to keep. * @returns The new object with only the provided properties. */ -export function objectWithOnly(a: any, props: string[]): any { - const existingProps = Object.keys(a); +export function objectWithOnly>(a: O, props: P): {[k in P[number]]: O[k]} { + const existingProps = Object.keys(a) as (keyof O)[]; const diff = arrayDiff(existingProps, props); if (diff.removed.length === 0) { return objectShallowClone(a); } else { - return objectExcluding(a, diff.removed); + return objectExcluding(a, diff.removed) as {[k in P[number]]: O[k]}; } } @@ -64,9 +66,9 @@ export function objectWithOnly(a: any, props: string[]): any { * First argument is the property key with the second being the current value. * @returns A cloned object. */ -export function objectShallowClone(a: any, propertyCloner?: (k: string, v: any) => any): any { - const newObj = {}; - for (const [k, v] of Object.entries(a)) { +export function objectShallowClone(a: O, propertyCloner?: (k: keyof O, v: O[keyof O]) => any): O { + const newObj = {} as O; + for (const [k, v] of Object.entries(a) as [keyof O, O[keyof O]][]) { newObj[k] = v; if (propertyCloner) { newObj[k] = propertyCloner(k, v); @@ -83,7 +85,7 @@ export function objectShallowClone(a: any, propertyCloner?: (k: string, v: any) * @param b The second object. Must be defined. * @returns True if there's a difference between the objects, false otherwise */ -export function objectHasDiff(a: any, b: any): boolean { +export function objectHasDiff(a: O, b: O): boolean { const aKeys = Object.keys(a); const bKeys = Object.keys(b); if (arrayHasDiff(aKeys, bKeys)) return true; @@ -92,6 +94,8 @@ export function objectHasDiff(a: any, b: any): boolean { return possibleChanges.some(k => a[k] !== b[k]); } +type Diff = { changed: K[], added: K[], removed: K[] }; + /** * Determines the keys added, changed, and removed between two objects. * For changes, simple triple equal comparisons are done, not in-depth @@ -100,9 +104,9 @@ export function objectHasDiff(a: any, b: any): boolean { * @param b The second object. Must be defined. * @returns The difference between the keys of each object. */ -export function objectDiff(a: any, b: any): { changed: string[], added: string[], removed: string[] } { - const aKeys = Object.keys(a); - const bKeys = Object.keys(b); +export function objectDiff(a: O, b: O): Diff { + const aKeys = Object.keys(a) as (keyof O)[]; + const bKeys = Object.keys(b) as (keyof O)[]; const keyDiff = arrayDiff(aKeys, bKeys); const possibleChanges = arrayUnion(aKeys, bKeys); const changes = possibleChanges.filter(k => a[k] !== b[k]); @@ -119,7 +123,7 @@ export function objectDiff(a: any, b: any): { changed: string[], added: string[] * @returns The keys which have been added, removed, or changed between the * two objects. */ -export function objectKeyChanges(a: any, b: any): string[] { +export function objectKeyChanges(a: O, b: O): (keyof O)[] { const diff = objectDiff(a, b); return arrayMerge(diff.removed, diff.added, diff.changed); } @@ -131,6 +135,6 @@ export function objectKeyChanges(a: any, b: any): string[] { * @param obj The object to clone. * @returns The cloned object */ -export function objectClone(obj: any): any { +export function objectClone(obj: O): O { return JSON.parse(JSON.stringify(obj)); } diff --git a/src/utils/pillify.js b/src/utils/pillify.js index cb140c61a4..432771592a 100644 --- a/src/utils/pillify.js +++ b/src/utils/pillify.js @@ -139,7 +139,7 @@ export function pillifyLinks(nodes, mxEvent, pills) { * It's critical to call this after pillifyLinks, otherwise * Pills will leak, leaking entire DOM trees via the event * emitter on BaseAvatar as per - * https://github.com/vector-im/riot-web/issues/12417 + * https://github.com/vector-im/element-web/issues/12417 * * @param {Node[]} pills - array of pill containers whose React * components should be unmounted. diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index 4775c30c95..15603e9437 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -65,7 +65,7 @@ export interface FromWidgetRequest extends WidgetRequest { } /** - * Handles Riot <--> Widget interactions for embedded/standalone widgets. + * Handles Element <--> Widget interactions for embedded/standalone widgets. * * Emitted events: * - terminate(wait): client requested the widget to terminate. @@ -141,7 +141,7 @@ export class WidgetApi extends EventEmitter { private replyToRequest(payload: ToWidgetRequest, reply: any) { if (!window.parent) return; - const request = objectClone(payload); + const request: ToWidgetRequest & {response?: any} = objectClone(payload); request.response = reply; window.parent.postMessage(request, this.origin); diff --git a/test/components/views/rooms/RoomSettings-test.js b/test/components/views/rooms/RoomSettings-test.js index b3790b2507..3b4989ccab 100644 --- a/test/components/views/rooms/RoomSettings-test.js +++ b/test/components/views/rooms/RoomSettings-test.js @@ -177,7 +177,7 @@ describe.skip('RoomSettings', () => { 'm.room.history_visibility': 50, 'm.room.power_levels': 50, 'm.room.topic': 50, - // TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111) + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) 'im.vector.modular.widgets': 50, }, }, diff --git a/test/editor/deserialize-test.js b/test/editor/deserialize-test.js index 2bd5d7e4c6..112ac7d02b 100644 --- a/test/editor/deserialize-test.js +++ b/test/editor/deserialize-test.js @@ -204,7 +204,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({type: "newline", text: "\n"}); expect(parts[4]).toStrictEqual({type: "plain", text: "```"}); }); - // failing likely because of https://github.com/vector-im/riot-web/issues/10316 + // failing likely because of https://github.com/vector-im/element-web/issues/10316 xit('code block with no trailing text and no newlines', function() { const html = "
0xDEADBEEF
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); diff --git a/test/end-to-end-tests/install.sh b/test/end-to-end-tests/install.sh index 937ef1f5eb..bbe7a24c9b 100755 --- a/test/end-to-end-tests/install.sh +++ b/test/end-to-end-tests/install.sh @@ -2,6 +2,6 @@ # run with PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true sh install.sh if chrome is already installed set -e ./synapse/install.sh -# local testing doesn't need a Riot fetched from master, +# local testing doesn't need a Element fetched from master, # so not installing that by default yarn install diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 2eafad8315..8b23dbcabc 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -24,12 +24,6 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(alice, "Notifications"); alice.log.done(); - alice.log.step(`accepts rebrand toast`); - await acceptToast(alice, "Riot is now Element!"); - let doneButton = await alice.query('.mx_Dialog_primary'); - await doneButton.click(); // also accept the resulting dialog - alice.log.done(); - alice.log.step(`accepts analytics toast`); await acceptToast(alice, "Help us improve Element"); alice.log.done(); @@ -44,12 +38,6 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(bob, "Notifications"); bob.log.done(); - bob.log.step(`accepts rebrand toast`); - await acceptToast(bob, "Riot is now Element!"); - doneButton = await bob.query('.mx_Dialog_primary'); - await doneButton.click(); // also accept the resulting dialog - bob.log.done(); - bob.log.step(`reject analytics toast`); await rejectToast(bob, "Help us improve Element"); bob.log.done(); diff --git a/test/end-to-end-tests/src/usecases/room-settings.js b/test/end-to-end-tests/src/usecases/room-settings.js index fac3fa0855..11e2f52c6e 100644 --- a/test/end-to-end-tests/src/usecases/room-settings.js +++ b/test/end-to-end-tests/src/usecases/room-settings.js @@ -162,7 +162,7 @@ async function changeRoomSettings(session, settings) { if (settings.visibility) { session.log.step(`sets visibility to ${settings.visibility}`); - const radios = await session.queryAll(".mx_RoomSettingsDialog input[type=radio]"); + const radios = await session.queryAll(".mx_RoomSettingsDialog label"); assert.equal(radios.length, 7); const inviteOnly = radios[0]; const publicNoGuests = radios[1]; diff --git a/yarn.lock b/yarn.lock index adf98d6bdd..ec7cc7caf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -111,6 +111,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== + dependencies: + "@babel/types" "^7.11.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" @@ -400,6 +409,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + "@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15" @@ -466,6 +482,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== +"@babel/parser@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.0.tgz#a9d7e11aead25d3b422d17b2c6502c8dddef6a5d" + integrity sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -1213,6 +1234,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" + integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.0" + "@babel/types" "^7.11.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" @@ -1231,6 +1267,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" + integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -4183,11 +4228,6 @@ estree-walker@^0.6.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== -estree-walker@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.9.0.tgz#9116372f09c02fd88fcafb0c04343631012a0aa6" - integrity sha512-12U47o7XHUX329+x3FzNVjCx3SHEzMF0nkDv7r/HnBzX/xNTKxajBk6gyygaxrAFtLj39219oMfbtxv4KpaOiA== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -4530,11 +4570,6 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -flow-parser@0.57.3: - version "0.57.3" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.57.3.tgz#b8d241a1b1cbae043afa7976e39f269988d8fe34" - integrity sha1-uNJBobHLrgQ6+nl2458mmYjY/jQ= - flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -6441,8 +6476,8 @@ mathml-tag-names@^2.0.1: integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "8.0.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c669382e12c8cd1d7d538421dfad4f50812af44d" + version "8.0.1" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/a6fe4cdf1cbf56baeb538f071c27326fe98630d0" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0"