From 44b143c8c3063be7ca2bf24e6cfdb81be9351c75 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 8 May 2021 21:17:05 -0400 Subject: [PATCH 01/94] Match requested avatar size to displayed size Reduces the blurriness of avatars in the EventTilePreview. Signed-off-by: Robin Townsend --- src/components/views/elements/EventTilePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index b15fbbed2b..95f9a97058 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -61,7 +61,7 @@ interface IState { message: string; } -const AVATAR_SIZE = 32; +const AVATAR_SIZE = 30; @replaceableComponent("views.elements.EventTilePreview") export default class EventTilePreview extends React.Component { From e46bc931781095447a1929938a5cb5bdbdb7de4d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 8 May 2021 21:22:31 -0400 Subject: [PATCH 02/94] Fall back to MXID when no display name is present MemberAvatar requires a display name, or else it refuses to render. Signed-off-by: Robin Townsend --- src/components/views/elements/EventTilePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 95f9a97058..6d2ea687de 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -101,7 +101,7 @@ export default class EventTilePreview extends React.Component { // Fake it more event.sender = { - name: this.props.displayName, + name: this.props.displayName || this.props.userId, userId: this.props.userId, getAvatarUrl: (..._) => { return Avatar.avatarUrlForUser( From 42ffc5c9e8ffd1673f14268a18c83bed31378984 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 May 2021 18:08:43 +0100 Subject: [PATCH 03/94] Add support to keyboard shortcuts dialog for [digits] --- src/accessibility/KeyboardShortcuts.tsx | 3 +++ src/i18n/strings/en_EN.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index 2a3e576e31..1cd5408210 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -57,6 +57,8 @@ export enum Modifiers { // Meta-modifier: isMac ? CMD : CONTROL export const CMD_OR_CTRL = isMac ? Modifiers.COMMAND : Modifiers.CONTROL; +// Meta-key representing the digits [0-9] often found at the top of standard keyboard layouts +export const DIGITS = "digits"; interface IKeybind { modifiers?: Modifiers[]; @@ -319,6 +321,7 @@ const alternateKeyName: Record = { [Key.SPACE]: _td("Space"), [Key.HOME]: _td("Home"), [Key.END]: _td("End"), + [DIGITS]: _td("[number]"), }; const keyIcon: Record = { [Key.ARROW_UP]: "↑", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7ceb039822..251c70e241 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2973,5 +2973,6 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "[number]": "[number]" } From 63ae84a72ed441853bb0c95857afc755eefe8486 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 May 2021 18:23:04 +0100 Subject: [PATCH 04/94] Wire space switch shortcut via dispatcher to prevent app load explosion due to skinning --- src/dispatcher/actions.ts | 5 ++++ src/dispatcher/payloads/SwitchSpacePayload.ts | 27 +++++++++++++++++++ src/stores/SpaceStore.tsx | 7 +++++ 3 files changed, 39 insertions(+) create mode 100644 src/dispatcher/payloads/SwitchSpacePayload.ts diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index cd32c3743f..79e1edeee9 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -138,4 +138,9 @@ export enum Action { * Fired when an upload is cancelled by the user. Should be used with UploadCanceledPayload. */ UploadCanceled = "upload_canceled", + + /** + * Switches space. Should be used with SwitchSpacePayload. + */ + SwitchSpace = "switch_space", } diff --git a/src/dispatcher/payloads/SwitchSpacePayload.ts b/src/dispatcher/payloads/SwitchSpacePayload.ts new file mode 100644 index 0000000000..04eb744334 --- /dev/null +++ b/src/dispatcher/payloads/SwitchSpacePayload.ts @@ -0,0 +1,27 @@ +/* +Copyright 2021 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 { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +export interface SwitchSpacePayload extends ActionPayload { + action: Action.SwitchSpace; + + /** + * The number of the space to switch to, 1-indexed, 0 is Home. + */ + num: number; +} diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 40997d30a8..0d09357fc1 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -33,6 +33,7 @@ import {EnhancedMap, mapDiff} from "../utils/maps"; import {setHasDiff} from "../utils/sets"; import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory"; import RoomViewStore from "./RoomViewStore"; +import {Action} from "../dispatcher/actions"; interface IState {} @@ -565,6 +566,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.setActiveSpace(null, false); } break; + case Action.SwitchSpace: + if (payload.num === 0) { + this.setActiveSpace(null); + } else if (this.spacePanelSpaces.length >= payload.num) { + this.setActiveSpace(this.spacePanelSpaces[payload.num - 1]); + } } } From 0b7d3f007aece9a15d53e53f0f953fa203da8457 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 1 Jun 2021 17:30:57 +0100 Subject: [PATCH 05/94] Remove react-beautiful-dnd --- package.json | 1 - src/components/structures/GroupFilterPanel.js | 32 ++--- src/components/structures/LoggedInView.tsx | 65 ++------- src/components/views/elements/DNDTagTile.js | 32 ++--- .../views/groups/GroupPublicityToggle.js | 4 +- src/components/views/groups/GroupTile.js | 44 +----- test/components/views/rooms/RoomList-test.js | 5 +- yarn.lock | 130 +----------------- 8 files changed, 36 insertions(+), 277 deletions(-) diff --git a/package.json b/package.json index 13047b69cf..270c86ddba 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,6 @@ "qs": "^6.9.6", "re-resizable": "^6.9.0", "react": "^16.14.0", - "react-beautiful-dnd": "^4.0.1", "react-dom": "^16.14.0", "react-focus-lock": "^2.5.0", "react-transition-group": "^4.4.1", diff --git a/src/components/structures/GroupFilterPanel.js b/src/components/structures/GroupFilterPanel.js index 2ff91e4976..f1c28d588a 100644 --- a/src/components/structures/GroupFilterPanel.js +++ b/src/components/structures/GroupFilterPanel.js @@ -24,7 +24,6 @@ import * as sdk from '../../index'; import dis from '../../dispatcher/dispatcher'; import { _t } from '../../languageHandler'; -import { Droppable } from 'react-beautiful-dnd'; import classNames from 'classnames'; import MatrixClientContext from "../../contexts/MatrixClientContext"; import AutoHideScrollbar from "./AutoHideScrollbar"; @@ -83,7 +82,7 @@ class GroupFilterPanel extends React.Component { } }; - onMouseDown = e => { + onClick = e => { // only dispatch if its not a no-op if (this.state.selectedTags.length > 0) { dis.dispatch({action: 'deselect_tags'}); @@ -151,28 +150,15 @@ class GroupFilterPanel extends React.Component { return
- - { (provided, snapshot) => ( -
- { this.renderGlobalIcon() } - { tags } -
- {createButton} -
- { provided.placeholder } -
- ) } -
+
+ { this.renderGlobalIcon() } + { tags } +
+ { createButton } +
+
; } diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index ad5c759f0d..f5df99d8c9 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -19,7 +19,6 @@ limitations under the License. import * as React from 'react'; import * as PropTypes from 'prop-types'; import { MatrixClient } from 'matrix-js-sdk/src/client'; -import { DragDropContext } from 'react-beautiful-dnd'; import {Key} from '../../Keyboard'; import PageTypes from '../../PageTypes'; @@ -569,50 +568,6 @@ class LoggedInView extends React.Component { } }; - _onDragEnd = (result) => { - // Dragged to an invalid destination, not onto a droppable - if (!result.destination) { - return; - } - - const dest = result.destination.droppableId; - - if (dest === 'tag-panel-droppable') { - // Could be "GroupTile +groupId:domain" - const draggableId = result.draggableId.split(' ').pop(); - - // Dispatch synchronously so that the GroupFilterPanel receives an - // optimistic update from GroupFilterOrderStore before the previous - // state is shown. - dis.dispatch(TagOrderActions.moveTag( - this._matrixClient, - draggableId, - result.destination.index, - ), true); - } else if (dest.startsWith('room-sub-list-droppable_')) { - this._onRoomTileEndDrag(result); - } - }; - - _onRoomTileEndDrag = (result) => { - let newTag = result.destination.droppableId.split('_')[1]; - let prevTag = result.source.droppableId.split('_')[1]; - if (newTag === 'undefined') newTag = undefined; - if (prevTag === 'undefined') prevTag = undefined; - - const roomId = result.draggableId.split('_')[1]; - - const oldIndex = result.source.index; - const newIndex = result.destination.index; - - dis.dispatch(RoomListActions.tagRoom( - this._matrixClient, - this._matrixClient.getRoom(roomId), - prevTag, newTag, - oldIndex, newIndex, - ), true); - }; - render() { const RoomView = sdk.getComponent('structures.RoomView'); const UserView = sdk.getComponent('structures.UserView'); @@ -679,17 +634,15 @@ class LoggedInView extends React.Component { aria-hidden={this.props.hideToSRUsers} > - -
- { SettingsStore.getValue("feature_spaces") ? : null } - - - { pageElement } -
-
+
+ { SettingsStore.getValue("feature_spaces") ? : null } + + + { pageElement } +
diff --git a/src/components/views/elements/DNDTagTile.js b/src/components/views/elements/DNDTagTile.js index 67572d4508..eaaa0f183b 100644 --- a/src/components/views/elements/DNDTagTile.js +++ b/src/components/views/elements/DNDTagTile.js @@ -18,7 +18,6 @@ limitations under the License. import TagTile from './TagTile'; import React from 'react'; -import { Draggable } from 'react-beautiful-dnd'; import { ContextMenu, toRightOf, useContextMenu } from "../../structures/ContextMenu"; import * as sdk from '../../../index'; @@ -35,28 +34,13 @@ export default function DNDTagTile(props) { ); } - return
- - {(provided, snapshot) => ( -
- -
- )} -
+ return <> + {contextMenu} -
; + ; } diff --git a/src/components/views/groups/GroupPublicityToggle.js b/src/components/views/groups/GroupPublicityToggle.js index c06d827550..6bef141cb8 100644 --- a/src/components/views/groups/GroupPublicityToggle.js +++ b/src/components/views/groups/GroupPublicityToggle.js @@ -66,9 +66,7 @@ export default class GroupPublicityToggle extends React.Component { render() { const GroupTile = sdk.getComponent('groups.GroupTile'); return
- + diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js index 42a977fb79..ce42662462 100644 --- a/src/components/views/groups/GroupTile.js +++ b/src/components/views/groups/GroupTile.js @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import { Draggable, Droppable } from 'react-beautiful-dnd'; import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; import FlairStore from '../../../stores/FlairStore'; @@ -24,8 +23,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {replaceableComponent} from "../../../utils/replaceableComponent"; import {mediaFromMxc} from "../../../customisations/Media"; -function nop() {} - @replaceableComponent("views.groups.GroupTile") class GroupTile extends React.Component { static propTypes = { @@ -34,7 +31,6 @@ class GroupTile extends React.Component { showDescription: PropTypes.bool, // Height of the group avatar in pixels avatarHeight: PropTypes.number, - draggable: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -42,7 +38,6 @@ class GroupTile extends React.Component { static defaultProps = { showDescription: true, avatarHeight: 50, - draggable: true, }; state = { @@ -57,7 +52,7 @@ class GroupTile extends React.Component { }); } - onMouseDown = e => { + onClick = e => { e.preventDefault(); dis.dispatch({ action: 'view_group', @@ -78,7 +73,7 @@ class GroupTile extends React.Component { ? mediaFromMxc(profile.avatarUrl).getSquareThumbnailHttp(avatarHeight) : null; - let avatarElement = ( + const avatarElement = (
); - if (this.props.draggable) { - const avatarClone = avatarElement; - avatarElement = ( - - { (droppableProvided, droppableSnapshot) => ( -
- - { (provided, snapshot) => ( -
-
- {avatarClone} -
- { /* Instead of a blank placeholder, use a copy of the avatar itself. */ } - { provided.placeholder ? avatarClone :
} -
- ) } - -
- ) } - - ); - } - // XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273 - // instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6156 - return + return { avatarElement }
{ name }
diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index bfb8e1afd4..6aad6a90fd 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -6,7 +6,6 @@ import * as TestUtils from '../../../test-utils'; import {MatrixClientPeg} from '../../../../src/MatrixClientPeg'; import sdk from '../../../skinned-sdk'; -import { DragDropContext } from 'react-beautiful-dnd'; import dis from '../../../../src/dispatcher/dispatcher'; import DMRoomMap from '../../../../src/utils/DMRoomMap'; @@ -68,9 +67,7 @@ describe('RoomList', () => { const RoomList = sdk.getComponent('views.rooms.RoomList'); const WrappedRoomList = TestUtils.wrapInMatrixClientContext(RoomList); root = ReactDOM.render( - - {}} /> - , + {}} />, parentDiv, ); ReactTestUtils.findRenderedComponentWithType(root, RoomList); diff --git a/yarn.lock b/yarn.lock index 0ff235a660..2c84237730 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1017,7 +1017,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== @@ -2114,14 +2114,6 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -2645,11 +2637,6 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4215,13 +4202,6 @@ highlight.js@^10.5.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" integrity sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw== -hoist-non-react-statics@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -4430,13 +4410,6 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -5556,11 +5529,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.2.1: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7" - integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA== - lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" @@ -5581,7 +5549,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.2.1: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5749,11 +5717,6 @@ mdurl@~1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= -memoize-one@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17" - integrity sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA== - meow@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" @@ -6374,11 +6337,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -6597,7 +6555,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6674,12 +6632,7 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -raf-schd@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-2.1.2.tgz#ec622b5167f2912089f054dc03ebd5bcf33c8f62" - integrity sha512-Orl0IEvMtUCgPddgSxtxreK77UiQz4nPYJy9RggVzu4mKsZkQWiAaG1y9HlYWdvm9xtN348xRaT37qkvL/+A+g== - -raf@^3.1.0, raf@^3.4.1: +raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -6706,22 +6659,6 @@ re-resizable@^6.9.0: dependencies: fast-memoize "^2.5.1" -react-beautiful-dnd@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-4.0.1.tgz#3b0a49bf6be75af351176c904f012611dd292b81" - integrity sha512-d73RMu4QOFCyjUELLWFyY/EuclnfqulI9pECx+2gIuJvV0ycf1uR88o+1x0RSB9ILD70inHMzCBKNkWVbbt+vA== - dependencies: - babel-runtime "^6.26.0" - invariant "^2.2.2" - memoize-one "^3.0.1" - prop-types "^15.6.0" - raf-schd "^2.1.0" - react-motion "^0.5.2" - react-redux "^5.0.6" - redux "^3.7.2" - redux-thunk "^2.2.0" - reselect "^3.0.1" - react-clientside-effect@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.3.tgz#95c95f520addfb71743608b990bfe01eb002012b" @@ -6751,7 +6688,7 @@ react-focus-lock@^2.5.0: use-callback-ref "^1.2.1" use-sidecar "^1.0.1" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6761,33 +6698,6 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== -react-lifecycles-compat@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== - -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== - dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - -react-redux@^5.0.6: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== - dependencies: - "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" - react-test-renderer@^16.0.0-0, react-test-renderer@^16.14.0: version "16.14.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae" @@ -6908,21 +6818,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux-thunk@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" - integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== - -redux@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" - integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== - dependencies: - lodash "^4.2.1" - lodash-es "^4.2.1" - loose-envify "^1.1.0" - symbol-observable "^1.0.3" - reflect.ownkeys@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" @@ -6940,11 +6835,6 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -7102,11 +6992,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -reselect@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" - integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= - resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" @@ -7813,11 +7698,6 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= -symbol-observable@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" From bc3c759feb2d3f062d0dbc9489e5741fa7d8af13 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 2 Jun 2021 11:33:25 +0100 Subject: [PATCH 06/94] Add temporary mechanism for managing communities without dnd --- .../context_menus/_TagTileContextMenu.scss | 9 ++++ src/components/structures/MyGroups.js | 3 +- .../views/context_menus/TagTileContextMenu.js | 49 ++++++++++++++----- src/components/views/elements/DNDTagTile.js | 2 +- src/components/views/groups/GroupTile.js | 23 +++++++++ src/i18n/strings/en_EN.json | 4 +- 6 files changed, 74 insertions(+), 16 deletions(-) diff --git a/res/css/views/context_menus/_TagTileContextMenu.scss b/res/css/views/context_menus/_TagTileContextMenu.scss index 8929c8906e..d707f4ce7c 100644 --- a/res/css/views/context_menus/_TagTileContextMenu.scss +++ b/res/css/views/context_menus/_TagTileContextMenu.scss @@ -38,6 +38,15 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/view-community.svg'); } +.mx_TagTileContextMenu_moveUp::before { + transform: rotate(180deg); + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); +} + +.mx_TagTileContextMenu_moveDown::before { + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); +} + .mx_TagTileContextMenu_hideCommunity::before { mask-image: url('$(res)/img/element-icons/hide.svg'); } diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 1fab6c4348..d0a2fbff41 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -82,8 +82,7 @@ export default class MyGroups extends React.Component {

{ _t( - "To set up a filter, drag a community avatar over to the filter panel on " + - "the far left hand side of the screen. You can click on an avatar in the " + + "You can click on an avatar in the " + "filter panel at any time to see only the rooms and people associated " + "with that community.", ) } diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js index 8dea62690c..4e381643ba 100644 --- a/src/components/views/context_menus/TagTileContextMenu.js +++ b/src/components/views/context_menus/TagTileContextMenu.js @@ -23,45 +23,70 @@ import TagOrderActions from '../../../actions/TagOrderActions'; import {MenuItem} from "../../structures/ContextMenu"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {replaceableComponent} from "../../../utils/replaceableComponent"; +import GroupFilterOrderStore from "../../../stores/GroupFilterOrderStore"; @replaceableComponent("views.context_menus.TagTileContextMenu") export default class TagTileContextMenu extends React.Component { static propTypes = { tag: PropTypes.string.isRequired, + index: PropTypes.number.isRequired, /* callback called when the menu is dismissed */ onFinished: PropTypes.func.isRequired, }; static contextType = MatrixClientContext; - constructor() { - super(); - - this._onViewCommunityClick = this._onViewCommunityClick.bind(this); - this._onRemoveClick = this._onRemoveClick.bind(this); - } - - _onViewCommunityClick() { + _onViewCommunityClick = () => { dis.dispatch({ action: 'view_group', group_id: this.props.tag, }); this.props.onFinished(); - } + }; - _onRemoveClick() { + _onRemoveClick = () => { dis.dispatch(TagOrderActions.removeTag(this.context, this.props.tag)); this.props.onFinished(); - } + }; + + _onMoveUp = () => { + dis.dispatch(TagOrderActions.moveTag(this.context, this.props.tag, this.props.index - 1)); + this.props.onFinished(); + }; + + _onMoveDown = () => { + dis.dispatch(TagOrderActions.moveTag(this.context, this.props.tag, this.props.index + 1)); + this.props.onFinished(); + }; render() { + let moveUp; + let moveDown; + if (this.props.index > 0) { + moveUp = ( + + { _t("Move up") } + + ); + } + if (this.props.index < (GroupFilterOrderStore.getOrderedTags() || []).length - 1) { + moveDown = ( + + { _t("Move down") } + + ); + } + return

{ _t('View Community') } + { (moveUp || moveDown) ?
: null } + { moveUp } + { moveDown }
- { _t('Hide') } + { _t("Unpin") }
; } diff --git a/src/components/views/elements/DNDTagTile.js b/src/components/views/elements/DNDTagTile.js index eaaa0f183b..2e88d37882 100644 --- a/src/components/views/elements/DNDTagTile.js +++ b/src/components/views/elements/DNDTagTile.js @@ -30,7 +30,7 @@ export default function DNDTagTile(props) { const TagTileContextMenu = sdk.getComponent('context_menus.TagTileContextMenu'); contextMenu = ( - + ); } diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js index ce42662462..dd8366bbe0 100644 --- a/src/components/views/groups/GroupTile.js +++ b/src/components/views/groups/GroupTile.js @@ -22,6 +22,9 @@ import FlairStore from '../../../stores/FlairStore'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {replaceableComponent} from "../../../utils/replaceableComponent"; import {mediaFromMxc} from "../../../customisations/Media"; +import { _t } from "../../../languageHandler"; +import TagOrderActions from "../../../actions/TagOrderActions"; +import GroupFilterOrderStore from "../../../stores/GroupFilterOrderStore"; @replaceableComponent("views.groups.GroupTile") class GroupTile extends React.Component { @@ -60,6 +63,18 @@ class GroupTile extends React.Component { }); }; + onPinClick = e => { + e.preventDefault(); + e.stopPropagation(); + dis.dispatch(TagOrderActions.moveTag(this.context, this.props.groupId, 0)); + }; + + onUnpinClick = e => { + e.preventDefault(); + e.stopPropagation(); + dis.dispatch(TagOrderActions.removeTag(this.context, this.props.groupId)); + }; + render() { const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); @@ -90,6 +105,14 @@ class GroupTile extends React.Component {
{ name }
{ descElement }
{ this.props.groupId }
+ { !(GroupFilterOrderStore.getOrderedTags() || []).includes(this.props.groupId) + ? + { _t("Pin") } + + : + { _t("Unpin") } + + }
; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3d6fcb8643..85647a17e5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2477,6 +2477,8 @@ "Update status": "Update status", "Set status": "Set status", "Set a new status...": "Set a new status...", + "Move up": "Move up", + "Move down": "Move down", "View Community": "View Community", "Unable to start audio streaming.": "Unable to start audio streaming.", "Failed to start livestream": "Failed to start livestream", @@ -2623,7 +2625,7 @@ "%(count)s messages deleted.|one": "%(count)s message deleted.", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your %(brand)s experience!": "Did you know: you can use communities to filter your %(brand)s experience!", - "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", + "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", "Error whilst fetching joined communities": "Error whilst fetching joined communities", "Create a new community": "Create a new community", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.", From 35948374e91d4cf6e3110de4218d043be9ff4db0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 2 Jun 2021 11:56:49 +0100 Subject: [PATCH 07/94] remove unused imports --- src/components/structures/LoggedInView.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index f5df99d8c9..388616c55e 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -29,8 +29,6 @@ import dis from '../../dispatcher/dispatcher'; import { IMatrixClientCreds } from '../../MatrixClientPeg'; import SettingsStore from "../../settings/SettingsStore"; -import TagOrderActions from '../../actions/TagOrderActions'; -import RoomListActions from '../../actions/RoomListActions'; import ResizeHandle from '../views/elements/ResizeHandle'; import {Resizer, CollapseDistributor} from '../../resizer'; import MatrixClientContext from "../../contexts/MatrixClientContext"; From 079a5c10ad8191d6d3c357400c948253b0738997 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 2 Jun 2021 16:43:38 +0100 Subject: [PATCH 08/94] Respect space ordering field in m.tag for top level spaces --- .../structures/SpaceRoomDirectory.tsx | 4 +-- src/stores/SpaceStore.tsx | 33 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/components/structures/SpaceRoomDirectory.tsx b/src/components/structures/SpaceRoomDirectory.tsx index 8d59fe6c68..2b4fb24c1b 100644 --- a/src/components/structures/SpaceRoomDirectory.tsx +++ b/src/components/structures/SpaceRoomDirectory.tsx @@ -39,7 +39,7 @@ import {mediaFromMxc} from "../../customisations/Media"; import InfoTooltip from "../views/elements/InfoTooltip"; import TextWithTooltip from "../views/elements/TextWithTooltip"; import {useStateToggle} from "../../hooks/useStateToggle"; -import {getOrder} from "../../stores/SpaceStore"; +import {getChildOrder} from "../../stores/SpaceStore"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import {linkifyElement} from "../../HtmlUtils"; @@ -286,7 +286,7 @@ export const HierarchyLevel = ({ const children = Array.from(relations.get(spaceId)?.values() || []); const sortedChildren = sortBy(children, ev => { // XXX: Space Summary API doesn't give the child origin_server_ts but once it does we should use it for sorting - return getOrder(ev.content.order, null, ev.state_key); + return getChildOrder(ev.content.order, null, ev.state_key); }); const [subspaces, childRooms] = sortedChildren.reduce((result, ev: ISpaceSummaryEvent) => { const roomId = ev.state_key; diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 40997d30a8..1333fc5d37 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -33,6 +33,7 @@ import {EnhancedMap, mapDiff} from "../utils/maps"; import {setHasDiff} from "../utils/sets"; import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory"; import RoomViewStore from "./RoomViewStore"; +import { arrayHasOrderChange } from "../utils/arrays"; interface IState {} @@ -60,8 +61,16 @@ const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, }, [[], []]); }; +const SpaceTagOrderingField = "org.matrix.mscXXXX.space"; + +const getSpaceTagOrdering = (space: Room): number | undefined => { + return space?.getAccountData(EventType.Tag)?.getContent()?.tags?.[SpaceTagOrderingField]?.order; +}; + +const sortRootSpaces = (spaces: Room[]): Room[] => sortBy(spaces, [getSpaceTagOrdering, "roomId"]); + // For sorting space children using a validated `order`, `m.room.create`'s `origin_server_ts`, `room_id` -export const getOrder = (order: string, creationTs: number, roomId: string): Array>> => { +export const getChildOrder = (order: string, creationTs: number, roomId: string): Array>> => { let validatedOrder: string = null; if (typeof order === "string" && Array.from(order).every((c: string) => { @@ -214,7 +223,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { const roomId = ev.getStateKey(); const childRoom = this.matrixClient?.getRoom(roomId); const createTs = childRoom?.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs(); - return getOrder(ev.getContent().order, createTs, roomId); + return getChildOrder(ev.getContent().order, createTs, roomId); }).map(ev => { return this.matrixClient.getRoom(ev.getStateKey()); }).filter(room => { @@ -326,7 +335,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // rootSpaces.push(space); // }); - this.rootSpaces = rootSpaces; + this.rootSpaces = sortRootSpaces(rootSpaces); this.parentMap = backrefs; // if the currently selected space no longer exists, remove its selection @@ -338,7 +347,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); // build initial state of invited spaces as we would have missed the emitted events about the room at launch - this._invitedSpaces = new Set(invitedSpaces); + this._invitedSpaces = new Set(sortRootSpaces(invitedSpaces)); this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces); }, 100, {trailing: true, leading: true}); @@ -472,6 +481,20 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } }; + private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => { + if (!room.isSpaceRoom() || ev.getType() !== EventType.Tag) return; + + const order = ev.getContent()?.tags?.[SpaceTagOrderingField]?.order; + const lastOrder = lastEv?.getContent()?.tags?.[SpaceTagOrderingField]?.order; + if (order !== lastOrder) { + const rootSpaces = sortRootSpaces(this.rootSpaces); + if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) { + this.rootSpaces = rootSpaces; + this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); + } + } + }; + private onRoomState = (ev: MatrixEvent) => { const room = this.matrixClient.getRoom(ev.getRoomId()); if (!room) return; @@ -516,6 +539,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { if (this.matrixClient) { this.matrixClient.removeListener("Room", this.onRoom); this.matrixClient.removeListener("Room.myMembership", this.onRoom); + this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); this.matrixClient.removeListener("RoomState.events", this.onRoomState); } await this.reset(); @@ -525,6 +549,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { if (!SettingsStore.getValue("feature_spaces")) return; this.matrixClient.on("Room", this.onRoom); this.matrixClient.on("Room.myMembership", this.onRoom); + this.matrixClient.on("Room.accountData", this.onRoomAccountData); this.matrixClient.on("RoomState.events", this.onRoomState); await this.onSpaceUpdate(); // trigger an initial update From 3f12b7280d801ac505caec71d2a2c095ab68d3c9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Jun 2021 08:31:06 +0100 Subject: [PATCH 09/94] Make AutoHideScrollbar pass through all unknown props --- .../structures/AutoHideScrollbar.tsx | 18 +++++++++++------- .../structures/IndicatorScrollbar.js | 11 +++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/structures/AutoHideScrollbar.tsx b/src/components/structures/AutoHideScrollbar.tsx index 66f998b616..e5fa124fed 100644 --- a/src/components/structures/AutoHideScrollbar.tsx +++ b/src/components/structures/AutoHideScrollbar.tsx @@ -15,9 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, {HTMLAttributes} from "react"; -interface IProps { +interface IProps extends HTMLAttributes { className?: string; onScroll?: () => void; onWheel?: () => void; @@ -52,14 +52,18 @@ export default class AutoHideScrollbar extends React.Component { } public render() { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { className, onScroll, onWheel, style, tabIndex, wrappedRef, children, ...otherProps } = this.props; + return (
- { this.props.children } + { children }
); } } diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index 51a3b287f0..25dcaeed39 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -185,21 +185,24 @@ export default class IndicatorScrollbar extends React.Component { }; render() { + // eslint-disable-next-line no-unused-vars + const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props; + const leftIndicatorStyle = {left: this.state.leftIndicatorOffset}; const rightIndicatorStyle = {right: this.state.rightIndicatorOffset}; - const leftOverflowIndicator = this.props.trackHorizontalOverflow + const leftOverflowIndicator = trackHorizontalOverflow ?
: null; - const rightOverflowIndicator = this.props.trackHorizontalOverflow + const rightOverflowIndicator = trackHorizontalOverflow ?
: null; return ( { leftOverflowIndicator } - { this.props.children } + { children } { rightOverflowIndicator } ); } From e334ce81920723832c9260b0f009df88805e22a1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Jun 2021 08:32:36 +0100 Subject: [PATCH 10/94] First cut of space panel drag-and-drop ordering --- package.json | 2 + res/css/structures/_SpacePanel.scss | 7 +- src/components/views/spaces/SpacePanel.tsx | 136 +++++++++++------- .../views/spaces/SpaceTreeLevel.tsx | 27 ++-- src/stores/SpaceStore.tsx | 71 +++++++-- yarn.lock | 100 ++++++++++++- 6 files changed, 263 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index 270c86ddba..2d2506e1df 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "qs": "^6.9.6", "re-resizable": "^6.9.0", "react": "^16.14.0", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^16.14.0", "react-focus-lock": "^2.5.0", "react-transition-group": "^4.4.1", @@ -135,6 +136,7 @@ "@types/parse5": "^6.0.0", "@types/qrcode": "^1.3.5", "@types/react": "^16.9", + "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "^16.9.10", "@types/react-transition-group": "^4.4.0", "@types/sanitize-html": "^2.3.1", diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index c433ccf275..e64057d16c 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -31,7 +31,6 @@ $activeBorderColor: $secondary-fg-color; // Create another flexbox so the Panel fills the container display: flex; flex-direction: column; - overflow-y: auto; .mx_SpacePanel_spaceTreeWrapper { flex: 1; @@ -69,6 +68,12 @@ $activeBorderColor: $secondary-fg-color; cursor: pointer; } + .mx_SpaceItem_dragging { + .mx_SpaceButton_toggleCollapse { + visibility: hidden; + } + } + .mx_SpaceTreeLevel { display: flex; flex-direction: column; diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index eb63b21f0e..27f097e9d4 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -15,8 +15,9 @@ limitations under the License. */ import React, { useEffect, useState } from "react"; +import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import classNames from "classnames"; -import {Room} from "matrix-js-sdk/src/models/room"; +import { Room } from "matrix-js-sdk/src/models/room"; import {_t} from "../../../languageHandler"; import RoomAvatar from "../avatars/RoomAvatar"; @@ -204,58 +205,89 @@ const SpacePanel = () => { }; const activeSpaces = activeSpace ? [activeSpace] : []; - const expandCollapseButtonTitle = isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel"); - // TODO drag and drop for re-arranging order - return - {({onKeyDownHandler}) => ( -
    - -
    - SpaceStore.instance.setActiveSpace(null)} - selected={!activeSpace} - tooltip={_t("All rooms")} - notificationState={RoomNotificationStateStore.instance.globalState} - isNarrow={isPanelCollapsed} + return ( + { + if (!result.destination) return; // dropped outside the list + SpaceStore.instance.moveRootSpace(result.source.index, result.destination.index); + }}> + + {({onKeyDownHandler}) => ( +
      + + {(provided, snapshot) => ( + +
      + SpaceStore.instance.setActiveSpace(null)} + selected={!activeSpace} + tooltip={_t("All rooms")} + notificationState={RoomNotificationStateStore.instance.globalState} + isNarrow={isPanelCollapsed} + /> + { invites.map(s => ( + setPanelCollapsed(false)} + /> + )) } + { spaces.map((s, i) => ( + + {(provided, snapshot) => ( + setPanelCollapsed(false)} + /> + )} + + )) } + { provided.placeholder } +
      + { + if (!isPanelCollapsed) setPanelCollapsed(true); + openMenu(); + }} + isNarrow={isPanelCollapsed} + /> +
      + )} +
      + setPanelCollapsed(!isPanelCollapsed)} + title={isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel")} /> - { invites.map(s => setPanelCollapsed(false)} - />) } - { spaces.map(s => setPanelCollapsed(false)} - />) } -
    - { - if (!isPanelCollapsed) setPanelCollapsed(true); - openMenu(); - }} - isNarrow={isPanelCollapsed} - /> -
    - setPanelCollapsed(!isPanelCollapsed)} - title={expandCollapseButtonTitle} - /> - { contextMenu } -
- )} -
+ { contextMenu } + + )} + + + ); }; export default SpacePanel; diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index f34baf256b..7ac863b239 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, {InputHTMLAttributes, LegacyRef} from "react"; import classNames from "classnames"; import {Room} from "matrix-js-sdk/src/models/room"; @@ -49,13 +49,14 @@ import {EventType} from "matrix-js-sdk/src/@types/event"; import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState"; import {NotificationColor} from "../../../stores/notifications/NotificationColor"; -interface IItemProps { +interface IItemProps extends InputHTMLAttributes { space?: Room; activeSpaces: Room[]; isNested?: boolean; isPanelCollapsed?: boolean; onExpand?: Function; parents?: Set; + innerRef?: LegacyRef; } interface IItemState { @@ -300,18 +301,18 @@ export class SpaceItem extends React.PureComponent { } render() { - const {space, activeSpaces, isNested} = this.props; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { space, activeSpaces, isNested, isPanelCollapsed, onExpand, parents, innerRef, + ...otherProps } = this.props; - const forceCollapsed = this.props.isPanelCollapsed; - const isNarrow = this.props.isPanelCollapsed; - const collapsed = this.state.collapsed || forceCollapsed; + const collapsed = this.state.collapsed || isPanelCollapsed; const childSpaces = SpaceStore.instance.getChildSpaces(space.roomId) - .filter(s => !this.props.parents?.has(s.roomId)); + .filter(s => !parents?.has(s.roomId)); const isActive = activeSpaces.includes(space); - const itemClasses = classNames({ + const itemClasses = classNames(this.props.className, { "mx_SpaceItem": true, - "mx_SpaceItem_narrow": isNarrow, + "mx_SpaceItem_narrow": isPanelCollapsed, "collapsed": collapsed, "hasSubSpaces": childSpaces && childSpaces.length, }); @@ -320,7 +321,7 @@ export class SpaceItem extends React.PureComponent { const classes = classNames("mx_SpaceButton", { mx_SpaceButton_active: isActive, mx_SpaceButton_hasMenuOpen: !!this.state.contextMenuPosition, - mx_SpaceButton_narrow: isNarrow, + mx_SpaceButton_narrow: isPanelCollapsed, mx_SpaceButton_invite: isInvite, }); const notificationState = isInvite @@ -333,7 +334,7 @@ export class SpaceItem extends React.PureComponent { spaces={childSpaces} activeSpaces={activeSpaces} isNested={true} - parents={new Set(this.props.parents).add(this.props.space.roomId)} + parents={new Set(parents).add(space.roomId)} />; } @@ -353,7 +354,7 @@ export class SpaceItem extends React.PureComponent { /> : null; let button; - if (isNarrow) { + if (isPanelCollapsed) { button = ( { } return ( -
  • +
  • { button } { childItems }
  • diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 1333fc5d37..9ef961ce2d 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -63,12 +63,6 @@ const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, const SpaceTagOrderingField = "org.matrix.mscXXXX.space"; -const getSpaceTagOrdering = (space: Room): number | undefined => { - return space?.getAccountData(EventType.Tag)?.getContent()?.tags?.[SpaceTagOrderingField]?.order; -}; - -const sortRootSpaces = (spaces: Room[]): Room[] => sortBy(spaces, [getSpaceTagOrdering, "roomId"]); - // For sorting space children using a validated `order`, `m.room.create`'s `origin_server_ts`, `room_id` export const getChildOrder = (order: string, creationTs: number, roomId: string): Array>> => { let validatedOrder: string = null; @@ -104,6 +98,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private _activeSpace?: Room = null; private _suggestedRooms: ISuggestedRoom[] = []; private _invitedSpaces = new Set(); + private spaceOrderLocalEchoMap = new Map(); public get invitedSpaces(): Room[] { return Array.from(this._invitedSpaces); @@ -335,7 +330,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // rootSpaces.push(space); // }); - this.rootSpaces = sortRootSpaces(rootSpaces); + this.rootSpaces = this.sortRootSpaces(rootSpaces); this.parentMap = backrefs; // if the currently selected space no longer exists, remove its selection @@ -347,7 +342,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); // build initial state of invited spaces as we would have missed the emitted events about the room at launch - this._invitedSpaces = new Set(sortRootSpaces(invitedSpaces)); + this._invitedSpaces = new Set(this.sortRootSpaces(invitedSpaces)); this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces); }, 100, {trailing: true, leading: true}); @@ -484,17 +479,22 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => { if (!room.isSpaceRoom() || ev.getType() !== EventType.Tag) return; + this.spaceOrderLocalEchoMap.delete(room.roomId); // clear any local echo const order = ev.getContent()?.tags?.[SpaceTagOrderingField]?.order; const lastOrder = lastEv?.getContent()?.tags?.[SpaceTagOrderingField]?.order; if (order !== lastOrder) { - const rootSpaces = sortRootSpaces(this.rootSpaces); - if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) { - this.rootSpaces = rootSpaces; - this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); - } + this.notifyIfOrderChanged(); } }; + private notifyIfOrderChanged(): void { + const rootSpaces = this.sortRootSpaces(this.rootSpaces); + if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) { + this.rootSpaces = rootSpaces; + this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); + } + } + private onRoomState = (ev: MatrixEvent) => { const room = this.matrixClient.getRoom(ev.getRoomId()); if (!room) return; @@ -624,6 +624,51 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } childSpaces.forEach(s => this.traverseSpace(s.roomId, fn, includeRooms, newPath)); } + + private getSpaceTagOrdering = (space: Room): number | undefined => { + if (this.spaceOrderLocalEchoMap.has(space.roomId)) return this.spaceOrderLocalEchoMap.get(space.roomId); + return space.tags?.[SpaceTagOrderingField]?.order; + }; + + private sortRootSpaces(spaces: Room[]): Room[] { + return sortBy(spaces, [this.getSpaceTagOrdering, "roomId"]); + } + + public moveRootSpace(fromIndex: number, toIndex: number): void { + if ( + fromIndex < 0 || toIndex < 0 || + fromIndex > this.rootSpaces.length || toIndex > this.rootSpaces.length || + fromIndex === toIndex + ) { + return; + } + const space = this.rootSpaces[fromIndex]; + const orders = this.rootSpaces.map(this.getSpaceTagOrdering); + + let prevOrder = orders[toIndex - 1]; + let nextOrder = orders[toIndex]; // accounts for downwards displacement of existing inhabitant of this index + + if (prevOrder === undefined && nextOrder === undefined) { + // TODO WHAT A PAIN + } + + prevOrder = prevOrder || 0.0; + nextOrder = nextOrder || 1.0; + + if (prevOrder !== nextOrder) { + const order = prevOrder + ((nextOrder - prevOrder) / 2); + this.spaceOrderLocalEchoMap.set(space.roomId, order); + this.matrixClient.setRoomAccountData(space.roomId, EventType.Tag, { + tags: { + ...space.tags, + [SpaceTagOrderingField]: { order }, + }, + }); + this.notifyIfOrderChanged(); + } else { + // TODO REBUILD + } + } } export default class SpaceStore { diff --git a/yarn.lock b/yarn.lock index 2c84237730..7e24c220e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1024,6 +1024,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" + integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.3.3": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" @@ -1504,6 +1511,14 @@ dependencies: "@types/node" "*" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -1620,6 +1635,13 @@ dependencies: "@types/node" "*" +"@types/react-beautiful-dnd@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#e60d3d965312fcf1516894af92dc3e9249587db4" + integrity sha512-by80tJ8aTTDXT256Gl+RfLRtFjYbUWOnZuEigJgNsJrSEGxvFe5eY6k3g4VIvf0M/6+xoLgfYWoWonlOo6Wqdg== + dependencies: + "@types/react" "*" + "@types/react-dom@^16.9.10": version "16.9.10" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.10.tgz#4485b0bec3d41f856181b717f45fd7831101156f" @@ -1627,6 +1649,16 @@ dependencies: "@types/react" "^16" +"@types/react-redux@^7.1.16": + version "7.1.16" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21" + integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-transition-group@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" @@ -2696,6 +2728,13 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" +css-box-model@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" + integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== + dependencies: + tiny-invariant "^1.0.6" + css-select@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286" @@ -4202,6 +4241,13 @@ highlight.js@^10.5.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" integrity sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw== +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -5717,6 +5763,11 @@ mdurl@~1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +memoize-one@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + meow@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" @@ -6632,6 +6683,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +raf-schd@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" + integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== + raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -6659,6 +6715,19 @@ re-resizable@^6.9.0: dependencies: fast-memoize "^2.5.1" +react-beautiful-dnd@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d" + integrity sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA== + dependencies: + "@babel/runtime" "^7.9.2" + css-box-model "^1.2.0" + memoize-one "^5.1.1" + raf-schd "^4.0.2" + react-redux "^7.2.0" + redux "^4.0.4" + use-memo-one "^1.1.1" + react-clientside-effect@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.3.tgz#95c95f520addfb71743608b990bfe01eb002012b" @@ -6688,7 +6757,7 @@ react-focus-lock@^2.5.0: use-callback-ref "^1.2.1" use-sidecar "^1.0.1" -react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6698,6 +6767,18 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== +react-redux@^7.2.0: + version "7.2.4" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" + integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.13.1" + react-test-renderer@^16.0.0-0, react-test-renderer@^16.14.0: version "16.14.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae" @@ -6818,6 +6899,13 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redux@^4.0.0, redux@^4.0.4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" + integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== + dependencies: + "@babel/runtime" "^7.9.2" + reflect.ownkeys@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" @@ -7765,6 +7853,11 @@ through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tiny-invariant@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + tmatch@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf" @@ -8070,6 +8163,11 @@ use-callback-ref@^1.2.1: resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5" integrity sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg== +use-memo-one@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20" + integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ== + use-sidecar@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.4.tgz#38398c3723727f9f924bed2343dfa3db6aaaee46" From dbaa394d65c640581f3ef89f52aba13e4801cc3a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Jun 2021 08:54:30 +0100 Subject: [PATCH 11/94] i18n --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 85647a17e5..2a5297122f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1018,9 +1018,9 @@ "You can change these anytime.": "You can change these anytime.", "Creating...": "Creating...", "Create": "Create", + "All rooms": "All rooms", "Expand space panel": "Expand space panel", "Collapse space panel": "Collapse space panel", - "All rooms": "All rooms", "Click to copy": "Click to copy", "Copied!": "Copied!", "Failed to copy": "Failed to copy", From 43921500d3459896a8a220c870a2b46d49d34303 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 5 Jun 2021 22:21:10 -0400 Subject: [PATCH 12/94] Revert "Match requested avatar size to displayed size" This reverts commit 44b143c8c3063be7ca2bf24e6cfdb81be9351c75. --- src/components/views/elements/EventTilePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 6d2ea687de..77db94b5dd 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -61,7 +61,7 @@ interface IState { message: string; } -const AVATAR_SIZE = 30; +const AVATAR_SIZE = 32; @replaceableComponent("views.elements.EventTilePreview") export default class EventTilePreview extends React.Component { From b2b95257a8571edde2cf1a7ed110eb1a65652252 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 7 Jun 2021 08:54:41 +0100 Subject: [PATCH 13/94] Convert RoomAliasField to Typescript --- src/components/views/elements/Field.tsx | 7 +- .../{RoomAliasField.js => RoomAliasField.tsx} | 75 ++++++++++--------- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 48 insertions(+), 36 deletions(-) rename src/components/views/elements/{RoomAliasField.js => RoomAliasField.tsx} (67%) diff --git a/src/components/views/elements/Field.tsx b/src/components/views/elements/Field.tsx index 59d9a11596..1373c2df0e 100644 --- a/src/components/views/elements/Field.tsx +++ b/src/components/views/elements/Field.tsx @@ -29,6 +29,11 @@ function getId() { return `${BASE_ID}_${count++}`; } +export interface IValidateOpts { + focused?: boolean; + allowEmpty?: boolean; +} + interface IProps { // The field's ID, which binds the input and label together. Immutable. id?: string; @@ -180,7 +185,7 @@ export default class Field extends React.PureComponent { } }; - public async validate({ focused, allowEmpty = true }: {focused?: boolean, allowEmpty?: boolean}) { + public async validate({ focused, allowEmpty = true }: IValidateOpts) { if (!this.props.onValidate) { return; } diff --git a/src/components/views/elements/RoomAliasField.js b/src/components/views/elements/RoomAliasField.tsx similarity index 67% rename from src/components/views/elements/RoomAliasField.js rename to src/components/views/elements/RoomAliasField.tsx index 813dd8b5cc..7eff529c46 100644 --- a/src/components/views/elements/RoomAliasField.js +++ b/src/components/views/elements/RoomAliasField.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 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. @@ -13,67 +13,74 @@ 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, { createRef } from "react"; + import { _t } from '../../../languageHandler'; -import React from 'react'; -import PropTypes from 'prop-types'; -import * as sdk from '../../../index'; import withValidation from './Validation'; -import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import Field, { IValidateOpts } from "./Field"; + +interface IProps { + domain: string; + value: string; + label?: string; + placeholder?: string; + onChange?(value: string): void; +} + +interface IState { + isValid: boolean; +} // Controlled form component wrapping Field for inputting a room alias scoped to a given domain @replaceableComponent("views.elements.RoomAliasField") -export default class RoomAliasField extends React.PureComponent { - static propTypes = { - domain: PropTypes.string.isRequired, - onChange: PropTypes.func, - value: PropTypes.string.isRequired, +export default class RoomAliasField extends React.PureComponent { + private fieldRef = createRef(); + + public state = { + isValid: true, }; - constructor(props) { - super(props); - this.state = {isValid: true}; - } - - _asFullAlias(localpart) { + private asFullAlias(localpart: string): string { return `#${localpart}:${this.props.domain}`; } render() { - const Field = sdk.getComponent('views.elements.Field'); const poundSign = (#); const aliasPostfix = ":" + this.props.domain; const domain = ({aliasPostfix}); const maxlength = 255 - this.props.domain.length - 2; // 2 for # and : return ( this._fieldRef = ref} - onValidate={this._onValidate} - placeholder={_t("e.g. my-room")} - onChange={this._onChange} + ref={this.fieldRef} + onValidate={this.onValidate} + placeholder={this.props.placeholder || _t("e.g. my-room")} + onChange={this.onChange} value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)} maxLength={maxlength} /> ); } - _onChange = (ev) => { + private onChange = (ev) => { if (this.props.onChange) { - this.props.onChange(this._asFullAlias(ev.target.value)); + this.props.onChange(this.asFullAlias(ev.target.value)); } }; - _onValidate = async (fieldState) => { - const result = await this._validationRules(fieldState); + private onValidate = async (fieldState) => { + const result = await this.validationRules(fieldState); this.setState({isValid: result.valid}); return result; }; - _validationRules = withValidation({ + private validationRules = withValidation({ rules: [ { key: "safeLocalpart", @@ -81,7 +88,7 @@ export default class RoomAliasField extends React.PureComponent { if (!value) { return true; } - const fullAlias = this._asFullAlias(value); + const fullAlias = this.asFullAlias(value); // XXX: FIXME https://github.com/matrix-org/matrix-doc/issues/668 return !value.includes("#") && !value.includes(":") && !value.includes(",") && encodeURI(fullAlias) === fullAlias; @@ -90,7 +97,7 @@ export default class RoomAliasField extends React.PureComponent { }, { key: "required", test: async ({ value, allowEmpty }) => allowEmpty || !!value, - invalid: () => _t("Please provide a room address"), + invalid: () => _t("Please provide an address"), }, { key: "taken", final: true, @@ -100,7 +107,7 @@ export default class RoomAliasField extends React.PureComponent { } const client = MatrixClientPeg.get(); try { - await client.getRoomIdForAlias(this._asFullAlias(value)); + await client.getRoomIdForAlias(this.asFullAlias(value)); // we got a room id, so the alias is taken return false; } catch (err) { @@ -120,11 +127,11 @@ export default class RoomAliasField extends React.PureComponent { return this.state.isValid; } - validate(options) { - return this._fieldRef.validate(options); + validate(options: IValidateOpts) { + return this.fieldRef.current?.validate(options); } focus() { - this._fieldRef.focus(); + this.fieldRef.current?.focus(); } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9e85ea28c8..02662aa508 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2014,7 +2014,7 @@ "Room address": "Room address", "e.g. my-room": "e.g. my-room", "Some characters not allowed": "Some characters not allowed", - "Please provide a room address": "Please provide a room address", + "Please provide an address": "Please provide an address", "This address is available to use": "This address is available to use", "This address is already in use": "This address is already in use", "Server Options": "Server Options", From 8c34a8461ee64949b670715cafac5948bedbca57 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 7 Jun 2021 08:57:39 +0100 Subject: [PATCH 14/94] Add way to specify address during public space creation --- res/css/views/spaces/_SpaceBasicSettings.scss | 2 +- .../views/spaces/SpaceCreateMenu.tsx | 45 +++++++++++++++---- src/i18n/strings/en_EN.json | 2 + 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/res/css/views/spaces/_SpaceBasicSettings.scss b/res/css/views/spaces/_SpaceBasicSettings.scss index 204ccab2b7..e6e06e7181 100644 --- a/res/css/views/spaces/_SpaceBasicSettings.scss +++ b/res/css/views/spaces/_SpaceBasicSettings.scss @@ -16,7 +16,7 @@ limitations under the License. .mx_SpaceBasicSettings { .mx_Field { - margin: 32px 0; + margin: 24px 0; } .mx_SpaceBasicSettings_avatarContainer { diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 0ebf511018..a65b53a045 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -33,6 +33,7 @@ import {USER_LABS_TAB} from "../dialogs/UserSettingsDialog"; import Field from "../elements/Field"; import withValidation from "../elements/Validation"; import {SpaceFeedbackPrompt} from "../../structures/SpaceRoomView"; +import RoomAliasField from "../elements/RoomAliasField"; const SpaceCreateMenuType = ({ title, description, className, onClick }) => { return ( @@ -58,6 +59,11 @@ const spaceNameValidator = withValidation({ ], }); +const nameToAlias = (name: string, domain: string): string => { + const localpart = name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9_-]+/gi, ""); + return `#${localpart}:${domain}`; +}; + const SpaceCreateMenu = ({ onFinished }) => { const cli = useContext(MatrixClientContext); const [visibility, setVisibility] = useState(null); @@ -65,6 +71,8 @@ const SpaceCreateMenu = ({ onFinished }) => { const [name, setName] = useState(""); const spaceNameField = useRef(); + const [alias, setAlias] = useState(""); + const spaceAliasField = useRef(); const [avatar, setAvatar] = useState(null); const [topic, setTopic] = useState(""); @@ -80,6 +88,13 @@ const SpaceCreateMenu = ({ onFinished }) => { setBusy(false); return; } + // validate the space name alias field but do not require it + if (visibility === Visibility.Public && !await spaceAliasField.current.validate({ allowEmpty: true })) { + spaceAliasField.current.focus(); + spaceAliasField.current.validate({ allowEmpty: true, focused: true }); + setBusy(false); + return; + } const initialState: IStateEvent[] = [ { @@ -97,12 +112,6 @@ const SpaceCreateMenu = ({ onFinished }) => { content: { url }, }); } - if (topic) { - initialState.push({ - type: EventType.RoomTopic, - content: { topic }, - }); - } try { await createRoom({ @@ -110,7 +119,6 @@ const SpaceCreateMenu = ({ onFinished }) => { preset: visibility === Visibility.Public ? Preset.PublicChat : Preset.PrivateChat, name, creation_content: { - // Based on MSC1840 [RoomCreateTypeField]: RoomType.Space, }, initial_state: initialState, @@ -119,6 +127,8 @@ const SpaceCreateMenu = ({ onFinished }) => { events_default: 100, ...Visibility.Public ? { invite: 0 } : {}, }, + room_alias_name: alias ? alias.substr(1, alias.indexOf(":") - 1) : undefined, + topic, }, spinner: false, encryption: false, @@ -157,6 +167,7 @@ const SpaceCreateMenu = ({ onFinished }) => { ; } else { + const domain = cli.getDomain(); body = { label={_t("Name")} autoFocus={true} value={name} - onChange={ev => setName(ev.target.value)} + onChange={ev => { + const newName = ev.target.value; + if (!alias || alias === nameToAlias(name, domain)) { + setAlias(nameToAlias(newName, domain)); + } + setName(newName); + }} ref={spaceNameField} onValidate={spaceNameValidator} disabled={busy} /> + { visibility === Visibility.Public + ? + : null + } + Date: Mon, 7 Jun 2021 08:59:57 +0100 Subject: [PATCH 15/94] Stash --- src/stores/SpaceStore.tsx | 62 +++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 9ef961ce2d..5e09b617a7 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -61,8 +61,6 @@ const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, }, [[], []]); }; -const SpaceTagOrderingField = "org.matrix.mscXXXX.space"; - // For sorting space children using a validated `order`, `m.room.create`'s `origin_server_ts`, `room_id` export const getChildOrder = (order: string, creationTs: number, roomId: string): Array>> => { let validatedOrder: string = null; @@ -98,7 +96,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private _activeSpace?: Room = null; private _suggestedRooms: ISuggestedRoom[] = []; private _invitedSpaces = new Set(); - private spaceOrderLocalEchoMap = new Map(); + private spaceOrderLocalEchoMap = new Map(); public get invitedSpaces(): Room[] { return Array.from(this._invitedSpaces); @@ -477,11 +475,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => { - if (!room.isSpaceRoom() || ev.getType() !== EventType.Tag) return; + if (!room.isSpaceRoom() || ev.getType() !== EventType.SpaceOrder) return; this.spaceOrderLocalEchoMap.delete(room.roomId); // clear any local echo - const order = ev.getContent()?.tags?.[SpaceTagOrderingField]?.order; - const lastOrder = lastEv?.getContent()?.tags?.[SpaceTagOrderingField]?.order; + const order = ev.getContent()?.order; + const lastOrder = lastEv?.getContent()?.order; if (order !== lastOrder) { this.notifyIfOrderChanged(); } @@ -625,15 +623,21 @@ export class SpaceStoreClass extends AsyncStoreWithClient { childSpaces.forEach(s => this.traverseSpace(s.roomId, fn, includeRooms, newPath)); } - private getSpaceTagOrdering = (space: Room): number | undefined => { + private getSpaceTagOrdering = (space: Room): string | undefined => { if (this.spaceOrderLocalEchoMap.has(space.roomId)) return this.spaceOrderLocalEchoMap.get(space.roomId); - return space.tags?.[SpaceTagOrderingField]?.order; + const order = space.getAccountData(EventType.SpaceOrder)?.getContent()?.order; + return typeof order === "string" ? order : undefined; }; private sortRootSpaces(spaces: Room[]): Room[] { return sortBy(spaces, [this.getSpaceTagOrdering, "roomId"]); } + private setRootSpaceOrder(space: Room, order: string): void { + this.spaceOrderLocalEchoMap.set(space.roomId, order); + this.matrixClient.setRoomAccountData(space.roomId, EventType.SpaceOrder, { order }); + } + public moveRootSpace(fromIndex: number, toIndex: number): void { if ( fromIndex < 0 || toIndex < 0 || @@ -645,29 +649,43 @@ export class SpaceStoreClass extends AsyncStoreWithClient { const space = this.rootSpaces[fromIndex]; const orders = this.rootSpaces.map(this.getSpaceTagOrdering); - let prevOrder = orders[toIndex - 1]; - let nextOrder = orders[toIndex]; // accounts for downwards displacement of existing inhabitant of this index + let prevOrder: string; + let nextOrder: string; - if (prevOrder === undefined && nextOrder === undefined) { - // TODO WHAT A PAIN + if (toIndex > fromIndex) { + prevOrder = toIndex >= 0 ? orders[toIndex] : "aaaaa"; + nextOrder = toIndex <= orders.length ? orders[toIndex + 1] : "zzzzz"; + } else { + // accounts for downwards displacement of existing inhabitant of this index + prevOrder = toIndex > 0 ? orders[toIndex - 1] : "aaaaa"; + nextOrder = toIndex < orders.length ? orders[toIndex] : "zzzzz"; } + console.log("@@ start", {fromIndex, toIndex, orders, prevOrder, nextOrder}); - prevOrder = prevOrder || 0.0; - nextOrder = nextOrder || 1.0; + if (prevOrder === undefined) { + const firstUndefinedIndex = orders.indexOf(undefined); + const numUndefined = orders.length - firstUndefinedIndex; + const lastOrder = orders[firstUndefinedIndex - 1]; + console.log("@@ precalc", {firstUndefinedIndex, numUndefined, lastOrder}); + nextOrder = lastOrder + step; + for (let i = firstUndefinedIndex; i < toIndex; i++, nextOrder += step) { + console.log("@@ preset", {i, nextOrder}); + this.setRootSpaceOrder(this.rootSpaces[i], nextOrder); + } + + prevOrder = nextOrder; + nextOrder += step; + } if (prevOrder !== nextOrder) { const order = prevOrder + ((nextOrder - prevOrder) / 2); - this.spaceOrderLocalEchoMap.set(space.roomId, order); - this.matrixClient.setRoomAccountData(space.roomId, EventType.Tag, { - tags: { - ...space.tags, - [SpaceTagOrderingField]: { order }, - }, - }); - this.notifyIfOrderChanged(); + console.log("@@ set", {prevOrder, nextOrder, order}); + this.setRootSpaceOrder(space, order); } else { // TODO REBUILD } + + this.notifyIfOrderChanged(); } } From a7eb09af1ebbffd5f700250dc52dd00238e847f1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 7 Jun 2021 15:48:55 +0100 Subject: [PATCH 16/94] Convert EditableItemList & AliasSettings to Typescript --- ...itableItemList.js => EditableItemList.tsx} | 133 +++++++++-------- .../{AliasSettings.js => AliasSettings.tsx} | 134 ++++++++++-------- 2 files changed, 146 insertions(+), 121 deletions(-) rename src/components/views/elements/{EditableItemList.js => EditableItemList.tsx} (54%) rename src/components/views/room_settings/{AliasSettings.js => AliasSettings.tsx} (78%) diff --git a/src/components/views/elements/EditableItemList.js b/src/components/views/elements/EditableItemList.tsx similarity index 54% rename from src/components/views/elements/EditableItemList.js rename to src/components/views/elements/EditableItemList.tsx index d8ec5af278..89e2e1b8a0 100644 --- a/src/components/views/elements/EditableItemList.js +++ b/src/components/views/elements/EditableItemList.tsx @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 New Vector Ltd. +Copyright 2017-2021 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,48 +14,48 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; -import {_t} from '../../../languageHandler'; +import React from "react"; + +import { _t } from '../../../languageHandler'; import Field from "./Field"; import AccessibleButton from "./AccessibleButton"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; -export class EditableItem extends React.Component { - static propTypes = { - index: PropTypes.number, - value: PropTypes.string, - onRemove: PropTypes.func, +interface IItemProps { + index?: number; + value?: string; + onRemove?(index: number): void; +} + +interface IItemState { + verifyRemove: boolean; +} + +export class EditableItem extends React.Component { + public state = { + verifyRemove: false, }; - constructor() { - super(); - - this.state = { - verifyRemove: false, - }; - } - - _onRemove = (e) => { + private onRemove = (e) => { e.stopPropagation(); e.preventDefault(); - this.setState({verifyRemove: true}); + this.setState({ verifyRemove: true }); }; - _onDontRemove = (e) => { + private onDontRemove = (e) => { e.stopPropagation(); e.preventDefault(); - this.setState({verifyRemove: false}); + this.setState({ verifyRemove: false }); }; - _onActuallyRemove = (e) => { + private onActuallyRemove = (e) => { e.stopPropagation(); e.preventDefault(); if (this.props.onRemove) this.props.onRemove(this.props.index); - this.setState({verifyRemove: false}); + this.setState({ verifyRemove: false }); }; render() { @@ -66,14 +66,14 @@ export class EditableItem extends React.Component { {_t("Are you sure?")} {_t("Yes")} @@ -85,59 +85,68 @@ export class EditableItem extends React.Component { return (
    -
    +
    {this.props.value}
    ); } } +interface IProps { + id: string; + items: string[]; + itemsLabel?: string; + noItemsLabel?: string; + placeholder?: string; + newItem?: string; + canEdit?: boolean; + canRemove?: boolean; + suggestionsListId?: string; + onItemAdded?(item: string): void; + onItemRemoved?(index: number): void; + onNewItemChanged?(item: string): void; +} + @replaceableComponent("views.elements.EditableItemList") -export default class EditableItemList extends React.Component { - static propTypes = { - id: PropTypes.string.isRequired, - items: PropTypes.arrayOf(PropTypes.string).isRequired, - itemsLabel: PropTypes.string, - noItemsLabel: PropTypes.string, - placeholder: PropTypes.string, - newItem: PropTypes.string, - - onItemAdded: PropTypes.func, - onItemRemoved: PropTypes.func, - onNewItemChanged: PropTypes.func, - - canEdit: PropTypes.bool, - canRemove: PropTypes.bool, - }; - - _onItemAdded = (e) => { +export default class EditableItemList

    extends React.PureComponent { + protected onItemAdded = (e) => { e.stopPropagation(); e.preventDefault(); if (this.props.onItemAdded) this.props.onItemAdded(this.props.newItem); }; - _onItemRemoved = (index) => { + protected onItemRemoved = (index) => { if (this.props.onItemRemoved) this.props.onItemRemoved(index); }; - _onNewItemChanged = (e) => { + protected onNewItemChanged = (e) => { if (this.props.onNewItemChanged) this.props.onNewItemChanged(e.target.value); }; - _renderNewItemField() { + protected renderNewItemField() { return (

    - - - {_t("Add")} + + + { _t("Add") } ); @@ -153,19 +162,21 @@ export default class EditableItemList extends React.Component { key={item} index={index} value={item} - onRemove={this._onItemRemoved} + onRemove={this.onItemRemoved} />; }); const editableItemsSection = this.props.canRemove ? editableItems :
      {editableItems}
    ; const label = this.props.items.length > 0 ? this.props.itemsLabel : this.props.noItemsLabel; - return (
    -
    - { label } + return ( +
    +
    + { label } +
    + { editableItemsSection } + { this.props.canEdit ? this.renderNewItemField() :
    }
    - { editableItemsSection } - { this.props.canEdit ? this._renderNewItemField() :
    } -
    ); + ); } } diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.tsx similarity index 78% rename from src/components/views/room_settings/AliasSettings.js rename to src/components/views/room_settings/AliasSettings.tsx index 80e0099ab3..d6e79c4ee9 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -1,6 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd -Copyright 2018, 2019 New Vector Ltd +Copyright 2016-2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,59 +14,60 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React, { ChangeEvent, createRef } from "react"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + import EditableItemList from "../elements/EditableItemList"; -import React, {createRef} from 'react'; -import PropTypes from 'prop-types'; -import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import * as sdk from "../../../index"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; import Field from "../elements/Field"; +import Spinner from "../elements/Spinner"; import ErrorDialog from "../dialogs/ErrorDialog"; import AccessibleButton from "../elements/AccessibleButton"; import Modal from "../../../Modal"; import RoomPublishSetting from "./RoomPublishSetting"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import RoomAliasField from "../elements/RoomAliasField"; -class EditableAliasesList extends EditableItemList { - constructor(props) { - super(props); +interface IEditableAliasesListProps { + domain?: string; +} - this._aliasField = createRef(); - } +class EditableAliasesList extends EditableItemList { + private aliasField = createRef(); - _onAliasAdded = async () => { - await this._aliasField.current.validate({ allowEmpty: false }); + private onAliasAdded = async () => { + await this.aliasField.current.validate({ allowEmpty: false }); - if (this._aliasField.current.isValid) { + if (this.aliasField.current.isValid) { if (this.props.onItemAdded) this.props.onItemAdded(this.props.newItem); return; } - this._aliasField.current.focus(); - this._aliasField.current.validate({ allowEmpty: false, focused: true }); + this.aliasField.current.focus(); + this.aliasField.current.validate({ allowEmpty: false, focused: true }); }; - _renderNewItemField() { + protected renderNewItemField() { // if we don't need the RoomAliasField, - // we don't need to overriden version of _renderNewItemField + // we don't need to overriden version of renderNewItemField if (!this.props.domain) { - return super._renderNewItemField(); + return super.renderNewItemField(); } - const RoomAliasField = sdk.getComponent('views.elements.RoomAliasField'); - const onChange = (alias) => this._onNewItemChanged({target: {value: alias}}); + const onChange = (alias) => this.onNewItemChanged({target: {value: alias}}); return (
    - + { _t("Add") } @@ -75,15 +75,27 @@ class EditableAliasesList extends EditableItemList { } } -@replaceableComponent("views.room_settings.AliasSettings") -export default class AliasSettings extends React.Component { - static propTypes = { - roomId: PropTypes.string.isRequired, - canSetCanonicalAlias: PropTypes.bool.isRequired, - canSetAliases: PropTypes.bool.isRequired, - canonicalAliasEvent: PropTypes.object, // MatrixEvent - }; +interface IProps { + roomId: string; + canSetCanonicalAlias: boolean; + canSetAliases: boolean; + canonicalAliasEvent?: MatrixEvent; + aliasEvents?: MatrixEvent[]; +} +interface IState { + altAliases: string[]; + localAliases: string[]; + canonicalAlias?: string; + updatingCanonicalAlias: boolean; + localAliasesLoading: boolean; + detailsOpen: boolean; + newAlias?: string; + newAltAlias?: string; +} + +@replaceableComponent("views.room_settings.AliasSettings") +export default class AliasSettings extends React.Component { static defaultProps = { canSetAliases: false, canSetCanonicalAlias: false, @@ -122,7 +134,7 @@ export default class AliasSettings extends React.Component { } } - async loadLocalAliases() { + private async loadLocalAliases() { this.setState({ localAliasesLoading: true }); try { const cli = MatrixClientPeg.get(); @@ -139,7 +151,7 @@ export default class AliasSettings extends React.Component { } } - changeCanonicalAlias(alias) { + private changeCanonicalAlias(alias: string) { if (!this.props.canSetCanonicalAlias) return; const oldAlias = this.state.canonicalAlias; @@ -170,7 +182,7 @@ export default class AliasSettings extends React.Component { }); } - changeAltAliases(altAliases) { + private changeAltAliases(altAliases: string[]) { if (!this.props.canSetCanonicalAlias) return; this.setState({ @@ -181,7 +193,7 @@ export default class AliasSettings extends React.Component { const eventContent = {}; if (this.state.canonicalAlias) { - eventContent.alias = this.state.canonicalAlias; + eventContent["alias"] = this.state.canonicalAlias; } if (altAliases) { eventContent["alt_aliases"] = altAliases; @@ -202,11 +214,11 @@ export default class AliasSettings extends React.Component { }); } - onNewAliasChanged = (value) => { - this.setState({newAlias: value}); + private onNewAliasChanged = (value: string) => { + this.setState({ newAlias: value }); }; - onLocalAliasAdded = (alias) => { + private onLocalAliasAdded = (alias: string) => { if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases const localDomain = MatrixClientPeg.get().getDomain(); @@ -232,7 +244,7 @@ export default class AliasSettings extends React.Component { }); }; - onLocalAliasDeleted = (index) => { + private onLocalAliasDeleted = (index: number) => { 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/element-web/issues/7353 @@ -261,7 +273,7 @@ export default class AliasSettings extends React.Component { }); }; - onLocalAliasesToggled = (event) => { + private onLocalAliasesToggled = (event: ChangeEvent) => { // expanded if (event.target.open) { // if local aliases haven't been preloaded yet at component mount @@ -269,37 +281,37 @@ export default class AliasSettings extends React.Component { this.loadLocalAliases(); } } - this.setState({detailsOpen: event.target.open}); + this.setState({ detailsOpen: event.currentTarget.open }); }; - onCanonicalAliasChange = (event) => { + private onCanonicalAliasChange = (event: ChangeEvent) => { this.changeCanonicalAlias(event.target.value); }; - onNewAltAliasChanged = (value) => { - this.setState({newAltAlias: value}); + private onNewAltAliasChanged = (value: string) => { + this.setState({ newAltAlias: value }); } - onAltAliasAdded = (alias) => { + private onAltAliasAdded = (alias: string) => { const altAliases = this.state.altAliases.slice(); if (!altAliases.some(a => a.trim() === alias.trim())) { altAliases.push(alias.trim()); this.changeAltAliases(altAliases); - this.setState({newAltAlias: ""}); + this.setState({ newAltAlias: "" }); } } - onAltAliasDeleted = (index) => { + private onAltAliasDeleted = (index: number) => { const altAliases = this.state.altAliases.slice(); altAliases.splice(index, 1); this.changeAltAliases(altAliases); } - _getAliases() { - return this.state.altAliases.concat(this._getLocalNonAltAliases()); + private getAliases() { + return this.state.altAliases.concat(this.getLocalNonAltAliases()); } - _getLocalNonAltAliases() { + private getLocalNonAltAliases() { const {altAliases} = this.state; return this.state.localAliases.filter(alias => !altAliases.includes(alias)); } @@ -320,7 +332,7 @@ export default class AliasSettings extends React.Component { > { - this._getAliases().map((alias, i) => { + this.getAliases().map((alias, i) => { if (alias === this.state.canonicalAlias) found = true; return (
    ); From 4725a9e8fa954b3858f4dd8e8d4212f6a0d61488 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 7 Jun 2021 16:42:59 +0100 Subject: [PATCH 17/94] Extract useRoomState hook into hooks directory --- .../views/right_panel/PinnedMessagesCard.tsx | 19 +-------- src/hooks/useRoomState.ts | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 src/hooks/useRoomState.ts diff --git a/src/components/views/right_panel/PinnedMessagesCard.tsx b/src/components/views/right_panel/PinnedMessagesCard.tsx index a3f1f2d9df..ad62619593 100644 --- a/src/components/views/right_panel/PinnedMessagesCard.tsx +++ b/src/components/views/right_panel/PinnedMessagesCard.tsx @@ -28,6 +28,7 @@ import { useEventEmitter } from "../../../hooks/useEventEmitter"; import PinningUtils from "../../../utils/PinningUtils"; import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; import PinnedEventTile from "../rooms/PinnedEventTile"; +import { useRoomState } from "../../../hooks/useRoomState"; interface IProps { room: Room; @@ -75,24 +76,6 @@ export const useReadPinnedEvents = (room: Room): Set => { return readPinnedEvents; }; -const useRoomState = (room: Room, mapper: (state: RoomState) => T): T => { - const [value, setValue] = useState(room ? mapper(room.currentState) : undefined); - - const update = useCallback(() => { - if (!room) return; - setValue(mapper(room.currentState)); - }, [room, mapper]); - - useEventEmitter(room?.currentState, "RoomState.events", update); - useEffect(() => { - update(); - return () => { - setValue(undefined); - }; - }, [update]); - return value; -}; - const PinnedMessagesCard = ({ room, onClose }: IProps) => { const cli = useContext(MatrixClientContext); const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli)); diff --git a/src/hooks/useRoomState.ts b/src/hooks/useRoomState.ts new file mode 100644 index 0000000000..ded51c3900 --- /dev/null +++ b/src/hooks/useRoomState.ts @@ -0,0 +1,40 @@ +/* +Copyright 2021 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 { useCallback, useEffect, useState } from "react"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { RoomState } from "matrix-js-sdk/src/models/room-state"; + +import { useEventEmitter } from "./useEventEmitter"; + +// Hook to simplify watching Matrix Room state +export const useRoomState = (room: Room, mapper: (state: RoomState) => T): T => { + const [value, setValue] = useState(room ? mapper(room.currentState) : undefined); + + const update = useCallback(() => { + if (!room) return; + setValue(mapper(room.currentState)); + }, [room, mapper]); + + useEventEmitter(room?.currentState, "RoomState.events", update); + useEffect(() => { + update(); + return () => { + setValue(undefined); + }; + }, [update]); + return value; +}; From 5e3ad621892786890692c987d95f74881bebe232 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 7 Jun 2021 19:03:04 -0400 Subject: [PATCH 18/94] Remove mysterious dot from EventTilePreviews It was a bullet point, since EventTiles now get created as li by default :P Signed-off-by: Robin Townsend --- src/components/views/elements/EventTilePreview.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 77db94b5dd..20d6cbaeb3 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -128,6 +128,7 @@ export default class EventTilePreview extends React.Component { mxEvent={event} layout={this.props.layout} enableFlair={SettingsStore.getValue(UIFeature.Flair)} + as="div" />
    ; } From 9454a4e6c79f0ecfa5ebd91bd7af689e478ca559 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:28:02 +0100 Subject: [PATCH 19/94] Convert AdvancedRoomSettingsTab to Typescript --- ...ingsTab.js => AdvancedRoomSettingsTab.tsx} | 87 ++++++++++--------- 1 file changed, 47 insertions(+), 40 deletions(-) rename src/components/views/settings/tabs/room/{AdvancedRoomSettingsTab.js => AdvancedRoomSettingsTab.tsx} (73%) diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx similarity index 73% rename from src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js rename to src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index 28aad65129..f587210095 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,68 +15,75 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; -import {_t} from "../../../../../languageHandler"; -import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; -import * as sdk from "../../../../.."; + +import { _t } from "../../../../../languageHandler"; +import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; +import RoomUpgradeDialog from "../../../dialogs/RoomUpgradeDialog"; +import DevtoolsDialog from "../../../dialogs/DevtoolsDialog"; import Modal from "../../../../../Modal"; import dis from "../../../../../dispatcher/dispatcher"; -import {replaceableComponent} from "../../../../../utils/replaceableComponent"; +import { replaceableComponent } from "../../../../../utils/replaceableComponent"; + +interface IProps { + roomId: string; + closeSettingsFn(): void; +} + +interface IRecommendedVersion { + version: string; + needsUpgrade: boolean; + urgent: boolean; +} + +interface IState { + upgradeRecommendation?: IRecommendedVersion; + oldRoomId?: string; + oldEventId?: string; + upgraded?: boolean; +} @replaceableComponent("views.settings.tabs.room.AdvancedRoomSettingsTab") -export default class AdvancedRoomSettingsTab extends React.Component { - static propTypes = { - roomId: PropTypes.string.isRequired, - closeSettingsFn: PropTypes.func.isRequired, - }; - - constructor(props) { - super(props); +export default class AdvancedRoomSettingsTab extends React.Component { + constructor(props, context) { + super(props, context); this.state = { // This is eventually set to the value of room.getRecommendedVersion() upgradeRecommendation: null, }; - } - // TODO: [REACT-WARNING] Move this to constructor - UNSAFE_componentWillMount() { // eslint-disable-line camelcase // we handle lack of this object gracefully later, so don't worry about it failing here. const room = MatrixClientPeg.get().getRoom(this.props.roomId); room.getRecommendedVersion().then((v) => { const tombstone = room.currentState.getStateEvents("m.room.tombstone", ""); - const additionalStateChanges = {}; + const additionalStateChanges: Partial = {}; const createEvent = room.currentState.getStateEvents("m.room.create", ""); const predecessor = createEvent ? createEvent.getContent().predecessor : null; if (predecessor && predecessor.room_id) { - additionalStateChanges['oldRoomId'] = predecessor.room_id; - additionalStateChanges['oldEventId'] = predecessor.event_id; - additionalStateChanges['hasPreviousRoom'] = true; + additionalStateChanges.oldRoomId = predecessor.room_id; + additionalStateChanges.oldEventId = predecessor.event_id; } - this.setState({ - upgraded: tombstone && tombstone.getContent().replacement_room, + upgraded: !!tombstone?.getContent().replacement_room, upgradeRecommendation: v, ...additionalStateChanges, }); }); } - _upgradeRoom = (e) => { - const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog'); + private upgradeRoom = (e) => { const room = MatrixClientPeg.get().getRoom(this.props.roomId); Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: room}); }; - _openDevtools = (e) => { - const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog'); + private openDevtools = (e) => { Modal.createDialog(DevtoolsDialog, {roomId: this.props.roomId}); }; - _onOldRoomClicked = (e) => { + private onOldRoomClicked = (e) => { e.preventDefault(); e.stopPropagation(); @@ -113,7 +120,7 @@ export default class AdvancedRoomSettingsTab extends React.Component { }, )}

    - + {_t("Upgrade this room to the recommended room version")}
    @@ -121,13 +128,13 @@ export default class AdvancedRoomSettingsTab extends React.Component { } let oldRoomLink; - if (this.state.hasPreviousRoom) { + if (this.state.oldRoomId) { let name = _t("this room"); const room = MatrixClientPeg.get().getRoom(this.props.roomId); if (room && room.name) name = room.name; oldRoomLink = ( - - {_t("View older messages in %(roomName)s.", {roomName: name})} + + { _t("View older messages in %(roomName)s.", { roomName: name }) } ); } @@ -139,23 +146,23 @@ export default class AdvancedRoomSettingsTab extends React.Component { {_t("Room information")}
    {_t("Internal room ID:")}  - {this.props.roomId} + { this.props.roomId }
    - {unfederatableSection} + { unfederatableSection }
    {_t("Room version")}
    {_t("Room version:")}  - {room.getVersion()} + { room.getVersion() }
    - {oldRoomLink} - {roomUpgradeButton} + { oldRoomLink } + { roomUpgradeButton }
    - {_t("Developer options")} - - {_t("Open Devtools")} + { _t("Developer options") } + + { _t("Open Devtools") }
    From 8d4ac90265839a98aeb83277834ef8673fc2b97e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:28:37 +0100 Subject: [PATCH 20/94] Convert RoomPublishSetting and LabelledToggleSwitch to Typescript --- ...ggleSwitch.js => LabelledToggleSwitch.tsx} | 47 +++++++++---------- ...blishSetting.js => RoomPublishSetting.tsx} | 47 ++++++++++++------- 2 files changed, 51 insertions(+), 43 deletions(-) rename src/components/views/elements/{LabelledToggleSwitch.js => LabelledToggleSwitch.tsx} (63%) rename src/components/views/room_settings/{RoomPublishSetting.js => RoomPublishSetting.tsx} (62%) diff --git a/src/components/views/elements/LabelledToggleSwitch.js b/src/components/views/elements/LabelledToggleSwitch.tsx similarity index 63% rename from src/components/views/elements/LabelledToggleSwitch.js rename to src/components/views/elements/LabelledToggleSwitch.tsx index ef60eeed7b..957e3dbc97 100644 --- a/src/components/views/elements/LabelledToggleSwitch.js +++ b/src/components/views/elements/LabelledToggleSwitch.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 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,38 +14,33 @@ 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 ToggleSwitch from "./ToggleSwitch"; import {replaceableComponent} from "../../../utils/replaceableComponent"; +interface IProps { + // The value for the toggle switch + value: boolean; + // The translated label for the switch + label: string; + // Whether or not to disable the toggle switch + disabled?: boolean; + // True to put the toggle in front of the label + // Default false. + toggleInFront?: boolean; + // Additional class names to append to the switch. Optional. + className?: string; + // The function to call when the value changes + onChange(checked: boolean): void; +} + @replaceableComponent("views.elements.LabelledToggleSwitch") -export default class LabelledToggleSwitch extends React.Component { - static propTypes = { - // The value for the toggle switch - value: PropTypes.bool.isRequired, - - // The function to call when the value changes - onChange: PropTypes.func.isRequired, - - // The translated label for the switch - label: PropTypes.string.isRequired, - - // Whether or not to disable the toggle switch - disabled: PropTypes.bool, - - // True to put the toggle in front of the label - // Default false. - toggleInFront: PropTypes.bool, - - // Additional class names to append to the switch. Optional. - className: PropTypes.string, - }; - +export default class LabelledToggleSwitch extends React.PureComponent { render() { // This is a minimal version of a SettingsFlag - let firstPart = {this.props.label}; + let firstPart = { this.props.label }; let secondPart = { + public state = { + isRoomPublished: false, + }; - onRoomPublishChange = (e) => { + private onRoomPublishChange = (e) => { const valueBefore = this.state.isRoomPublished; const newValue = !valueBefore; this.setState({isRoomPublished: newValue}); @@ -52,11 +62,14 @@ export default class RoomPublishSetting extends React.PureComponent { render() { const client = MatrixClientPeg.get(); - return (); + return ( + + ); } } From 13a2f779b962e8498ae09d9675a91baeca3cd671 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:29:06 +0100 Subject: [PATCH 21/94] improve defaults for useRoomState and useStateToggle hooks --- src/hooks/useRoomState.ts | 5 ++++- src/hooks/useStateToggle.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hooks/useRoomState.ts b/src/hooks/useRoomState.ts index ded51c3900..11ac7de49e 100644 --- a/src/hooks/useRoomState.ts +++ b/src/hooks/useRoomState.ts @@ -20,8 +20,11 @@ import { RoomState } from "matrix-js-sdk/src/models/room-state"; import { useEventEmitter } from "./useEventEmitter"; +type Mapper = (roomState: RoomState) => T; +const defaultMapper: Mapper = (roomState: RoomState) => roomState; + // Hook to simplify watching Matrix Room state -export const useRoomState = (room: Room, mapper: (state: RoomState) => T): T => { +export const useRoomState = (room: Room, mapper: Mapper = defaultMapper): T => { const [value, setValue] = useState(room ? mapper(room.currentState) : undefined); const update = useCallback(() => { diff --git a/src/hooks/useStateToggle.ts b/src/hooks/useStateToggle.ts index b50a923234..33701c4f16 100644 --- a/src/hooks/useStateToggle.ts +++ b/src/hooks/useStateToggle.ts @@ -18,7 +18,7 @@ import {Dispatch, SetStateAction, 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: boolean): [boolean, () => void, Dispatch>] => { +export const useStateToggle = (initialValue = false): [boolean, () => void, Dispatch>] => { const [value, setValue] = useState(initialValue); const toggleValue = () => { setValue(!value); From 5c85ee1ea03097325680ed4574006c559fcccaa9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:30:46 +0100 Subject: [PATCH 22/94] Make AdvancedRoomSettingsTab space-aware --- .../views/settings/tabs/room/AdvancedRoomSettingsTab.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index f587210095..7e7d9cba90 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -76,7 +76,7 @@ export default class AdvancedRoomSettingsTab extends React.Component { const room = MatrixClientPeg.get().getRoom(this.props.roomId); - Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: room}); + Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, { room }); }; private openDevtools = (e) => { @@ -143,7 +143,9 @@ export default class AdvancedRoomSettingsTab extends React.Component
    {_t("Advanced")}
    - {_t("Room information")} + + { room?.isSpaceRoom() ? _t("Space information") : _t("Room information") } +
    {_t("Internal room ID:")}  { this.props.roomId } From 78debcc93b99eea96f270cbff8a5266a10a52db5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:31:39 +0100 Subject: [PATCH 23/94] Add method to disable StyledRadioGroup and wrap description in element with a className --- .../views/elements/StyledRadioGroup.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/StyledRadioGroup.tsx b/src/components/views/elements/StyledRadioGroup.tsx index 6b9e992f92..744b6f2059 100644 --- a/src/components/views/elements/StyledRadioGroup.tsx +++ b/src/components/views/elements/StyledRadioGroup.tsx @@ -34,10 +34,19 @@ interface IProps { definitions: IDefinition[]; value?: T; // if not provided no options will be selected outlined?: boolean; + disabled?: boolean; onChange(newValue: T): void; } -function StyledRadioGroup({name, definitions, value, className, outlined, onChange}: IProps) { +function StyledRadioGroup({ + name, + definitions, + value, + className, + outlined, + disabled, + onChange, +}: IProps) { const _onChange = e => { onChange(e.target.value); }; @@ -50,12 +59,12 @@ function StyledRadioGroup({name, definitions, value, className checked={d.checked !== undefined ? d.checked : d.value === value} name={name} value={d.value} - disabled={d.disabled} + disabled={disabled || d.disabled} outlined={outlined} > - {d.label} + { d.label } - {d.description} + { d.description ? { d.description } : null } )} ; } From fdecba2fe54a5bfd4e5b9e6974e1fd9467858539 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 16:32:43 +0100 Subject: [PATCH 24/94] Make AliasSettings space-aware, remove stale unused props --- .../views/room_settings/AliasSettings.tsx | 34 ++++++++++++++----- .../tabs/room/GeneralRoomSettingsTab.js | 3 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/components/views/room_settings/AliasSettings.tsx b/src/components/views/room_settings/AliasSettings.tsx index d6e79c4ee9..6f83d64eaf 100644 --- a/src/components/views/room_settings/AliasSettings.tsx +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -80,7 +80,7 @@ interface IProps { canSetCanonicalAlias: boolean; canSetAliases: boolean; canonicalAliasEvent?: MatrixEvent; - aliasEvents?: MatrixEvent[]; + hidePublishSetting?: boolean; } interface IState { @@ -99,7 +99,6 @@ export default class AliasSettings extends React.Component { static defaultProps = { canSetAliases: false, canSetCanonicalAlias: false, - aliasEvents: [], }; constructor(props) { @@ -317,7 +316,9 @@ export default class AliasSettings extends React.Component { } render() { - const localDomain = MatrixClientPeg.get().getDomain(); + const cli = MatrixClientPeg.get(); + const localDomain = cli.getDomain(); + const isSpaceRoom = cli.getRoom(this.props.roomId)?.isSpaceRoom(); let found = false; const canonicalValue = this.state.canonicalAlias || ""; @@ -363,7 +364,9 @@ export default class AliasSettings extends React.Component { canEdit={this.props.canSetAliases} onItemAdded={this.onLocalAliasAdded} onItemRemoved={this.onLocalAliasDeleted} - noItemsLabel={_t('This room has no local addresses')} + noItemsLabel={isSpaceRoom + ? _t("This space has no local addresses") + : _t("This room has no local addresses")} placeholder={_t('Local address')} domain={localDomain} />); @@ -372,10 +375,20 @@ export default class AliasSettings extends React.Component { return (
    {_t("Published Addresses")} -

    {_t("Published addresses can be used by anyone on any server to join your room. " + - "To publish an address, it needs to be set as a local address first.")}

    - {canonicalAliasSection} - +

    + { isSpaceRoom + ? _t("Published addresses can be used by anyone on any server to join your space.") + : _t("Published addresses can be used by anyone on any server to join your room.")} +   + { _t("To publish an address, it needs to be set as a local address first.") } +

    + { canonicalAliasSection } + { this.props.hidePublishSetting + ? null + : } {this.getLocalNonAltAliases().map(alias => { return
    - + > + {_t("Decline")} +
    - + > + {_t("Accept")} +
    ; } From 7f635c68c519e0df9ba20e34c8f278ea33b20720 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 21 Jun 2021 14:50:21 +0100 Subject: [PATCH 60/94] Migrate SearchBar to TypeScript --- src/components/structures/RoomView.tsx | 9 +-- .../rooms/{SearchBar.js => SearchBar.tsx} | 69 ++++++++++++++----- 2 files changed, 55 insertions(+), 23 deletions(-) rename src/components/views/rooms/{SearchBar.js => SearchBar.tsx} (55%) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 1e3adcb518..c1dcb81e08 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -60,7 +60,7 @@ import ScrollPanel from "./ScrollPanel"; import TimelinePanel from "./TimelinePanel"; import ErrorBoundary from "../views/elements/ErrorBoundary"; import RoomPreviewBar from "../views/rooms/RoomPreviewBar"; -import SearchBar from "../views/rooms/SearchBar"; +import SearchBar, { SearchScope } from "../views/rooms/SearchBar"; import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar"; import AuxPanel from "../views/rooms/AuxPanel"; import RoomHeader from "../views/rooms/RoomHeader"; @@ -82,6 +82,7 @@ import SpaceRoomView from "./SpaceRoomView"; import { IOpts } from "../../createRoom"; import { replaceableComponent } from "../../utils/replaceableComponent"; import UIStore from "../../stores/UIStore"; +import Search from '../views/emojipicker/Search'; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -139,7 +140,7 @@ export interface IState { draggingFile: boolean; searching: boolean; searchTerm?: string; - searchScope?: "All" | "Room"; + searchScope?: SearchScope; searchResults?: XOR<{}, { count: number; highlights: string[]; @@ -1267,7 +1268,7 @@ export default class RoomView extends React.Component { }); } - private onSearch = (term: string, scope) => { + private onSearch = (term: string, scope: SearchScope) => { this.setState({ searchTerm: term, searchScope: scope, @@ -1288,7 +1289,7 @@ export default class RoomView extends React.Component { this.searchId = new Date().getTime(); let roomId; - if (scope === "Room") roomId = this.state.room.roomId; + if (scope === SearchScope.Room) roomId = this.state.room.roomId; debuglog("sending search request"); const searchPromise = eventSearch(term, roomId); diff --git a/src/components/views/rooms/SearchBar.js b/src/components/views/rooms/SearchBar.tsx similarity index 55% rename from src/components/views/rooms/SearchBar.js rename to src/components/views/rooms/SearchBar.tsx index 029516c932..de99305d81 100644 --- a/src/components/views/rooms/SearchBar.js +++ b/src/components/views/rooms/SearchBar.tsx @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {createRef} from 'react'; +import React, {createRef, RefObject} from 'react'; import AccessibleButton from "../elements/AccessibleButton"; import classNames from "classnames"; import { _t } from '../../../languageHandler'; @@ -23,27 +23,42 @@ import {Key} from "../../../Keyboard"; import DesktopBuildsNotice, {WarningKind} from "../elements/DesktopBuildsNotice"; import {replaceableComponent} from "../../../utils/replaceableComponent"; +interface IProps { + onCancelClick: () => void; + onSearch: (query: string, scope: string) => void; + searchInProgress?: boolean; + isRoomEncrypted?: boolean; +} + +interface IState { + scope: SearchScope; +} + +export enum SearchScope { + Room = "Room", + All = "All", +} + @replaceableComponent("views.rooms.SearchBar") -export default class SearchBar extends React.Component { - constructor(props) { +export default class SearchBar extends React.Component { + private searchTerm: RefObject = createRef(); + + constructor(props: IProps) { super(props); - - this._search_term = createRef(); - this.state = { - scope: 'Room', + scope: SearchScope.Room, }; } - onThisRoomClick = () => { - this.setState({ scope: 'Room' }, () => this._searchIfQuery()); + public onThisRoomClick = () => { + this.setState({ scope: SearchScope.Room }, () => this._searchIfQuery()); }; - onAllRoomsClick = () => { - this.setState({ scope: 'All' }, () => this._searchIfQuery()); + public onAllRoomsClick = () => { + this.setState({ scope: SearchScope.All }, () => this._searchIfQuery()); }; - onSearchChange = (e) => { + public onSearchChange = (e: React.KeyboardEvent) => { switch (e.key) { case Key.ENTER: this.onSearch(); @@ -55,13 +70,13 @@ export default class SearchBar extends React.Component { }; _searchIfQuery() { - if (this._search_term.current.value) { + if (this.searchTerm.current.value) { this.onSearch(); } } onSearch = () => { - this.props.onSearch(this._search_term.current.value, this.state.scope); + this.props.onSearch(this.searchTerm.current.value, this.state.scope); }; render() { @@ -69,25 +84,41 @@ export default class SearchBar extends React.Component { mx_SearchBar_searching: this.props.searchInProgress, }); const thisRoomClasses = classNames("mx_SearchBar_button", { - mx_SearchBar_unselected: this.state.scope !== 'Room', + mx_SearchBar_unselected: this.state.scope !== SearchScope.Room, }); const allRoomsClasses = classNames("mx_SearchBar_button", { - mx_SearchBar_unselected: this.state.scope !== 'All', + mx_SearchBar_unselected: this.state.scope !== SearchScope.All, }); return ( <>
    - + {_t("This Room")} - + {_t("All Rooms")}
    - +
    From 7825c30bf773185c967c61ef3cb2a448bdf8f791 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 21 Jun 2021 15:41:47 +0100 Subject: [PATCH 61/94] Improve event index initialisation failure message in search bar for supported platforms --- .../views/elements/DesktopBuildsNotice.tsx | 19 ++++++++++++++++++- src/i18n/strings/en_EN.json | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/DesktopBuildsNotice.tsx b/src/components/views/elements/DesktopBuildsNotice.tsx index fd1c7848aa..e5e94d4bd4 100644 --- a/src/components/views/elements/DesktopBuildsNotice.tsx +++ b/src/components/views/elements/DesktopBuildsNotice.tsx @@ -14,10 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from "react"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; -import React from "react"; + +import dis from "../../../dispatcher/dispatcher"; +import { Action } from "../../../dispatcher/actions"; +import { UserTab } from "../dialogs/UserSettingsDialog"; + export enum WarningKind { Files, @@ -33,6 +38,18 @@ export default function DesktopBuildsNotice({isRoomEncrypted, kind}: IProps) { if (!isRoomEncrypted) return null; if (EventIndexPeg.get()) return null; + if (EventIndexPeg.error) { + return _t("Message search initialisation failed, check your settings for more information", {}, { + a: sub => ( { + evt.preventDefault(); + dis.dispatch({ + action: Action.ViewUserSettings, + initialTabId: UserTab.Security, + }); + }}>{sub}), + }); + } + const {desktopBuilds, brand} = SdkConfig.get(); let text = null; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b88dc79da5..37f6416460 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1933,6 +1933,7 @@ "Error loading Widget": "Error loading Widget", "Error - Mixed content": "Error - Mixed content", "Popout widget": "Popout widget", + "Message search initialisation failed, check your settings for more information": "Message search initialisation failed, check your settings for more information", "Use the Desktop app to see all encrypted files": "Use the Desktop app to see all encrypted files", "Use the Desktop app to search encrypted messages": "Use the Desktop app to search encrypted messages", "This version of %(brand)s does not support viewing some encrypted files": "This version of %(brand)s does not support viewing some encrypted files", From 88d25ad6af058d098f147553e7289606cf57a38d Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 21 Jun 2021 15:44:09 +0100 Subject: [PATCH 62/94] Fix typo in default import name --- src/components/views/voip/IncomingCallBox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/voip/IncomingCallBox.tsx b/src/components/views/voip/IncomingCallBox.tsx index 10b102832d..cd1a3afd10 100644 --- a/src/components/views/voip/IncomingCallBox.tsx +++ b/src/components/views/voip/IncomingCallBox.tsx @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import { ActionPayload } from '../../../dispatcher/payloads'; import CallHandler, { AudioID } from '../../../CallHandler'; import RoomAvatar from '../avatars/RoomAvatar'; -import AccesibleButton from '../elements/AccessibleButton'; +import AccessibleButton from '../elements/AccessibleButton'; import { CallState } from 'matrix-js-sdk/src/webrtc/call'; import {replaceableComponent} from "../../../utils/replaceableComponent"; import AccessibleTooltipButton from '../elements/AccessibleTooltipButton'; @@ -143,21 +143,21 @@ export default class IncomingCallBox extends React.Component { />
    - {_t("Decline")} - +
    - {_t("Accept")} - +
    ; } From dda4c8ec4c9fc5e00c5d9cc5525433acbf8539d9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 21 Jun 2021 21:04:29 +0100 Subject: [PATCH 63/94] Move Promise::allSettled typing from react-sdk to js-sdk --- src/@types/global.d.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 0c6b63dd33..7eff341095 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -113,19 +113,6 @@ declare global { usageDetails?: {[key: string]: number}; } - export interface ISettledFulfilled { - status: "fulfilled"; - value: T; - } - export interface ISettledRejected { - status: "rejected"; - reason: any; - } - - interface PromiseConstructor { - allSettled(promises: Promise[]): Promise | ISettledRejected>>; - } - interface HTMLAudioElement { type?: string; // sinkId & setSinkId are experimental and typescript doesn't know about them From de4065719475d6f9d6214406ffbbbfd2a393f1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 22 Jun 2021 09:12:41 +0200 Subject: [PATCH 64/94] Don't show room if we don't click on buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomDirectory.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/structures/RoomDirectory.tsx b/src/components/structures/RoomDirectory.tsx index 1e0605f263..eb7208f98d 100644 --- a/src/components/structures/RoomDirectory.tsx +++ b/src/components/structures/RoomDirectory.tsx @@ -337,11 +337,10 @@ export default class RoomDirectory extends React.Component { } private onRoomClicked = (room: IRoom, ev: ButtonEvent) => { + // If room was shift-clicked, remove it from the room directory if (ev.shiftKey && !this.state.selectedCommunityId) { ev.preventDefault(); this.removeFromDirectory(room); - } else { - this.showRoom(room); } }; From a59deeb49165429acc91032280bf8205d2b013e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 22 Jun 2021 09:16:45 +0200 Subject: [PATCH 65/94] Add onClick handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomDirectory.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomDirectory.tsx b/src/components/structures/RoomDirectory.tsx index eb7208f98d..731e2387cc 100644 --- a/src/components/structures/RoomDirectory.tsx +++ b/src/components/structures/RoomDirectory.tsx @@ -589,12 +589,23 @@ export default class RoomDirectory extends React.Component { onMouseDown={(ev) => {ev.preventDefault();}} className="mx_RoomDirectory_roomDescription" > -
    { name }
      -
    { ev.stopPropagation(); } } +
    this.onRoomClicked(room, ev)} + > + { name } +
      +
    this.onRoomClicked(room, ev)} dangerouslySetInnerHTML={{ __html: topic }} /> -
    { getDisplayAliasForRoom(room) }
    +
    this.onRoomClicked(room, ev)} + > + { getDisplayAliasForRoom(room) } +
    ,
    this.onRoomClicked(room, ev)} From deb075777d4d59cefff26c98c7149651eb729540 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 08:17:09 +0100 Subject: [PATCH 66/94] Upgrade @types/react and @types/react-dom --- package.json | 4 ++-- yarn.lock | 26 ++++++++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f232d4301b..8ebb90f342 100644 --- a/package.json +++ b/package.json @@ -132,8 +132,8 @@ "@types/pako": "^1.0.1", "@types/parse5": "^6.0.0", "@types/qrcode": "^1.3.5", - "@types/react": "^16.9", - "@types/react-dom": "^16.9.10", + "@types/react": "^17.0.2", + "@types/react-dom": "^17.0.2", "@types/react-transition-group": "^4.4.0", "@types/sanitize-html": "^2.3.1", "@types/zxcvbn": "^4.4.0", diff --git a/yarn.lock b/yarn.lock index 952d08d0f6..4f17b63337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1620,12 +1620,12 @@ dependencies: "@types/node" "*" -"@types/react-dom@^16.9.10": - version "16.9.10" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.10.tgz#4485b0bec3d41f856181b717f45fd7831101156f" - integrity sha512-ItatOrnXDMAYpv6G8UCk2VhbYVTjZT9aorLtA/OzDN9XJ2GKcfam68jutoAcILdRjsRUO8qb7AmyObF77Q8QFw== +"@types/react-dom@^17.0.2": + version "17.0.8" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.8.tgz#3180de6d79bf53762001ad854e3ce49f36dd71fc" + integrity sha512-0ohAiJAx1DAUEcY9UopnfwCE9sSMDGnY/oXjWMax6g3RpzmTt2GMyMVAXcbn0mo8XAff0SbQJl2/SBU+hjSZ1A== dependencies: - "@types/react" "^16" + "@types/react" "*" "@types/react-transition-group@^4.4.0": version "4.4.0" @@ -1634,7 +1634,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16", "@types/react@^16.14", "@types/react@^16.9": +"@types/react@*", "@types/react@^16.14": version "16.14.2" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.2.tgz#85dcc0947d0645349923c04ccef6018a1ab7538c" integrity sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ== @@ -1642,6 +1642,15 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@^17.0.2": + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" + integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/sanitize-html@^2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.3.1.tgz#094d696b83b7394b016e96342bbffa6a028795ce" @@ -1649,6 +1658,11 @@ dependencies: htmlparser2 "^6.0.0" +"@types/scheduler@*": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" + integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" From 066ef18db2eb56561bb5736b58c95b41426cb244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 22 Jun 2021 09:24:42 +0200 Subject: [PATCH 67/94] Replace onClick by onMouseDown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomDirectory.tsx | 45 ++++++++++----------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/components/structures/RoomDirectory.tsx b/src/components/structures/RoomDirectory.tsx index 731e2387cc..62944a9d98 100644 --- a/src/components/structures/RoomDirectory.tsx +++ b/src/components/structures/RoomDirectory.tsx @@ -567,11 +567,11 @@ export default class RoomDirectory extends React.Component { let avatarUrl = null; if (room.avatar_url) avatarUrl = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(32); + // We use onMouseDown instead of onClick, so that we can avoid text getting selected return [ -
    this.onRoomClicked(room, ev)} - // cancel onMouseDown otherwise shift-clicking highlights text - onMouseDown={(ev) => {ev.preventDefault();}} +
    this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomAvatar" > { url={avatarUrl} />
    , -
    this.onRoomClicked(room, ev)} - // cancel onMouseDown otherwise shift-clicking highlights text - onMouseDown={(ev) => {ev.preventDefault();}} +
    this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomDescription" >
    this.onRoomClicked(room, ev)} + onMouseDown={(ev) => this.onRoomClicked(room, ev)} > { name }
     
    this.onRoomClicked(room, ev)} + onMouseDown={(ev) => this.onRoomClicked(room, ev)} dangerouslySetInnerHTML={{ __html: topic }} />
    this.onRoomClicked(room, ev)} + onMouseDown={(ev) => this.onRoomClicked(room, ev)} > { getDisplayAliasForRoom(room) }
    , -
    this.onRoomClicked(room, ev)} - // cancel onMouseDown otherwise shift-clicking highlights text - onMouseDown={(ev) => {ev.preventDefault();}} +
    this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomMemberCount" > { room.num_joined_members }
    , -
    this.onRoomClicked(room, ev)} +
    this.onRoomClicked(room, ev)} // cancel onMouseDown otherwise shift-clicking highlights text - onMouseDown={(ev) => {ev.preventDefault();}} className="mx_RoomDirectory_preview" > - {previewButton} + { previewButton }
    , -
    this.onRoomClicked(room, ev)} - // cancel onMouseDown otherwise shift-clicking highlights text - onMouseDown={(ev) => {ev.preventDefault();}} +
    this.onRoomClicked(room, ev)} className="mx_RoomDirectory_join" > - {joinOrViewButton} + { joinOrViewButton }
    , ]; } From 8090d2b583f33f4baf5bf8b8dbb2ff1c6cdfa1de Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 09:31:15 +0100 Subject: [PATCH 68/94] Fix branch matching for BuildKite --- scripts/fetchdep.sh | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 02af402951..246add7e31 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -25,18 +25,20 @@ clone() { # First we check if GITHUB_HEAD_REF is defined, # Then we check if BUILDKITE_BRANCH is defined, # if it isn't we can assume this is a Netlify build -if [ -n ${GITHUB_HEAD_REF+x} ]; then - head=$GITHUB_HEAD_REF -elif [ -n ${BUILDKITE_BRANCH+x} ]; then - head=$BUILDKITE_BRANCH +if [ -z ${BUILDKITE_BRANCH+x} ]; then + if [ -z ${GITHUB_HEAD_REF+x} ]; then + # Netlify doesn't give us info about the fork so we have to get it from GitHub API + apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" + apiEndpoint+=$REVIEW_ID + head=$(curl $apiEndpoint | jq -r '.head.label') + else + head=$GITHUB_HEAD_REF + fi else - # Netlify doesn't give us info about the fork so we have to get it from GitHub API - apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" - apiEndpoint+=$REVIEW_ID - head=$(curl $apiEndpoint | jq -r '.head.label') + head=$BUILDKITE_BRANCH fi -# If head is set, it will contain on BuilKite either: +# If head is set, it will contain on BuildKite either: # * "branch" when the author's branch and target branch are in the same repo # * "fork:branch" when the author's branch is in their fork or if this is a Netlify build # We can split on `:` into an array to check. @@ -44,11 +46,16 @@ fi # to determine whether the branch is from a fork or not BRANCH_ARRAY=(${head//:/ }) if [[ "${#BRANCH_ARRAY[@]}" == "1" ]]; then - if [[ "$GITHUB_REPOSITORY" = "$deforg/$defrepo" ]]; then - clone $deforg $defrepo $head + if [ -z ${BUILDKITE_BRANCH+x} ]; then + if [[ "$GITHUB_REPOSITORY" == "$deforg"* ]]; then + clone $deforg $defrepo $GITHUB_HEAD_REF + else + clone $GITHUB_ACTOR $defrepo $GITHUB_HEAD_REF + fi else - clone $GITHUB_ACTOR $defrepo $head + clone $deforg $defrepo $BUILDKITE_BRANCH fi + elif [[ "${#BRANCH_ARRAY[@]}" == "2" ]]; then clone ${BRANCH_ARRAY[0]} $defrepo ${BRANCH_ARRAY[1]} fi From 3c725692704a3cecfe721c011ec4b076e816f491 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 09:53:58 +0100 Subject: [PATCH 69/94] Fix modal opening race condition Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> React 17 is hitting a race condition when a modal is closing and is trying to open another one within the same tick. A proper long term fix would be using React.createPortal to avoid manually mounting and unmounting new React roots --- src/Modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Modal.tsx b/src/Modal.tsx index ce11c571b6..2f2d5a2d52 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -385,7 +385,7 @@ export class ModalManager {
    ); - ReactDOM.render(dialog, ModalManager.getOrCreateContainer()); + setImmediate(() => ReactDOM.render(dialog, ModalManager.getOrCreateContainer())); } else { // This is safe to call repeatedly if we happen to do that ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer()); From e9d87478e2c6a4b48658de90fb70862f74ca52bb Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 22 Jun 2021 10:06:31 +0100 Subject: [PATCH 70/94] Spaces before/after curlies Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/toasts/GenericToast.tsx | 4 ++-- src/components/views/voip/IncomingCallBox.tsx | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/views/toasts/GenericToast.tsx b/src/components/views/toasts/GenericToast.tsx index ae01e8bfb7..45b65ae1fb 100644 --- a/src/components/views/toasts/GenericToast.tsx +++ b/src/components/views/toasts/GenericToast.tsx @@ -51,10 +51,10 @@ const GenericToast: React.FC> = ({
    {onReject && rejectLabel && - {rejectLabel} + { rejectLabel } } - {acceptLabel} + { acceptLabel }
    ; diff --git a/src/components/views/voip/IncomingCallBox.tsx b/src/components/views/voip/IncomingCallBox.tsx index cd1a3afd10..c09043da24 100644 --- a/src/components/views/voip/IncomingCallBox.tsx +++ b/src/components/views/voip/IncomingCallBox.tsx @@ -148,7 +148,7 @@ export default class IncomingCallBox extends React.Component { onClick={this.onRejectClick} kind="danger" > - {_t("Decline")} + { _t("Decline") }
    { onClick={this.onAnswerClick} kind="primary" > - {_t("Accept")} + { _t("Accept") }
    ; } } - From db9ffe9b3ebb4c07b645808c3048fe76df9ab8c3 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 10:18:09 +0100 Subject: [PATCH 71/94] Fix AccessibleButton label for VerificationRequest --- .../views/right_panel/VerificationPanel.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index ce39141391..d3f2ba8cbf 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -209,13 +209,19 @@ export default class VerificationPanel extends React.PureComponent
    + onClick={this.onReciprocateNoClick} + > + { _t("No") } + + onClick={this.onReciprocateYesClick} + > + { _t("Yes") } +
    ; } else { From 3d3c4284555ab95a9bc798844c16db517b0f111b Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 10:26:49 +0100 Subject: [PATCH 72/94] Fix DesktopBuildsNotice return type --- src/components/structures/RoomView.tsx | 1 - .../views/elements/DesktopBuildsNotice.tsx | 23 +++++++++++-------- src/components/views/rooms/SearchBar.tsx | 20 ++++++++-------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index c1dcb81e08..a4338e832a 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -82,7 +82,6 @@ import SpaceRoomView from "./SpaceRoomView"; import { IOpts } from "../../createRoom"; import { replaceableComponent } from "../../utils/replaceableComponent"; import UIStore from "../../stores/UIStore"; -import Search from '../views/emojipicker/Search'; const DEBUG = false; let debuglog = function(msg: string) {}; diff --git a/src/components/views/elements/DesktopBuildsNotice.tsx b/src/components/views/elements/DesktopBuildsNotice.tsx index e5e94d4bd4..426554f31e 100644 --- a/src/components/views/elements/DesktopBuildsNotice.tsx +++ b/src/components/views/elements/DesktopBuildsNotice.tsx @@ -18,7 +18,6 @@ import React from "react"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; - import dis from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; import { UserTab } from "../dialogs/UserSettingsDialog"; @@ -39,15 +38,19 @@ export default function DesktopBuildsNotice({isRoomEncrypted, kind}: IProps) { if (EventIndexPeg.get()) return null; if (EventIndexPeg.error) { - return _t("Message search initialisation failed, check your settings for more information", {}, { - a: sub => ( { - evt.preventDefault(); - dis.dispatch({ - action: Action.ViewUserSettings, - initialTabId: UserTab.Security, - }); - }}>{sub}), - }); + return <> + {_t("Message search initialisation failed, check your settings for more information", {}, { + a: sub => ( { + evt.preventDefault(); + dis.dispatch({ + action: Action.ViewUserSettings, + initialTabId: UserTab.Security, + }); + }}> + {sub} + ), + })} + ; } const {desktopBuilds, brand} = SdkConfig.get(); diff --git a/src/components/views/rooms/SearchBar.tsx b/src/components/views/rooms/SearchBar.tsx index de99305d81..47994f5251 100644 --- a/src/components/views/rooms/SearchBar.tsx +++ b/src/components/views/rooms/SearchBar.tsx @@ -15,13 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {createRef, RefObject} from 'react'; +import React, { createRef, RefObject } from 'react'; import AccessibleButton from "../elements/AccessibleButton"; import classNames from "classnames"; import { _t } from '../../../languageHandler'; import {Key} from "../../../Keyboard"; import DesktopBuildsNotice, {WarningKind} from "../elements/DesktopBuildsNotice"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { onCancelClick: () => void; @@ -50,15 +50,15 @@ export default class SearchBar extends React.Component { }; } - public onThisRoomClick = () => { - this.setState({ scope: SearchScope.Room }, () => this._searchIfQuery()); + private onThisRoomClick = () => { + this.setState({ scope: SearchScope.Room }, () => this.searchIfQuery()); }; - public onAllRoomsClick = () => { - this.setState({ scope: SearchScope.All }, () => this._searchIfQuery()); + private onAllRoomsClick = () => { + this.setState({ scope: SearchScope.All }, () => this.searchIfQuery()); }; - public onSearchChange = (e: React.KeyboardEvent) => { + private onSearchChange = (e: React.KeyboardEvent) => { switch (e.key) { case Key.ENTER: this.onSearch(); @@ -69,17 +69,17 @@ export default class SearchBar extends React.Component { } }; - _searchIfQuery() { + private searchIfQuery(): void { if (this.searchTerm.current.value) { this.onSearch(); } } - onSearch = () => { + private onSearch = (): void => { this.props.onSearch(this.searchTerm.current.value, this.state.scope); }; - render() { + public render() { const searchButtonClasses = classNames("mx_SearchBar_searchButton", { mx_SearchBar_searching: this.props.searchInProgress, }); From 66b3feb802b913b9500ee38c8807f2535eb4ec99 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 11:50:00 +0100 Subject: [PATCH 73/94] Fix keyboard accessibility of the space panel --- .../views/elements/AccessibleButton.tsx | 6 + .../views/spaces/SpaceTreeLevel.tsx | 145 +++++++++++------- 2 files changed, 98 insertions(+), 53 deletions(-) diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index e634057a21..05bcca24b2 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -62,6 +62,8 @@ export default function AccessibleButton({ disabled, inputRef, className, + onKeyDown, + onKeyUp, ...restProps }: IProps) { const newProps: IAccessibleButtonProps = restProps; @@ -83,6 +85,8 @@ export default function AccessibleButton({ if (e.key === Key.SPACE) { e.stopPropagation(); e.preventDefault(); + } else { + onKeyDown?.(e); } }; newProps.onKeyUp = (e) => { @@ -94,6 +98,8 @@ export default function AccessibleButton({ if (e.key === Key.ENTER) { e.stopPropagation(); e.preventDefault(); + } else { + onKeyUp?.(e); } }; } diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index f34baf256b..b3577e436a 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -14,23 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { createRef } from "react"; import classNames from "classnames"; -import {Room} from "matrix-js-sdk/src/models/room"; +import { Room } from "matrix-js-sdk/src/models/room"; import RoomAvatar from "../avatars/RoomAvatar"; import SpaceStore from "../../../stores/SpaceStore"; import SpaceTreeLevelLayoutStore from "../../../stores/SpaceTreeLevelLayoutStore"; import NotificationBadge from "../rooms/NotificationBadge"; -import {RovingAccessibleButton} from "../../../accessibility/roving/RovingAccessibleButton"; -import {RovingAccessibleTooltipButton} from "../../../accessibility/roving/RovingAccessibleTooltipButton"; +import { RovingAccessibleTooltipButton } from "../../../accessibility/roving/RovingAccessibleTooltipButton"; import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList, } from "../context_menus/IconizedContextMenu"; -import {_t} from "../../../languageHandler"; -import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton"; -import {toRightOf} from "../../structures/ContextMenu"; +import { _t } from "../../../languageHandler"; +import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton"; +import { toRightOf } from "../../structures/ContextMenu"; import { shouldShowSpaceSettings, showAddExistingRooms, @@ -39,15 +38,16 @@ import { showSpaceSettings, } from "../../../utils/space"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import AccessibleButton, {ButtonEvent} from "../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import defaultDispatcher from "../../../dispatcher/dispatcher"; -import {Action} from "../../../dispatcher/actions"; +import { Action } from "../../../dispatcher/actions"; import RoomViewStore from "../../../stores/RoomViewStore"; -import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload"; -import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; -import {EventType} from "matrix-js-sdk/src/@types/event"; -import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState"; -import {NotificationColor} from "../../../stores/notifications/NotificationColor"; +import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload"; +import { RightPanelPhases } from "../../../stores/RightPanelStorePhases"; +import { EventType } from "matrix-js-sdk/src/@types/event"; +import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; +import { NotificationColor } from "../../../stores/notifications/NotificationColor"; +import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager"; interface IItemProps { space?: Room; @@ -61,11 +61,14 @@ interface IItemProps { interface IItemState { collapsed: boolean; contextMenuPosition: Pick; + childSpaces: Room[]; } export class SpaceItem extends React.PureComponent { static contextType = MatrixClientContext; + private buttonRef = createRef(); + constructor(props) { super(props); @@ -78,14 +81,36 @@ export class SpaceItem extends React.PureComponent { this.state = { collapsed: collapsed, contextMenuPosition: null, + childSpaces: this.childSpaces, }; + + SpaceStore.instance.on(this.props.space.roomId, this.onSpaceUpdate); } - private toggleCollapse(evt) { - if (this.props.onExpand && this.state.collapsed) { + componentWillUnmount() { + SpaceStore.instance.off(this.props.space.roomId, this.onSpaceUpdate); + } + + private onSpaceUpdate = () => { + this.setState({ + childSpaces: this.childSpaces, + }); + }; + + private get childSpaces() { + return SpaceStore.instance.getChildSpaces(this.props.space.roomId) + .filter(s => !this.props.parents?.has(s.roomId)); + } + + private get isCollapsed() { + return this.state.collapsed || this.props.isPanelCollapsed; + } + + private toggleCollapse = evt => { + if (this.props.onExpand && this.isCollapsed) { this.props.onExpand(); } - const newCollapsedState = !this.state.collapsed; + const newCollapsedState = !this.isCollapsed; SpaceTreeLevelLayoutStore.instance.setSpaceCollapsedState( this.props.space.roomId, @@ -96,7 +121,7 @@ export class SpaceItem extends React.PureComponent { // don't bubble up so encapsulating button for space // doesn't get triggered evt.stopPropagation(); - } + }; private onContextMenu = (ev: React.MouseEvent) => { if (this.props.space.getMyMembership() !== "join") return; @@ -111,6 +136,43 @@ export class SpaceItem extends React.PureComponent { }); } + private onKeyDown = (ev: React.KeyboardEvent) => { + let handled = true; + const action = getKeyBindingsManager().getRoomListAction(ev); + const hasChildren = this.state.childSpaces?.length; + switch (action) { + case RoomListAction.CollapseSection: + if (hasChildren && !this.isCollapsed) { + this.toggleCollapse(ev); + } else { + const parentItem = this.buttonRef?.current?.parentElement?.parentElement; + const parentButton = parentItem?.previousElementSibling as HTMLElement; + parentButton?.focus(); + } + break; + + case RoomListAction.ExpandSection: + if (hasChildren) { + if (this.isCollapsed) { + this.toggleCollapse(ev); + } else { + const childLevel = this.buttonRef?.current?.nextElementSibling; + const firstSpaceItemChild = childLevel?.querySelector(".mx_SpaceItem"); + firstSpaceItemChild?.querySelector(".mx_SpaceButton")?.focus(); + } + } + break; + + default: + handled = false; + } + + if (handled) { + ev.stopPropagation(); + ev.preventDefault(); + } + }; + private onClick = (ev: React.MouseEvent) => { ev.preventDefault(); ev.stopPropagation(); @@ -302,18 +364,15 @@ export class SpaceItem extends React.PureComponent { render() { const {space, activeSpaces, isNested} = this.props; - const forceCollapsed = this.props.isPanelCollapsed; const isNarrow = this.props.isPanelCollapsed; - const collapsed = this.state.collapsed || forceCollapsed; + const collapsed = this.isCollapsed; - const childSpaces = SpaceStore.instance.getChildSpaces(space.roomId) - .filter(s => !this.props.parents?.has(s.roomId)); const isActive = activeSpaces.includes(space); const itemClasses = classNames({ "mx_SpaceItem": true, "mx_SpaceItem_narrow": isNarrow, "collapsed": collapsed, - "hasSubSpaces": childSpaces && childSpaces.length, + "hasSubSpaces": this.state.childSpaces?.length, }); const isInvite = space.getMyMembership() === "invite"; @@ -328,9 +387,9 @@ export class SpaceItem extends React.PureComponent { : SpaceStore.instance.getNotificationState(space.roomId); let childItems; - if (childSpaces && !collapsed) { + if (this.state.childSpaces?.length && !collapsed) { childItems = { const avatarSize = isNested ? 24 : 32; - const toggleCollapseButton = childSpaces && childSpaces.length ? + const toggleCollapseButton = this.state.childSpaces?.length ? this.toggleCollapse(evt)} + onClick={this.toggleCollapse} /> : null; - let button; - if (isNarrow) { - button = ( + return ( +
  • { toggleCollapseButton }
    + { !isNarrow && { space.name } } { notifBadge } { this.renderContextMenu() }
    - ); - } else { - button = ( - - { toggleCollapseButton } -
    - - { space.name } - { notifBadge } - { this.renderContextMenu() } -
    -
    - ); - } - return ( -
  • - { button } { childItems }
  • ); From 1f0fdb95cd2f52731cc1ca2253a249a00fe8a83c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 11:59:04 +0100 Subject: [PATCH 74/94] Improve accessibility of subspaces in the space panel --- src/components/views/spaces/SpaceTreeLevel.tsx | 3 +++ src/i18n/strings/en_EN.json | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index b3577e436a..cbc1cab86b 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -409,6 +409,8 @@ export class SpaceItem extends React.PureComponent { : null; return ( @@ -420,6 +422,7 @@ export class SpaceItem extends React.PureComponent { onContextMenu={this.onContextMenu} forceHide={!isNarrow || !!this.state.contextMenuPosition} role="treeitem" + aria-expanded={!collapsed} inputRef={this.buttonRef} onKeyDown={this.onKeyDown} > diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b88dc79da5..a2fb93dc17 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1038,6 +1038,8 @@ "Manage & explore rooms": "Manage & explore rooms", "Explore rooms": "Explore rooms", "Space options": "Space options", + "Expand": "Expand", + "Collapse": "Collapse", "Remove": "Remove", "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", From a7daf558bb8f911eaedbf96cd7b8564869624e92 Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 22 Jun 2021 13:03:55 +0100 Subject: [PATCH 75/94] Use proper capitalisation for Buildkite Co-authored-by: J. Ryan Stinnett --- scripts/fetchdep.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 246add7e31..c7d8daeda5 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -38,7 +38,7 @@ else head=$BUILDKITE_BRANCH fi -# If head is set, it will contain on BuildKite either: +# If head is set, it will contain on Buildkite either: # * "branch" when the author's branch and target branch are in the same repo # * "fork:branch" when the author's branch is in their fork or if this is a Netlify build # We can split on `:` into an array to check. From 660f3900f8a1369a1dabea3d8e273a5c4861e673 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 14:11:41 +0100 Subject: [PATCH 76/94] Change if statement syntax to use positive expressions --- scripts/fetchdep.sh | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index c7d8daeda5..55f068e49d 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -25,17 +25,15 @@ clone() { # First we check if GITHUB_HEAD_REF is defined, # Then we check if BUILDKITE_BRANCH is defined, # if it isn't we can assume this is a Netlify build -if [ -z ${BUILDKITE_BRANCH+x} ]; then - if [ -z ${GITHUB_HEAD_REF+x} ]; then - # Netlify doesn't give us info about the fork so we have to get it from GitHub API - apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" - apiEndpoint+=$REVIEW_ID - head=$(curl $apiEndpoint | jq -r '.head.label') - else - head=$GITHUB_HEAD_REF - fi -else +if [ -n "$BUILDKITE_BRANCH" ]; then head=$BUILDKITE_BRANCH +elif [ -n "$GITHUB_HEAD_REF" ]; then + head=$GITHUB_HEAD_REF +else + # Netlify doesn't give us info about the fork so we have to get it from GitHub API + apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" + apiEndpoint+=$REVIEW_ID + head=$(curl $apiEndpoint | jq -r '.head.label') fi # If head is set, it will contain on Buildkite either: @@ -46,7 +44,8 @@ fi # to determine whether the branch is from a fork or not BRANCH_ARRAY=(${head//:/ }) if [[ "${#BRANCH_ARRAY[@]}" == "1" ]]; then - if [ -z ${BUILDKITE_BRANCH+x} ]; then + + if [ -n "$GITHUB_HEAD_REF" ]; then if [[ "$GITHUB_REPOSITORY" == "$deforg"* ]]; then clone $deforg $defrepo $GITHUB_HEAD_REF else @@ -61,9 +60,9 @@ elif [[ "${#BRANCH_ARRAY[@]}" == "2" ]]; then fi # Try the target branch of the push or PR. -if [ -n ${GITHUB_BASE_REF+x} ]; then +if [ -n $GITHUB_BASE_REF ]; then clone $deforg $defrepo $GITHUB_BASE_REF -elif [ -n ${BUILDKITE_PULL_REQUEST_BASE_BRANCH+x} ]; then +elif [ -n $BUILDKITE_PULL_REQUEST_BASE_BRANCH ]; then clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH fi From c42f0fd2e4774c60283e33c0233f93993604184f Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 14:17:11 +0100 Subject: [PATCH 77/94] split GITHUB_REPOSITORY rather than using GITHUB_ACTOR --- scripts/fetchdep.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 55f068e49d..7d893a6039 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -49,7 +49,8 @@ if [[ "${#BRANCH_ARRAY[@]}" == "1" ]]; then if [[ "$GITHUB_REPOSITORY" == "$deforg"* ]]; then clone $deforg $defrepo $GITHUB_HEAD_REF else - clone $GITHUB_ACTOR $defrepo $GITHUB_HEAD_REF + REPO_ARRAY=(${GITHUB_REPOSITORY//\// }) + clone $REPO_ARRAY[0] $defrepo $GITHUB_HEAD_REF fi else clone $deforg $defrepo $BUILDKITE_BRANCH From ded738ce8c820cffb9b2a49f659e5febf130e4f1 Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 22 Jun 2021 14:57:44 +0100 Subject: [PATCH 78/94] Add spaces around curlies Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/SearchBar.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/SearchBar.tsx b/src/components/views/rooms/SearchBar.tsx index 47994f5251..d71bb8da73 100644 --- a/src/components/views/rooms/SearchBar.tsx +++ b/src/components/views/rooms/SearchBar.tsx @@ -19,8 +19,8 @@ import React, { createRef, RefObject } from 'react'; import AccessibleButton from "../elements/AccessibleButton"; import classNames from "classnames"; import { _t } from '../../../languageHandler'; -import {Key} from "../../../Keyboard"; -import DesktopBuildsNotice, {WarningKind} from "../elements/DesktopBuildsNotice"; +import { Key } from "../../../Keyboard"; +import DesktopBuildsNotice, { WarningKind } from "../elements/DesktopBuildsNotice"; import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { @@ -95,7 +95,7 @@ export default class SearchBar extends React.Component {
    { {_t("This Room")} Date: Tue, 22 Jun 2021 15:29:53 +0100 Subject: [PATCH 79/94] remove spurious full stop --- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 179b58b617..a9a0d15ac4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -796,7 +796,7 @@ "Show all rooms in Home": "Show all rooms in Home", "Show people in spaces": "Show people in spaces", "If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.": "If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.", - "Show notification badges for DMs in Spaces.": "Show notification badges for DMs in Spaces.", + "Show notification badges for DMs in Spaces": "Show notification badges for DMs in Spaces", "Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode", "Send and receive voice messages": "Send and receive voice messages", "Render LaTeX maths in messages": "Render LaTeX maths in messages", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index af026f4103..3937b7d821 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -193,7 +193,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { controller: new ReloadOnChangeController(), }, "feature_spaces.space_dm_badges": { - displayName: _td("Show notification badges for DMs in Spaces."), + displayName: _td("Show notification badges for DMs in Spaces"), supportedLevels: LEVELS_FEATURE, default: false, controller: new ReloadOnChangeController(), From fca2feaae8ad83cdef7eb1d426873e33bc3e9c00 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 16:09:33 +0100 Subject: [PATCH 80/94] make github env variable check first as it is new home for ci --- scripts/fetchdep.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 7d893a6039..0b15db6a23 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -25,10 +25,10 @@ clone() { # First we check if GITHUB_HEAD_REF is defined, # Then we check if BUILDKITE_BRANCH is defined, # if it isn't we can assume this is a Netlify build -if [ -n "$BUILDKITE_BRANCH" ]; then - head=$BUILDKITE_BRANCH -elif [ -n "$GITHUB_HEAD_REF" ]; then +if [ -n "$GITHUB_HEAD_REF" ]; then head=$GITHUB_HEAD_REF +elif [ -n "$BUILDKITE_BRANCH" ]; then + head=$BUILDKITE_BRANCH else # Netlify doesn't give us info about the fork so we have to get it from GitHub API apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" From b092686453604cb37df602e6fcc796418688c022 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 22 Jun 2021 16:14:01 +0100 Subject: [PATCH 81/94] improve comment grammar --- scripts/fetchdep.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 0b15db6a23..0990af70ce 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -24,7 +24,7 @@ clone() { # Try the PR author's branch in case it exists on the deps as well. # First we check if GITHUB_HEAD_REF is defined, # Then we check if BUILDKITE_BRANCH is defined, -# if it isn't we can assume this is a Netlify build +# if they aren't we can assume this is a Netlify build if [ -n "$GITHUB_HEAD_REF" ]; then head=$GITHUB_HEAD_REF elif [ -n "$BUILDKITE_BRANCH" ]; then From 6d92953311ed49567a0dad80e2ff81df9cbcd417 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 17:22:38 +0100 Subject: [PATCH 82/94] Convert bunch of utils to TS --- ...tateTransfer.js => EditorStateTransfer.ts} | 30 ++++---- src/utils/{ErrorUtils.js => ErrorUtils.tsx} | 16 +++-- src/utils/{EventUtils.js => EventUtils.ts} | 17 +++-- ...yServerUtils.js => IdentityServerUtils.ts} | 11 +-- ...ssageDiffUtils.js => MessageDiffUtils.tsx} | 68 ++++++++++--------- .../{PinningUtils.js => PinningUtils.ts} | 4 +- src/utils/{Receipt.js => Receipt.ts} | 6 +- .../{ResizeNotifier.js => ResizeNotifier.ts} | 45 ++++++------ ...eMatrixClient.js => createMatrixClient.ts} | 21 +++--- 9 files changed, 120 insertions(+), 98 deletions(-) rename src/utils/{EditorStateTransfer.js => EditorStateTransfer.ts} (58%) rename src/utils/{ErrorUtils.js => ErrorUtils.tsx} (84%) rename src/utils/{EventUtils.js => EventUtils.ts} (85%) rename src/utils/{IdentityServerUtils.js => IdentityServerUtils.ts} (82%) rename src/utils/{MessageDiffUtils.js => MessageDiffUtils.tsx} (84%) rename src/utils/{PinningUtils.js => PinningUtils.ts} (89%) rename src/utils/{Receipt.js => Receipt.ts} (83%) rename src/utils/{ResizeNotifier.js => ResizeNotifier.ts} (62%) rename src/utils/{createMatrixClient.js => createMatrixClient.ts} (76%) diff --git a/src/utils/EditorStateTransfer.js b/src/utils/EditorStateTransfer.ts similarity index 58% rename from src/utils/EditorStateTransfer.js rename to src/utils/EditorStateTransfer.ts index c7782a9ea8..42e1a316d6 100644 --- a/src/utils/EditorStateTransfer.js +++ b/src/utils/EditorStateTransfer.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 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,36 +14,40 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + +import { SerializedPart } from "../editor/parts"; +import { Caret } from "../editor/caret"; + /** * Used while editing, to pass the event, and to preserve editor state * from one editor instance to another when remounting the editor * upon receiving the remote echo for an unsent event. */ export default class EditorStateTransfer { - constructor(event) { - this._event = event; - this._serializedParts = null; - this.caret = null; - } + private serializedParts: SerializedPart[] = null; + private caret: Caret = null; - setEditorState(caret, serializedParts) { - this._caret = caret; - this._serializedParts = serializedParts; + constructor(private readonly event: MatrixEvent) {} + + setEditorState(caret: Caret, serializedParts: SerializedPart[]) { + this.caret = caret; + this.serializedParts = serializedParts; } hasEditorState() { - return !!this._serializedParts; + return !!this.serializedParts; } getSerializedParts() { - return this._serializedParts; + return this.serializedParts; } getCaret() { - return this._caret; + return this.caret; } getEvent() { - return this._event; + return this.event; } } diff --git a/src/utils/ErrorUtils.js b/src/utils/ErrorUtils.tsx similarity index 84% rename from src/utils/ErrorUtils.js rename to src/utils/ErrorUtils.tsx index b5bd5b0af0..c39ee21f09 100644 --- a/src/utils/ErrorUtils.js +++ b/src/utils/ErrorUtils.tsx @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018 - 2021 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,7 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { _t, _td } from '../languageHandler'; +import React, { ReactNode } from "react"; +import { MatrixError } from "matrix-js-sdk/src/http-api"; + +import { _t, _td, Tags, TranslatedString } from '../languageHandler'; /** * Produce a translated error message for a @@ -30,7 +33,12 @@ import { _t, _td } from '../languageHandler'; * for any tags in the strings apart from 'a' * @returns {*} Translated string or react component */ -export function messageForResourceLimitError(limitType, adminContact, strings, extraTranslations) { +export function messageForResourceLimitError( + limitType: string, + adminContact: string, + strings: Record, + extraTranslations?: Tags, +): TranslatedString { let errString = strings[limitType]; if (errString === undefined) errString = strings['']; @@ -49,7 +57,7 @@ export function messageForResourceLimitError(limitType, adminContact, strings, e } } -export function messageForSyncError(err) { +export function messageForSyncError(err: MatrixError | Error): ReactNode { if (err.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { const limitError = messageForResourceLimitError( err.data.limit_type, diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.ts similarity index 85% rename from src/utils/EventUtils.js rename to src/utils/EventUtils.ts index be21896417..3d9c60d9cd 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 - 2021 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,9 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventStatus } from 'matrix-js-sdk/src/models/event'; -import {MatrixClientPeg} from '../MatrixClientPeg'; +import { Room } from 'matrix-js-sdk/src/models/room'; +import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event'; + +import { MatrixClientPeg } from '../MatrixClientPeg'; import shouldHideEvent from "../shouldHideEvent"; + /** * Returns whether an event should allow actions like reply, reactions, edit, etc. * which effectively checks whether it's a regular message that has been sent and that we @@ -25,7 +28,7 @@ import shouldHideEvent from "../shouldHideEvent"; * @param {MatrixEvent} mxEvent The event to check * @returns {boolean} true if actionable */ -export function isContentActionable(mxEvent) { +export function isContentActionable(mxEvent: MatrixEvent): boolean { const { status: eventStatus } = mxEvent; // status is SENT before remote-echo, null after @@ -45,7 +48,7 @@ export function isContentActionable(mxEvent) { return false; } -export function canEditContent(mxEvent) { +export function canEditContent(mxEvent: MatrixEvent): boolean { if (mxEvent.status === EventStatus.CANCELLED || mxEvent.getType() !== "m.room.message" || mxEvent.isRedacted()) { return false; } @@ -56,7 +59,7 @@ export function canEditContent(mxEvent) { mxEvent.getSender() === MatrixClientPeg.get().getUserId(); } -export function canEditOwnEvent(mxEvent) { +export function canEditOwnEvent(mxEvent: MatrixEvent): boolean { // for now we only allow editing // your own events. So this just call through // In the future though, moderators will be able to @@ -67,7 +70,7 @@ export function canEditOwnEvent(mxEvent) { } const MAX_JUMP_DISTANCE = 100; -export function findEditableEvent(room, isForward, fromEventId = undefined) { +export function findEditableEvent(room: Room, isForward: boolean, fromEventId: string = undefined): MatrixEvent { const liveTimeline = room.getLiveTimeline(); const events = liveTimeline.getEvents().concat(room.getPendingEvents()); const maxIdx = events.length - 1; diff --git a/src/utils/IdentityServerUtils.js b/src/utils/IdentityServerUtils.ts similarity index 82% rename from src/utils/IdentityServerUtils.js rename to src/utils/IdentityServerUtils.ts index 5ece308954..2476adca19 100644 --- a/src/utils/IdentityServerUtils.js +++ b/src/utils/IdentityServerUtils.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,14 +15,15 @@ limitations under the License. */ import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; + import SdkConfig from '../SdkConfig'; import {MatrixClientPeg} from '../MatrixClientPeg'; -export function getDefaultIdentityServerUrl() { +export function getDefaultIdentityServerUrl(): string { return SdkConfig.get()['validated_server_config']['isUrl']; } -export function useDefaultIdentityServer() { +export function useDefaultIdentityServer(): void { const url = getDefaultIdentityServerUrl(); // Account data change will update localstorage, client, etc through dispatcher MatrixClientPeg.get().setAccountData("m.identity_server", { @@ -30,7 +31,7 @@ export function useDefaultIdentityServer() { }); } -export async function doesIdentityServerHaveTerms(fullUrl) { +export async function doesIdentityServerHaveTerms(fullUrl: string): Promise { let terms; try { terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl); @@ -46,7 +47,7 @@ export async function doesIdentityServerHaveTerms(fullUrl) { return terms && terms["policies"] && (Object.keys(terms["policies"]).length > 0); } -export function doesAccountDataHaveIdentityServer() { +export function doesAccountDataHaveIdentityServer(): boolean { const event = MatrixClientPeg.get().getAccountData("m.identity_server"); return event && event.getContent() && event.getContent()['base_url']; } diff --git a/src/utils/MessageDiffUtils.js b/src/utils/MessageDiffUtils.tsx similarity index 84% rename from src/utils/MessageDiffUtils.js rename to src/utils/MessageDiffUtils.tsx index 7398173fdd..b5d5e31432 100644 --- a/src/utils/MessageDiffUtils.js +++ b/src/utils/MessageDiffUtils.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 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,31 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import classNames from 'classnames'; -import DiffMatchPatch from 'diff-match-patch'; -import {DiffDOM} from "diff-dom"; -import { checkBlockNode, bodyToHtml } from "../HtmlUtils"; +import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch'; +import { Action, DiffDOM, IDiff } from "diff-dom"; +import { IContent } from "matrix-js-sdk/src/models/event"; + +import { bodyToHtml, checkBlockNode, IOptsReturnString } from "../HtmlUtils"; const decodeEntities = (function() { let textarea = null; - return function(string) { + return function(str: string): string { if (!textarea) { textarea = document.createElement("textarea"); } - textarea.innerHTML = string; + textarea.innerHTML = str; return textarea.value; }; })(); -function textToHtml(text) { +function textToHtml(text: string): string { const container = document.createElement("div"); container.textContent = text; return container.innerHTML; } -function getSanitizedHtmlBody(content) { - const opts = { +function getSanitizedHtmlBody(content: IContent): string { + const opts: IOptsReturnString = { stripReplyFallback: true, returnString: true, }; @@ -57,21 +59,21 @@ function getSanitizedHtmlBody(content) { } } -function wrapInsertion(child) { +function wrapInsertion(child: Node): HTMLElement { const wrapper = document.createElement(checkBlockNode(child) ? "div" : "span"); wrapper.className = "mx_EditHistoryMessage_insertion"; wrapper.appendChild(child); return wrapper; } -function wrapDeletion(child) { +function wrapDeletion(child: Node): HTMLElement { const wrapper = document.createElement(checkBlockNode(child) ? "div" : "span"); wrapper.className = "mx_EditHistoryMessage_deletion"; wrapper.appendChild(child); return wrapper; } -function findRefNodes(root, route, isAddition) { +function findRefNodes(root: Node, route: number[], isAddition = false) { let refNode = root; let refParentNode; const end = isAddition ? route.length - 1 : route.length; @@ -79,7 +81,7 @@ function findRefNodes(root, route, isAddition) { refParentNode = refNode; refNode = refNode.childNodes[route[i]]; } - return {refNode, refParentNode}; + return { refNode, refParentNode }; } function diffTreeToDOM(desc) { @@ -101,7 +103,7 @@ function diffTreeToDOM(desc) { } } -function insertBefore(parent, nextSibling, child) { +function insertBefore(parent: Node, nextSibling: Node | null, child: Node): void { if (nextSibling) { parent.insertBefore(child, nextSibling); } else { @@ -109,7 +111,7 @@ function insertBefore(parent, nextSibling, child) { } } -function isRouteOfNextSibling(route1, route2) { +function isRouteOfNextSibling(route1: number[], route2: number[]): boolean { // routes are arrays with indices, // to be interpreted as a path in the dom tree @@ -127,7 +129,7 @@ function isRouteOfNextSibling(route1, route2) { return route2[lastD1Idx] >= route1[lastD1Idx]; } -function adjustRoutes(diff, remainingDiffs) { +function adjustRoutes(diff: IDiff, remainingDiffs: IDiff[]): void { if (diff.action === "removeTextElement" || diff.action === "removeElement") { // as removed text is not removed from the html, but marked as deleted, // we need to readjust indices that assume the current node has been removed. @@ -140,14 +142,14 @@ function adjustRoutes(diff, remainingDiffs) { } } -function stringAsTextNode(string) { +function stringAsTextNode(string: string): Text { return document.createTextNode(decodeEntities(string)); } -function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { +function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatch: DiffMatchPatch): void { const {refNode, refParentNode} = findRefNodes(originalRootNode, diff.route); switch (diff.action) { - case "replaceElement": { + case Action.ReplaceElement: { const container = document.createElement("span"); const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue)); const insNode = wrapInsertion(diffTreeToDOM(diff.newValue)); @@ -156,22 +158,22 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { refNode.parentNode.replaceChild(container, refNode); break; } - case "removeTextElement": { + case Action.RemoveTextElement: { const delNode = wrapDeletion(stringAsTextNode(diff.value)); refNode.parentNode.replaceChild(delNode, refNode); break; } - case "removeElement": { + case Action.RemoveElement: { const delNode = wrapDeletion(diffTreeToDOM(diff.element)); refNode.parentNode.replaceChild(delNode, refNode); break; } - case "modifyTextElement": { + case Action.ModifyTextElement: { const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue); diffMathPatch.diff_cleanupSemantic(textDiffs); const container = document.createElement("span"); for (const [modifier, text] of textDiffs) { - let textDiffNode = stringAsTextNode(text); + let textDiffNode: Node = stringAsTextNode(text); if (modifier < 0) { textDiffNode = wrapDeletion(textDiffNode); } else if (modifier > 0) { @@ -182,12 +184,12 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { refNode.parentNode.replaceChild(container, refNode); break; } - case "addElement": { + case Action.AddElement: { const insNode = wrapInsertion(diffTreeToDOM(diff.element)); insertBefore(refParentNode, refNode, insNode); break; } - case "addTextElement": { + case Action.AddTextElement: { // XXX: sometimes diffDOM says insert a newline when there shouldn't be one // but we must insert the node anyway so that we don't break the route child IDs. // See https://github.com/fiduswriter/diffDOM/issues/100 @@ -197,11 +199,11 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { } // e.g. when changing a the href of a link, // show the link with old href as removed and with the new href as added - case "removeAttribute": - case "addAttribute": - case "modifyAttribute": { + case Action.RemoveAttribute: + case Action.AddAttribute: + case Action.ModifyAttribute: { const delNode = wrapDeletion(refNode.cloneNode(true)); - const updatedNode = refNode.cloneNode(true); + const updatedNode = refNode.cloneNode(true) as HTMLElement; if (diff.action === "addAttribute" || diff.action === "modifyAttribute") { updatedNode.setAttribute(diff.name, diff.newValue); } else { @@ -220,12 +222,12 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { } } -function routeIsEqual(r1, r2) { +function routeIsEqual(r1: number[], r2: number[]): boolean { return r1.length === r2.length && !r1.some((e, i) => e !== r2[i]); } // workaround for https://github.com/fiduswriter/diffDOM/issues/90 -function filterCancelingOutDiffs(originalDiffActions) { +function filterCancelingOutDiffs(originalDiffActions: IDiff[]): IDiff[] { const diffActions = originalDiffActions.slice(); for (let i = 0; i < diffActions.length; ++i) { @@ -252,7 +254,7 @@ function filterCancelingOutDiffs(originalDiffActions) { * @param {object} editContent the content for the edit message * @return {object} a react element similar to what `bodyToHtml` returns */ -export function editBodyDiffToHtml(originalContent, editContent) { +export function editBodyDiffToHtml(originalContent: IContent, editContent: IContent): ReactNode { // wrap the body in a div, DiffDOM needs a root element const originalBody = `
    ${getSanitizedHtmlBody(originalContent)}
    `; const editBody = `
    ${getSanitizedHtmlBody(editContent)}
    `; diff --git a/src/utils/PinningUtils.js b/src/utils/PinningUtils.ts similarity index 89% rename from src/utils/PinningUtils.js rename to src/utils/PinningUtils.ts index 90d26cc988..ec1eeccf1f 100644 --- a/src/utils/PinningUtils.js +++ b/src/utils/PinningUtils.ts @@ -14,13 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + export default class PinningUtils { /** * Determines if the given event may be pinned. * @param {MatrixEvent} event The event to check. * @return {boolean} True if the event may be pinned, false otherwise. */ - static isPinnable(event) { + static isPinnable(event: MatrixEvent): boolean { if (!event) return false; if (event.getType() !== "m.room.message") return false; if (event.isRedacted()) return false; diff --git a/src/utils/Receipt.js b/src/utils/Receipt.ts similarity index 83% rename from src/utils/Receipt.js rename to src/utils/Receipt.ts index d88c67fb18..2a626decc4 100644 --- a/src/utils/Receipt.js +++ b/src/utils/Receipt.ts @@ -1,5 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd +Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + /** * Given MatrixEvent containing receipts, return the first * read receipt from the given user ID, or null if no such @@ -23,7 +25,7 @@ limitations under the License. * @param {string} userId A user ID * @returns {Object} Read receipt */ -export function findReadReceiptFromUserId(receiptEvent, userId) { +export function findReadReceiptFromUserId(receiptEvent: MatrixEvent, userId: string): object | null { const receiptKeys = Object.keys(receiptEvent.getContent()); for (let i = 0; i < receiptKeys.length; ++i) { const rcpt = receiptEvent.getContent()[receiptKeys[i]]; diff --git a/src/utils/ResizeNotifier.js b/src/utils/ResizeNotifier.ts similarity index 62% rename from src/utils/ResizeNotifier.js rename to src/utils/ResizeNotifier.ts index 4d46d10f6c..8bb7f52e57 100644 --- a/src/utils/ResizeNotifier.js +++ b/src/utils/ResizeNotifier.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 - 2021 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. @@ -22,59 +22,58 @@ limitations under the License. * Fires when the middle panel has been resized by a pixel. * @event module:utils~ResizeNotifier#"middlePanelResizedNoisy" */ + import { EventEmitter } from "events"; import { throttle } from "lodash"; export default class ResizeNotifier extends EventEmitter { - constructor() { - super(); - // with default options, will call fn once at first call, and then every x ms - // if there was another call in that timespan - this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200); - this._isResizing = false; - } + private _isResizing = false; - get isResizing() { + // with default options, will call fn once at first call, and then every x ms + // if there was another call in that timespan + private throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200); + + public get isResizing() { return this._isResizing; } - startResizing() { + public startResizing() { this._isResizing = true; this.emit("isResizing", true); } - stopResizing() { + public stopResizing() { this._isResizing = false; this.emit("isResizing", false); } - _noisyMiddlePanel() { + private noisyMiddlePanel() { this.emit("middlePanelResizedNoisy"); } - _updateMiddlePanel() { - this._throttledMiddlePanel(); - this._noisyMiddlePanel(); + private updateMiddlePanel() { + this.throttledMiddlePanel(); + this.noisyMiddlePanel(); } // can be called in quick succession - notifyLeftHandleResized() { + public notifyLeftHandleResized() { // don't emit event for own region - this._updateMiddlePanel(); + this.updateMiddlePanel(); } // can be called in quick succession - notifyRightHandleResized() { - this._updateMiddlePanel(); + public notifyRightHandleResized() { + this.updateMiddlePanel(); } - notifyTimelineHeightChanged() { - this._updateMiddlePanel(); + public notifyTimelineHeightChanged() { + this.updateMiddlePanel(); } // can be called in quick succession - notifyWindowResized() { - this._updateMiddlePanel(); + public notifyWindowResized() { + this.updateMiddlePanel(); } } diff --git a/src/utils/createMatrixClient.js b/src/utils/createMatrixClient.ts similarity index 76% rename from src/utils/createMatrixClient.js rename to src/utils/createMatrixClient.ts index f5e196d846..caaf75616d 100644 --- a/src/utils/createMatrixClient.js +++ b/src/utils/createMatrixClient.ts @@ -1,5 +1,5 @@ /* -Copyright 2017 Vector Creations Ltd +Copyright 2017 - 2021 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,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {createClient} from "matrix-js-sdk/src/matrix"; -import {IndexedDBCryptoStore} from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; -import {WebStorageSessionStore} from "matrix-js-sdk/src/store/session/webstorage"; -import {IndexedDBStore} from "matrix-js-sdk/src/store/indexeddb"; +import { createClient, ICreateClientOpts } from "matrix-js-sdk/src/matrix"; +import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; +import { WebStorageSessionStore } from "matrix-js-sdk/src/store/session/webstorage"; +import { IndexedDBStore } from "matrix-js-sdk/src/store/indexeddb"; const localStorage = window.localStorage; @@ -41,8 +41,8 @@ try { * * @returns {MatrixClient} the newly-created MatrixClient */ -export default function createMatrixClient(opts) { - const storeOpts = { +export default function createMatrixClient(opts: ICreateClientOpts) { + const storeOpts: Partial = { useAuthorizationHeader: true, }; @@ -65,9 +65,10 @@ export default function createMatrixClient(opts) { ); } - opts = Object.assign(storeOpts, opts); - - return createClient(opts); + return createClient({ + ...storeOpts, + ...opts, + }); } createMatrixClient.indexedDbWorkerScript = null; From a839d0f3963eb00f22db2f205c4d285d34125a3a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 17:23:13 +0100 Subject: [PATCH 83/94] More typescript conversion --- package.json | 1 + src/@types/diff-dom.ts | 50 +++++++++++++++++ ...Tracker.js => DecryptionFailureTracker.ts} | 53 ++++++++++--------- src/HtmlUtils.tsx | 50 +++++++++-------- src/{Rooms.js => Rooms.ts} | 16 +++--- src/{Unread.js => Unread.ts} | 47 +++++++++------- src/components/structures/MatrixChat.tsx | 2 +- src/components/views/rooms/AuxPanel.tsx | 2 +- src/components/views/rooms/EventTile.tsx | 27 +++++----- src/components/views/rooms/RoomList.tsx | 2 +- src/components/views/rooms/RoomSublist.tsx | 2 +- src/languageHandler.tsx | 2 +- yarn.lock | 5 ++ 13 files changed, 166 insertions(+), 93 deletions(-) create mode 100644 src/@types/diff-dom.ts rename src/{DecryptionFailureTracker.js => DecryptionFailureTracker.ts} (80%) rename src/{Rooms.js => Rooms.ts} (89%) rename src/{Unread.js => Unread.ts} (75%) diff --git a/package.json b/package.json index f232d4301b..756604269d 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "@sinonjs/fake-timers": "^7.0.2", "@types/classnames": "^2.2.11", "@types/counterpart": "^0.18.1", + "@types/diff-match-patch": "^1.0.5", "@types/flux": "^3.1.9", "@types/jest": "^26.0.20", "@types/linkifyjs": "^2.1.3", diff --git a/src/@types/diff-dom.ts b/src/@types/diff-dom.ts new file mode 100644 index 0000000000..884ee6126d --- /dev/null +++ b/src/@types/diff-dom.ts @@ -0,0 +1,50 @@ +/* +Copyright 2021 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. +*/ + +declare module "diff-dom" { + enum Action { + AddElement = "addElement", + AddTextElement = "addTextElement", + RemoveTextElement = "removeTextElement", + RemoveElement = "removeElement", + ReplaceElement = "replaceElement", + ModifyTextElement = "modifyTextElement", + AddAttribute = "addAttribute", + RemoveAttribute = "removeAttribute", + ModifyAttribute = "modifyAttribute", + } + + export interface IDiff { + action: Action; + name: string; + text?: string; + route: number[]; + value: string; + element: unknown; + oldValue: string; + newValue: string; + } + + interface IOpts { + } + + export class DiffDOM { + public constructor(opts?: IOpts); + public apply(tree: unknown, diffs: IDiff[]): unknown; + public undo(tree: unknown, diffs: IDiff[]): unknown; + public diff(a: HTMLElement | string, b: HTMLElement | string): IDiff[]; + } +} diff --git a/src/DecryptionFailureTracker.js b/src/DecryptionFailureTracker.ts similarity index 80% rename from src/DecryptionFailureTracker.js rename to src/DecryptionFailureTracker.ts index b02a5e937b..960d844e9e 100644 --- a/src/DecryptionFailureTracker.js +++ b/src/DecryptionFailureTracker.ts @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018 - 2021 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,34 +14,40 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixError } from "matrix-js-sdk/src/http-api"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + export class DecryptionFailure { - constructor(failedEventId, errorCode) { - this.failedEventId = failedEventId; - this.errorCode = errorCode; + public readonly ts: number; + + constructor(public readonly failedEventId: string, public readonly errorCode: string) { this.ts = Date.now(); } } +type Fn = (count: number, trackedErrCode: string) => void; +type ErrCodeMapFn = (errcode: string) => string; + export class DecryptionFailureTracker { // Array of items of type DecryptionFailure. Every `CHECK_INTERVAL_MS`, this list // is checked for failures that happened > `GRACE_PERIOD_MS` ago. Those that did // are accumulated in `failureCounts`. - failures = []; + public failures: DecryptionFailure[] = []; // A histogram of the number of failures that will be tracked at the next tracking // interval, split by failure error code. - failureCounts = { + public failureCounts: Record = { // [errorCode]: 42 }; // Event IDs of failures that were tracked previously - trackedEventHashMap = { + public trackedEventHashMap: Record = { // [eventId]: true }; // Set to an interval ID when `start` is called - checkInterval = null; - trackInterval = null; + public checkInterval: NodeJS.Timeout = null; + public trackInterval: NodeJS.Timeout = null; // Spread the load on `Analytics` by tracking at a low frequency, `TRACK_INTERVAL_MS`. static TRACK_INTERVAL_MS = 60000; @@ -67,7 +73,7 @@ export class DecryptionFailureTracker { * @param {function?} errorCodeMapFn The function used to map error codes to the * trackedErrorCode. If not provided, the `.code` of errors will be used. */ - constructor(fn, errorCodeMapFn) { + constructor(private readonly fn: Fn, private readonly errorCodeMapFn?: ErrCodeMapFn) { if (!fn || typeof fn !== 'function') { throw new Error('DecryptionFailureTracker requires tracking function'); } @@ -75,9 +81,6 @@ export class DecryptionFailureTracker { if (errorCodeMapFn && typeof errorCodeMapFn !== 'function') { throw new Error('DecryptionFailureTracker second constructor argument should be a function'); } - - this._trackDecryptionFailure = fn; - this._mapErrorCode = errorCodeMapFn; } // loadTrackedEventHashMap() { @@ -88,7 +91,7 @@ export class DecryptionFailureTracker { // localStorage.setItem('mx-decryption-failure-event-id-hashes', JSON.stringify(this.trackedEventHashMap)); // } - eventDecrypted(e, err) { + public eventDecrypted(e: MatrixEvent, err: MatrixError | Error): void { if (err) { this.addDecryptionFailure(new DecryptionFailure(e.getId(), err.code)); } else { @@ -97,18 +100,18 @@ export class DecryptionFailureTracker { } } - addDecryptionFailure(failure) { + public addDecryptionFailure(failure: DecryptionFailure): void { this.failures.push(failure); } - removeDecryptionFailuresForEvent(e) { + public removeDecryptionFailuresForEvent(e: MatrixEvent): void { this.failures = this.failures.filter((f) => f.failedEventId !== e.getId()); } /** * Start checking for and tracking failures. */ - start() { + public start(): void { this.checkInterval = setInterval( () => this.checkFailures(Date.now()), DecryptionFailureTracker.CHECK_INTERVAL_MS, @@ -123,7 +126,7 @@ export class DecryptionFailureTracker { /** * Clear state and stop checking for and tracking failures. */ - stop() { + public stop(): void { clearInterval(this.checkInterval); clearInterval(this.trackInterval); @@ -132,11 +135,11 @@ export class DecryptionFailureTracker { } /** - * Mark failures that occured before nowTs - GRACE_PERIOD_MS as failures that should be + * Mark failures that occurred before nowTs - GRACE_PERIOD_MS as failures that should be * tracked. Only mark one failure per event ID. * @param {number} nowTs the timestamp that represents the time now. */ - checkFailures(nowTs) { + public checkFailures(nowTs: number): void { const failuresGivenGrace = []; const failuresNotReady = []; while (this.failures.length > 0) { @@ -175,10 +178,10 @@ export class DecryptionFailureTracker { const dedupedFailures = dedupedFailuresMap.values(); - this._aggregateFailures(dedupedFailures); + this.aggregateFailures(dedupedFailures); } - _aggregateFailures(failures) { + private aggregateFailures(failures: DecryptionFailure[]): void { for (const failure of failures) { const errorCode = failure.errorCode; this.failureCounts[errorCode] = (this.failureCounts[errorCode] || 0) + 1; @@ -189,12 +192,12 @@ export class DecryptionFailureTracker { * If there are failures that should be tracked, call the given trackDecryptionFailure * function with the number of failures that should be tracked. */ - trackFailures() { + public trackFailures(): void { for (const errorCode of Object.keys(this.failureCounts)) { if (this.failureCounts[errorCode] > 0) { - const trackedErrorCode = this._mapErrorCode ? this._mapErrorCode(errorCode) : errorCode; + const trackedErrorCode = this.errorCodeMapFn ? this.errorCodeMapFn(errorCode) : errorCode; - this._trackDecryptionFailure(this.failureCounts[errorCode], trackedErrorCode); + this.fn(this.failureCounts[errorCode], trackedErrorCode); this.failureCounts[errorCode] = 0; } } diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index ef5ac383e3..5803029030 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -17,11 +17,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import sanitizeHtml from 'sanitize-html'; -import { IExtendedSanitizeOptions } from './@types/sanitize-html'; +import cheerio from 'cheerio'; import * as linkify from 'linkifyjs'; -import linkifyMatrix from './linkify-matrix'; import _linkifyElement from 'linkifyjs/element'; import _linkifyString from 'linkifyjs/string'; import classNames from 'classnames'; @@ -29,13 +28,15 @@ import EMOJIBASE_REGEX from 'emojibase-regex'; import url from 'url'; import katex from 'katex'; import { AllHtmlEntities } from 'html-entities'; -import SettingsStore from './settings/SettingsStore'; -import cheerio from 'cheerio'; +import { IContent } from 'matrix-js-sdk/src/models/event'; -import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; -import {SHORTCODE_TO_EMOJI, getEmojiFromUnicode} from "./emoji"; +import { IExtendedSanitizeOptions } from './@types/sanitize-html'; +import linkifyMatrix from './linkify-matrix'; +import SettingsStore from './settings/SettingsStore'; +import { tryTransformPermalinkToLocalHref } from "./utils/permalinks/Permalinks"; +import { SHORTCODE_TO_EMOJI, getEmojiFromUnicode } from "./emoji"; import ReplyThread from "./components/views/elements/ReplyThread"; -import {mediaFromMxc} from "./customisations/Media"; +import { mediaFromMxc } from "./customisations/Media"; linkifyMatrix(linkify); @@ -66,7 +67,7 @@ export const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet' * need emojification. * unicodeToImage uses this function. */ -function mightContainEmoji(str: string) { +function mightContainEmoji(str: string): boolean { return SURROGATE_PAIR_PATTERN.test(str) || SYMBOL_PATTERN.test(str); } @@ -76,7 +77,7 @@ function mightContainEmoji(str: string) { * @param {String} char The emoji character * @return {String} The shortcode (such as :thumbup:) */ -export function unicodeToShortcode(char: string) { +export function unicodeToShortcode(char: string): string { const data = getEmojiFromUnicode(char); return (data && data.shortcodes ? `:${data.shortcodes[0]}:` : ''); } @@ -87,7 +88,7 @@ export function unicodeToShortcode(char: string) { * @param {String} shortcode The shortcode (such as :thumbup:) * @return {String} The emoji character; null if none exists */ -export function shortcodeToUnicode(shortcode: string) { +export function shortcodeToUnicode(shortcode: string): string { shortcode = shortcode.slice(1, shortcode.length - 1); const data = SHORTCODE_TO_EMOJI.get(shortcode); return data ? data.unicode : null; @@ -124,13 +125,13 @@ export function processHtmlForSending(html: string): string { * Given an untrusted HTML string, return a React node with an sanitized version * of that HTML. */ -export function sanitizedHtmlNode(insaneHtml: string) { +export function sanitizedHtmlNode(insaneHtml: string): ReactNode { const saneHtml = sanitizeHtml(insaneHtml, sanitizeHtmlParams); return
    ; } -export function getHtmlText(insaneHtml: string) { +export function getHtmlText(insaneHtml: string): string { return sanitizeHtml(insaneHtml, { allowedTags: [], allowedAttributes: {}, @@ -148,7 +149,7 @@ export function getHtmlText(insaneHtml: string) { * other places we need to sanitise URLs. * @return true if permitted, otherwise false */ -export function isUrlPermitted(inputUrl: string) { +export function isUrlPermitted(inputUrl: string): boolean { try { const parsed = url.parse(inputUrl); if (!parsed.protocol) return false; @@ -351,13 +352,6 @@ class HtmlHighlighter extends BaseHighlighter { } } -interface IContent { - format?: string; - // eslint-disable-next-line camelcase - formatted_body?: string; - body: string; -} - interface IOpts { highlightLink?: string; disableBigEmoji?: boolean; @@ -367,6 +361,14 @@ interface IOpts { ref?: React.Ref; } +export interface IOptsReturnNode extends IOpts { + returnString: false; +} + +export interface IOptsReturnString extends IOpts { + returnString: true; +} + /* turn a matrix event body into html * * content: 'content' of the MatrixEvent @@ -380,6 +382,8 @@ interface IOpts { * opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer * opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString) */ +export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnString): string; +export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnNode): ReactNode; export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts = {}) { const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body; let bodyHasEmoji = false; @@ -523,7 +527,7 @@ export function linkifyElement(element: HTMLElement, options = linkifyMatrix.opt * @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options * @returns {string} */ -export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatrix.options) { +export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatrix.options): string { return sanitizeHtml(linkifyString(dirtyHtml, options), sanitizeHtmlParams); } @@ -534,7 +538,7 @@ export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatri * @param {Node} node * @returns {bool} */ -export function checkBlockNode(node: Node) { +export function checkBlockNode(node: Node): boolean { switch (node.nodeName) { case "H1": case "H2": diff --git a/src/Rooms.js b/src/Rooms.ts similarity index 89% rename from src/Rooms.js rename to src/Rooms.ts index 955498faaa..19d1c9ee05 100644 --- a/src/Rooms.js +++ b/src/Rooms.ts @@ -1,5 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd +Copyright 2015 - 2021 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,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClientPeg} from './MatrixClientPeg'; +import { Room } from "matrix-js-sdk/src/models/room"; + +import { MatrixClientPeg } from './MatrixClientPeg'; /** * Given a room object, return the alias we should use for it, @@ -25,11 +27,11 @@ import {MatrixClientPeg} from './MatrixClientPeg'; * @param {Object} room The room object * @returns {string} A display alias for the given room */ -export function getDisplayAliasForRoom(room) { +export function getDisplayAliasForRoom(room: Room): string { return room.getCanonicalAlias() || room.getAltAliases()[0]; } -export function looksLikeDirectMessageRoom(room, myUserId) { +export function looksLikeDirectMessageRoom(room: Room, myUserId: string): boolean { const myMembership = room.getMyMembership(); const me = room.getMember(myUserId); @@ -48,7 +50,7 @@ export function looksLikeDirectMessageRoom(room, myUserId) { return false; } -export function guessAndSetDMRoom(room, isDirect) { +export function guessAndSetDMRoom(room: Room, isDirect: boolean): Promise { let newTarget; if (isDirect) { const guessedUserId = guessDMRoomTargetId( @@ -70,7 +72,7 @@ export function guessAndSetDMRoom(room, isDirect) { this room as a DM room * @returns {object} A promise */ -export function setDMRoom(roomId, userId) { +export function setDMRoom(roomId: string, userId: string): Promise { if (MatrixClientPeg.get().isGuest()) { return Promise.resolve(); } @@ -114,7 +116,7 @@ export function setDMRoom(roomId, userId) { * @param {string} myUserId User ID of the current user * @returns {string} User ID of the user that the room is probably a DM with */ -function guessDMRoomTargetId(room, myUserId) { +function guessDMRoomTargetId(room: Room, myUserId: string): string { let oldestTs; let oldestUser; diff --git a/src/Unread.js b/src/Unread.ts similarity index 75% rename from src/Unread.js rename to src/Unread.ts index 25c425aa9a..b733f4175a 100644 --- a/src/Unread.js +++ b/src/Unread.ts @@ -1,5 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd +Copyright 2015 - 2021 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,9 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClientPeg} from "./MatrixClientPeg"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { EventType, MsgType } from "matrix-js-sdk/src/@types/event"; + +import { MatrixClientPeg } from "./MatrixClientPeg"; import shouldHideEvent from './shouldHideEvent'; -import {haveTileForEvent} from "./components/views/rooms/EventTile"; +import { haveTileForEvent } from "./components/views/rooms/EventTile"; /** * Returns true iff this event arriving in a room should affect the room's @@ -25,28 +29,33 @@ import {haveTileForEvent} from "./components/views/rooms/EventTile"; * @param {Object} ev The event * @returns {boolean} True if the given event should affect the unread message count */ -export function eventTriggersUnreadCount(ev) { +export function eventTriggersUnreadCount(ev: MatrixEvent): boolean { if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) { return false; - } else if (ev.getType() == 'm.room.member') { - return false; - } else if (ev.getType() == 'm.room.third_party_invite') { - return false; - } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') { - return false; - } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { - return false; - } else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') { - return false; - } else if (ev.getType() == 'm.room.server_acl') { - return false; - } else if (ev.isRedacted()) { - return false; } + + switch (ev.getType()) { + case EventType.RoomMember: + case EventType.RoomThirdPartyInvite: + case EventType.CallAnswer: + case EventType.CallHangup: + case EventType.RoomAliases: + case EventType.RoomCanonicalAlias: + case EventType.RoomServerAcl: + return false; + + case EventType.RoomMessage: + if (ev.getContent().msgtype === MsgType.Notice) { + return false; + } + break; + } + + if (ev.isRedacted()) return false; return haveTileForEvent(ev); } -export function doesRoomHaveUnreadMessages(room) { +export function doesRoomHaveUnreadMessages(room: Room): boolean { const myUserId = MatrixClientPeg.get().getUserId(); // get the most recent read receipt sent by our account. diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 0af2d3d635..27d628fecf 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1461,7 +1461,7 @@ export default class MatrixChat extends React.PureComponent { }); const dft = new DecryptionFailureTracker((total, errorCode) => { - Analytics.trackEvent('E2E', 'Decryption failure', errorCode, total); + Analytics.trackEvent('E2E', 'Decryption failure', errorCode, String(total)); CountlyAnalytics.instance.track("decryption_failure", { errorCode }, null, { sum: total }); }, (errorCode) => { // Map JS-SDK error codes to tracker codes for aggregation diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 74609cca13..cb01df05d1 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -23,7 +23,7 @@ import RateLimitedFunc from '../../../ratelimitedfunc'; import SettingsStore from "../../../settings/SettingsStore"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import { UIFeature } from "../../../settings/UIFeature"; -import { ResizeNotifier } from "../../../utils/ResizeNotifier"; +import ResizeNotifier from "../../../utils/ResizeNotifier"; import CallViewForRoom from '../voip/CallViewForRoom'; import { objectHasDiff } from "../../../utils/objects"; import { replaceableComponent } from "../../../utils/replaceableComponent"; diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 0099bf73fb..3d674efe04 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import classNames from "classnames"; - import { EventType } from "matrix-js-sdk/src/@types/event"; import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Relations } from "matrix-js-sdk/src/models/relations"; @@ -29,24 +28,24 @@ import { hasText } from "../../../TextForEvent"; import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; -import {Layout} from "../../../settings/Layout"; -import {formatTime} from "../../../DateUtils"; -import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; +import { Layout } from "../../../settings/Layout"; +import { formatTime } from "../../../DateUtils"; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import { ALL_RULE_TYPES } from "../../../mjolnir/BanList"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import {E2E_STATE} from "./E2EIcon"; -import {toRem} from "../../../utils/units"; -import {WidgetType} from "../../../widgets/WidgetType"; +import { E2E_STATE } from "./E2EIcon"; +import { toRem } from "../../../utils/units"; +import { WidgetType } from "../../../widgets/WidgetType"; import RoomAvatar from "../avatars/RoomAvatar"; -import {WIDGET_LAYOUT_EVENT_TYPE} from "../../../stores/widgets/WidgetLayoutStore"; -import {objectHasDiff} from "../../../utils/objects"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { WIDGET_LAYOUT_EVENT_TYPE } from "../../../stores/widgets/WidgetLayoutStore"; +import { objectHasDiff } from "../../../utils/objects"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; import Tooltip from "../elements/Tooltip"; -import { EditorStateTransfer } from "../../../utils/EditorStateTransfer"; +import EditorStateTransfer from "../../../utils/EditorStateTransfer"; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; -import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState"; +import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; import NotificationBadge from "./NotificationBadge"; -import {ComposerInsertPayload} from "../../../dispatcher/payloads/ComposerInsertPayload"; +import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload"; import { Action } from '../../../dispatcher/actions'; const eventTileTypes = { diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 5a1c3a24b3..d277a69907 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -22,7 +22,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event"; import { _t, _td } from "../../../languageHandler"; import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex"; -import { ResizeNotifier } from "../../../utils/ResizeNotifier"; +import ResizeNotifier from "../../../utils/ResizeNotifier"; import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; import RoomViewStore from "../../../stores/RoomViewStore"; import { ITagMap } from "../../../stores/room-list/algorithms/models"; diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index ba8bbffbcc..61166b4230 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -45,7 +45,7 @@ import { ActionPayload } from "../../../dispatcher/payloads"; import { Enable, Resizable } from "re-resizable"; import { Direction } from "re-resizable/lib/resizer"; import { polyfillTouchEvent } from "../../../@types/polyfill"; -import { ResizeNotifier } from "../../../utils/ResizeNotifier"; +import ResizeNotifier from "../../../utils/ResizeNotifier"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore"; import { arrayFastClone, arrayHasOrderChange } from "../../../utils/arrays"; diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 16950dc008..cc129fb6b9 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -112,7 +112,7 @@ export interface IVariables { [key: string]: SubstitutionValue; } -type Tags = Record; +export type Tags = Record; export type TranslatedString = string | React.ReactNode; diff --git a/yarn.lock b/yarn.lock index 952d08d0f6..5bd409e612 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1479,6 +1479,11 @@ resolved "https://registry.yarnpkg.com/@types/counterpart/-/counterpart-0.18.1.tgz#b1b784d9e54d9879f0a8cb12f2caedab65430fe8" integrity sha512-PRuFlBBkvdDOtxlIASzTmkEFar+S66Ek48NVVTWMUjtJAdn5vyMSN8y6IZIoIymGpR36q2nZbIYazBWyFxL+IQ== +"@types/diff-match-patch@^1.0.32": + version "1.0.32" + resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.32.tgz#d9c3b8c914aa8229485351db4865328337a3d09f" + integrity sha512-bPYT5ECFiblzsVzyURaNhljBH2Gh1t9LowgUwciMrNAhFewLkHT2H0Mto07Y4/3KCOGZHRQll3CTtQZ0X11D/A== + "@types/events@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" From 4a667942368305799e34ac8b0ea7d569a5207a36 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 17:26:19 +0100 Subject: [PATCH 84/94] update copy --- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a9a0d15ac4..17160a6b89 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -796,7 +796,7 @@ "Show all rooms in Home": "Show all rooms in Home", "Show people in spaces": "Show people in spaces", "If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.": "If disabled, you can still add Direct Messages to Personal Spaces. If enabled, you'll automatically see everyone who is a member of the Space.", - "Show notification badges for DMs in Spaces": "Show notification badges for DMs in Spaces", + "Show notification badges for People in Spaces": "Show notification badges for People in Spaces", "Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode", "Send and receive voice messages": "Send and receive voice messages", "Render LaTeX maths in messages": "Render LaTeX maths in messages", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 3937b7d821..f22882abc4 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -193,7 +193,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { controller: new ReloadOnChangeController(), }, "feature_spaces.space_dm_badges": { - displayName: _td("Show notification badges for DMs in Spaces"), + displayName: _td("Show notification badges for People in Spaces"), supportedLevels: LEVELS_FEATURE, default: false, controller: new ReloadOnChangeController(), From 28c61cca2798f0c41fea47a468057a4a9ecf2c58 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 17:56:52 +0100 Subject: [PATCH 85/94] Remove pinned resolution for @types/react to 16.x --- package.json | 3 --- yarn.lock | 10 +--------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/package.json b/package.json index 8ebb90f342..9ca1224baa 100644 --- a/package.json +++ b/package.json @@ -167,9 +167,6 @@ "typescript": "^4.1.3", "walk": "^2.3.14" }, - "resolutions": { - "**/@types/react": "^16.14" - }, "jest": { "testEnvironment": "./__test-utils__/environment.js", "testMatch": [ diff --git a/yarn.lock b/yarn.lock index 4f17b63337..b19a188014 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1634,15 +1634,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.14": - version "16.14.2" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.2.tgz#85dcc0947d0645349923c04ccef6018a1ab7538c" - integrity sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -"@types/react@^17.0.2": +"@types/react@*", "@types/react@^17.0.2": version "17.0.11" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA== From cd04fb76dc3dd2aa4b921e61a367015fd104f7df Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 18:01:29 +0100 Subject: [PATCH 86/94] Fix type error --- src/stores/SpaceStore.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index d371086b45..f11589485a 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -335,7 +335,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // rootSpaces.push(space); // }); - this.orphanedRooms = new Set(orphanedRooms); + this.orphanedRooms = new Set(orphanedRooms.map(r => r.roomId)); this.rootSpaces = rootSpaces; this.parentMap = backrefs; From 7948aa6181ec34cd2c55a3945721af747321bb40 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 21:10:29 +0100 Subject: [PATCH 87/94] Iterate PR, improve jsdoc and switch function style --- src/utils/arrays.ts | 8 ++++---- src/utils/stringOrderField.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts index 148861e5d3..6524debfb7 100644 --- a/src/utils/arrays.ts +++ b/src/utils/arrays.ts @@ -225,10 +225,10 @@ export function arrayMerge(...a: T[][]): T[] { /** * Moves a single element from fromIndex to toIndex. - * @param list the list from which to construct the new list. - * @param fromIndex the index of the element to move. - * @param toIndex the index of where to put the element. - * @returns A new array with the requested value moved. + * @param {array} list the list from which to construct the new list. + * @param {number} fromIndex the index of the element to move. + * @param {number} toIndex the index of where to put the element. + * @returns {array} A new array with the requested value moved. */ export function moveElement(list: T[], fromIndex: number, toIndex: number): T[] { const result = Array.from(list); diff --git a/src/utils/stringOrderField.ts b/src/utils/stringOrderField.ts index b312b85b08..da840792ee 100644 --- a/src/utils/stringOrderField.ts +++ b/src/utils/stringOrderField.ts @@ -18,13 +18,13 @@ import { alphabetPad, baseToString, stringToBase, DEFAULT_ALPHABET } from "matri import { moveElement } from "./arrays"; -export const midPointsBetweenStrings = ( +export function midPointsBetweenStrings( a: string, b: string, count: number, maxLen: number, alphabet = DEFAULT_ALPHABET, -): string[] => { +): string[] { const padN = Math.min(Math.max(a.length, b.length), maxLen); const padA = alphabetPad(a, padN, alphabet); const padB = alphabetPad(b, padN, alphabet); @@ -48,7 +48,7 @@ export const midPointsBetweenStrings = ( const step = (baseB - baseA) / BigInt(count + 1); const start = BigInt(baseA + step); return Array(count).fill(undefined).map((_, i) => baseToString(start + (BigInt(i) * step), alphabet)); -}; +} interface IEntry { index: number; From 99e3aea1e5eb0cc5f25742ab430fa7dcf18edc83 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 21:17:33 +0100 Subject: [PATCH 88/94] i18n and regen yarn lock --- src/i18n/strings/en_EN.json | 8 +- yarn.lock | 200 ++++++++++++++++-------------------- 2 files changed, 94 insertions(+), 114 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a94b608f2b..4ca011f404 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1025,10 +1025,10 @@ "You can change these anytime.": "You can change these anytime.", "Creating...": "Creating...", "Create": "Create", - "Expand space panel": "Expand space panel", - "Collapse space panel": "Collapse space panel", "All rooms": "All rooms", "Home": "Home", + "Expand space panel": "Expand space panel", + "Collapse space panel": "Collapse space panel", "Click to copy": "Click to copy", "Copied!": "Copied!", "Failed to copy": "Failed to copy", @@ -2511,6 +2511,8 @@ "Update status": "Update status", "Set status": "Set status", "Set a new status...": "Set a new status...", + "Move up": "Move up", + "Move down": "Move down", "View Community": "View Community", "Unable to start audio streaming.": "Unable to start audio streaming.", "Failed to start livestream": "Failed to start livestream", @@ -2657,7 +2659,7 @@ "%(count)s messages deleted.|one": "%(count)s message deleted.", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your %(brand)s experience!": "Did you know: you can use communities to filter your %(brand)s experience!", - "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", + "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", "Error whilst fetching joined communities": "Error whilst fetching joined communities", "Create a new community": "Create a new community", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.", diff --git a/yarn.lock b/yarn.lock index b19a188014..0d424cb93d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1017,13 +1017,20 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.3.3": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" @@ -1504,6 +1511,14 @@ dependencies: "@types/node" "*" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -1620,6 +1635,13 @@ dependencies: "@types/node" "*" +"@types/react-beautiful-dnd@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#e60d3d965312fcf1516894af92dc3e9249587db4" + integrity sha512-by80tJ8aTTDXT256Gl+RfLRtFjYbUWOnZuEigJgNsJrSEGxvFe5eY6k3g4VIvf0M/6+xoLgfYWoWonlOo6Wqdg== + dependencies: + "@types/react" "*" + "@types/react-dom@^17.0.2": version "17.0.8" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.8.tgz#3180de6d79bf53762001ad854e3ce49f36dd71fc" @@ -1627,6 +1649,16 @@ dependencies: "@types/react" "*" +"@types/react-redux@^7.1.16": + version "7.1.16" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21" + integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-transition-group@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" @@ -2122,14 +2154,6 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -2648,11 +2672,6 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2712,6 +2731,13 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" +css-box-model@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" + integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== + dependencies: + tiny-invariant "^1.0.6" + css-select@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286" @@ -4241,7 +4267,7 @@ highlight.js@^10.5.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" integrity sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw== -hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4456,13 +4482,6 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -5600,11 +5619,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.2.1: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7" - integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA== - lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" @@ -5625,7 +5639,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.2.1: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5794,10 +5808,10 @@ mdurl@~1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= -memoize-one@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17" - integrity sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA== +memoize-one@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== meow@^9.0.0: version "9.0.0" @@ -6443,11 +6457,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -6657,7 +6666,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: +prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6734,12 +6743,12 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -raf-schd@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-2.1.2.tgz#ec622b5167f2912089f054dc03ebd5bcf33c8f62" - integrity sha512-Orl0IEvMtUCgPddgSxtxreK77UiQz4nPYJy9RggVzu4mKsZkQWiAaG1y9HlYWdvm9xtN348xRaT37qkvL/+A+g== +raf-schd@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" + integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== -raf@^3.1.0, raf@^3.4.1: +raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -6766,21 +6775,18 @@ re-resizable@^6.9.0: dependencies: fast-memoize "^2.5.1" -react-beautiful-dnd@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-4.0.1.tgz#3b0a49bf6be75af351176c904f012611dd292b81" - integrity sha512-d73RMu4QOFCyjUELLWFyY/EuclnfqulI9pECx+2gIuJvV0ycf1uR88o+1x0RSB9ILD70inHMzCBKNkWVbbt+vA== +react-beautiful-dnd@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d" + integrity sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA== dependencies: - babel-runtime "^6.26.0" - invariant "^2.2.2" - memoize-one "^3.0.1" - prop-types "^15.6.0" - raf-schd "^2.1.0" - react-motion "^0.5.2" - react-redux "^5.0.6" - redux "^3.7.2" - redux-thunk "^2.2.0" - reselect "^3.0.1" + "@babel/runtime" "^7.9.2" + css-box-model "^1.2.0" + memoize-one "^5.1.1" + raf-schd "^4.0.2" + react-redux "^7.2.0" + redux "^4.0.4" + use-memo-one "^1.1.1" react-clientside-effect@^1.2.2: version "1.2.3" @@ -6815,7 +6821,7 @@ react-focus-lock@^2.5.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6825,32 +6831,17 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== -react-lifecycles-compat@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== - -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== +react-redux@^7.2.0: + version "7.2.4" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" + integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - -react-redux@^5.0.6: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== - dependencies: - "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.13.1" react-shallow-renderer@^16.13.1: version "16.14.1" @@ -6979,20 +6970,12 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux-thunk@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" - integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== - -redux@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" - integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== +redux@^4.0.0, redux@^4.0.4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" + integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== dependencies: - lodash "^4.2.1" - lodash-es "^4.2.1" - loose-envify "^1.1.0" - symbol-observable "^1.0.3" + "@babel/runtime" "^7.9.2" regenerate-unicode-properties@^8.2.0: version "8.2.0" @@ -7006,11 +6989,6 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -7168,11 +7146,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -reselect@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" - integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= - resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" @@ -7895,11 +7868,6 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= -symbol-observable@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -7962,6 +7930,11 @@ through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tiny-invariant@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + tmatch@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf" @@ -8277,6 +8250,11 @@ use-callback-ref@^1.2.1: resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5" integrity sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg== +use-memo-one@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20" + integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ== + use-sidecar@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.4.tgz#38398c3723727f9f924bed2343dfa3db6aaaee46" From 49d20d253034ad945708da53e8d7b3f19ca7f15d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 21:22:30 +0100 Subject: [PATCH 89/94] consolidate the two onRoomAccountData listeners --- src/stores/SpaceStore.tsx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 8b4e3a8f3a..e498574467 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -527,17 +527,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } }; - private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => { - if (!room.isSpaceRoom() || ev.getType() !== EventType.SpaceOrder) return; - - this.spaceOrderLocalEchoMap.delete(room.roomId); // clear any local echo - const order = ev.getContent()?.order; - const lastOrder = lastEv?.getContent()?.order; - if (order !== lastOrder) { - this.notifyIfOrderChanged(); - } - }; - private notifyIfOrderChanged(): void { const rootSpaces = this.sortRootSpaces(this.rootSpaces); if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) { @@ -577,10 +566,19 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } }; - private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEvent?: MatrixEvent) => { - if (ev.getType() === EventType.Tag && !room.isSpaceRoom()) { + private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEv?: MatrixEvent) => { + if (!room.isSpaceRoom()) return; + + if (ev.getType() === EventType.SpaceOrder) { + this.spaceOrderLocalEchoMap.delete(room.roomId); // clear any local echo + const order = ev.getContent()?.order; + const lastOrder = lastEv?.getContent()?.order; + if (order !== lastOrder) { + this.notifyIfOrderChanged(); + } + } else if (ev.getType() === EventType.Tag && !SettingsStore.getValue("feature_spaces.all_rooms")) { // If the room was in favourites and now isn't or the opposite then update its position in the trees - const oldTags = lastEvent?.getContent()?.tags || {}; + const oldTags = lastEv?.getContent()?.tags || {}; const newTags = ev.getContent()?.tags || {}; if (!!oldTags[DefaultTagID.Favourite] !== !!newTags[DefaultTagID.Favourite]) { this.onRoomUpdate(room); @@ -625,7 +623,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); this.matrixClient.removeListener("RoomState.events", this.onRoomState); if (!SettingsStore.getValue("feature_spaces.all_rooms")) { - this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); this.matrixClient.removeListener("accountData", this.onAccountData); } } @@ -639,7 +636,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.matrixClient.on("Room.accountData", this.onRoomAccountData); this.matrixClient.on("RoomState.events", this.onRoomState); if (!SettingsStore.getValue("feature_spaces.all_rooms")) { - this.matrixClient.on("Room.accountData", this.onRoomAccountData); this.matrixClient.on("accountData", this.onAccountData); } From 5dc542f18947efadcea7beb4f68af8311bd36357 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 22:01:18 +0100 Subject: [PATCH 90/94] Iterate PR --- .../views/dialogs/SpaceSettingsDialog.tsx | 3 +-- .../views/elements/LabelledToggleSwitch.tsx | 2 +- .../views/elements/RoomAliasField.tsx | 18 +++++++------ .../room_settings/RoomPublishSetting.tsx | 14 +++++++---- .../tabs/room/AdvancedRoomSettingsTab.tsx | 25 ++++++++++--------- .../views/spaces/SpaceSettingsGeneralTab.tsx | 2 +- .../spaces/SpaceSettingsVisibilityTab.tsx | 2 +- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx index 1273f06401..5e0cd96740 100644 --- a/src/components/views/dialogs/SpaceSettingsDialog.tsx +++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx @@ -27,7 +27,7 @@ import TabbedView, { Tab } from "../../structures/TabbedView"; import SpaceSettingsGeneralTab from '../spaces/SpaceSettingsGeneralTab'; import SpaceSettingsVisibilityTab from "../spaces/SpaceSettingsVisibilityTab"; import SettingsStore from "../../../settings/SettingsStore"; -import {UIFeature} from "../../../settings/UIFeature"; +import { UIFeature } from "../../../settings/UIFeature"; import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab"; export enum SpaceSettingsTab { @@ -91,4 +91,3 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin }; export default SpaceSettingsDialog; - diff --git a/src/components/views/elements/LabelledToggleSwitch.tsx b/src/components/views/elements/LabelledToggleSwitch.tsx index 957e3dbc97..d97b698fd8 100644 --- a/src/components/views/elements/LabelledToggleSwitch.tsx +++ b/src/components/views/elements/LabelledToggleSwitch.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019, 2021 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 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. diff --git a/src/components/views/elements/RoomAliasField.tsx b/src/components/views/elements/RoomAliasField.tsx index 7eff529c46..74af311b47 100644 --- a/src/components/views/elements/RoomAliasField.tsx +++ b/src/components/views/elements/RoomAliasField.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019, 2021 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 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. @@ -39,9 +39,13 @@ interface IState { export default class RoomAliasField extends React.PureComponent { private fieldRef = createRef(); - public state = { - isValid: true, - }; + constructor(props, context) { + super(props, context); + + this.state = { + isValid: true, + }; + } private asFullAlias(localpart: string): string { return `#${localpart}:${this.props.domain}`; @@ -123,15 +127,15 @@ export default class RoomAliasField extends React.PureComponent ], }); - get isValid() { + public get isValid() { return this.state.isValid; } - validate(options: IValidateOpts) { + public validate(options: IValidateOpts) { return this.fieldRef.current?.validate(options); } - focus() { + public focus() { this.fieldRef.current?.focus(); } } diff --git a/src/components/views/room_settings/RoomPublishSetting.tsx b/src/components/views/room_settings/RoomPublishSetting.tsx index 24df5f1c84..95b0ac100d 100644 --- a/src/components/views/room_settings/RoomPublishSetting.tsx +++ b/src/components/views/room_settings/RoomPublishSetting.tsx @@ -1,5 +1,5 @@ /* -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. +Copyright 2020 - 2021 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. @@ -33,9 +33,13 @@ interface IState { @replaceableComponent("views.room_settings.RoomPublishSetting") export default class RoomPublishSetting extends React.PureComponent { - public state = { - isRoomPublished: false, - }; + constructor(props, context) { + super(props, context); + + this.state = { + isRoomPublished: false, + }; + } private onRoomPublishChange = (e) => { const valueBefore = this.state.isRoomPublished; @@ -67,7 +71,7 @@ export default class RoomPublishSetting extends React.PureComponent ); diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index 7e7d9cba90..c4963d0154 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019, 2021 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ limitations under the License. */ import React from 'react'; +import { EventType } from 'matrix-js-sdk/src/@types/event'; import { _t } from "../../../../../languageHandler"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; @@ -56,10 +57,10 @@ export default class AdvancedRoomSettingsTab extends React.Component { - const tombstone = room.currentState.getStateEvents("m.room.tombstone", ""); + const tombstone = room.currentState.getStateEvents(EventType.RoomTombstone, ""); const additionalStateChanges: Partial = {}; - const createEvent = room.currentState.getStateEvents("m.room.create", ""); + const createEvent = room.currentState.getStateEvents(EventType.RoomCreate, ""); const predecessor = createEvent ? createEvent.getContent().predecessor : null; if (predecessor && predecessor.room_id) { additionalStateChanges.oldRoomId = predecessor.room_id; @@ -100,9 +101,9 @@ export default class AdvancedRoomSettingsTab extends React.Component{_t('This room is not accessible by remote Matrix servers')}
    ; + unfederatableSection =
    { _t('This room is not accessible by remote Matrix servers') }
    ; } let roomUpgradeButton; @@ -110,7 +111,7 @@ export default class AdvancedRoomSettingsTab extends React.Component

    - {_t( + { _t( "Warning: Upgrading a room will not automatically migrate room members " + "to the new version of the room. We'll post a link to the new room in the old " + "version of the room - room members will have to click this link to join the new room.", @@ -118,10 +119,10 @@ export default class AdvancedRoomSettingsTab extends React.Component {sub}, "i": (sub) => {sub}, }, - )} + ) }

    - {_t("Upgrade this room to the recommended room version")} + { _t("Upgrade this room to the recommended room version") }
    ); @@ -141,21 +142,21 @@ export default class AdvancedRoomSettingsTab extends React.Component -
    {_t("Advanced")}
    +
    { _t("Advanced") }
    { room?.isSpaceRoom() ? _t("Space information") : _t("Room information") }
    - {_t("Internal room ID:")}  + { _t("Internal room ID:") }  { this.props.roomId }
    { unfederatableSection }
    - {_t("Room version")} + { _t("Room version") }
    - {_t("Room version:")}  + { _t("Room version:") }  { room.getVersion() }
    { oldRoomLink } diff --git a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx index db0a180846..3afdc629e4 100644 --- a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx +++ b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx @@ -90,7 +90,7 @@ const SpaceSettingsGeneralTab = ({ matrixClient: cli, space, onFinished }: IProp }; return
    -
    {_t("General")}
    +
    { _t("General") }
    { _t("Edit settings relating to your space.") }
    diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index 2f80ad97a6..263823603b 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -137,7 +137,7 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => { } return
    -
    {_t("Visibility")}
    +
    { _t("Visibility") }
    { error &&
    { error }
    } From 9dc8493a5c845336dafc774193353d08e1ef5971 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 22:07:42 +0100 Subject: [PATCH 91/94] Hide communities invites and the community autocompleter when Spaces Beta is enabled --- src/autocomplete/Autocompleter.ts | 3 ++- src/components/views/rooms/RoomList.tsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/autocomplete/Autocompleter.ts b/src/autocomplete/Autocompleter.ts index ea8eddbb8d..7f3f5d2c01 100644 --- a/src/autocomplete/Autocompleter.ts +++ b/src/autocomplete/Autocompleter.ts @@ -55,13 +55,14 @@ const PROVIDERS = [ EmojiProvider, NotifProvider, CommandProvider, - CommunityProvider, DuckDuckGoProvider, ]; // as the spaces feature is device configurable only, and toggling it refreshes the page, we can do this here if (SettingsStore.getValue("feature_spaces")) { PROVIDERS.push(SpaceProvider); +} else { + PROVIDERS.push(CommunityProvider); } // Providers will get rejected if they take longer than this. diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 5a1c3a24b3..704c3cf620 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -466,6 +466,7 @@ export default class RoomList extends React.PureComponent { } private renderCommunityInvites(): ReactComponentElement[] { + if (SettingsStore.getValue("feature_spaces")) return []; // TODO: Put community invites in a more sensible place (not in the room list) // See https://github.com/vector-im/element-web/issues/14456 return MatrixClientPeg.get().getGroups().filter(g => { From 83296b74404c6a6e594c9bd382bbb9d37f4248ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 22:19:01 +0100 Subject: [PATCH 92/94] Fix typing --- src/hooks/useRoomState.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hooks/useRoomState.ts b/src/hooks/useRoomState.ts index 11ac7de49e..e778acf8a9 100644 --- a/src/hooks/useRoomState.ts +++ b/src/hooks/useRoomState.ts @@ -24,7 +24,10 @@ type Mapper = (roomState: RoomState) => T; const defaultMapper: Mapper = (roomState: RoomState) => roomState; // Hook to simplify watching Matrix Room state -export const useRoomState = (room: Room, mapper: Mapper = defaultMapper): T => { +export const useRoomState = ( + room: Room, + mapper: Mapper = defaultMapper as Mapper, +): T => { const [value, setValue] = useState(room ? mapper(room.currentState) : undefined); const update = useCallback(() => { From e0ac200e27197617d9be4df78ef957441fed2fde Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 22:23:09 +0100 Subject: [PATCH 93/94] Iterate PR --- src/DecryptionFailureTracker.ts | 4 ++-- src/HtmlUtils.tsx | 4 ++-- src/utils/EditorStateTransfer.ts | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/DecryptionFailureTracker.ts b/src/DecryptionFailureTracker.ts index 960d844e9e..07c0c546fe 100644 --- a/src/DecryptionFailureTracker.ts +++ b/src/DecryptionFailureTracker.ts @@ -25,7 +25,7 @@ export class DecryptionFailure { } } -type Fn = (count: number, trackedErrCode: string) => void; +type TrackingFn = (count: number, trackedErrCode: string) => void; type ErrCodeMapFn = (errcode: string) => string; export class DecryptionFailureTracker { @@ -73,7 +73,7 @@ export class DecryptionFailureTracker { * @param {function?} errorCodeMapFn The function used to map error codes to the * trackedErrorCode. If not provided, the `.code` of errors will be used. */ - constructor(private readonly fn: Fn, private readonly errorCodeMapFn?: ErrCodeMapFn) { + constructor(private readonly fn: TrackingFn, private readonly errorCodeMapFn?: ErrCodeMapFn) { if (!fn || typeof fn !== 'function') { throw new Error('DecryptionFailureTracker requires tracking function'); } diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 5803029030..983538d65b 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -505,7 +505,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts * @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options * @returns {string} Linkified string */ -export function linkifyString(str: string, options = linkifyMatrix.options) { +export function linkifyString(str: string, options = linkifyMatrix.options): string { return _linkifyString(str, options); } @@ -516,7 +516,7 @@ export function linkifyString(str: string, options = linkifyMatrix.options) { * @param {object} [options] Options for linkifyElement. Default: linkifyMatrix.options * @returns {object} */ -export function linkifyElement(element: HTMLElement, options = linkifyMatrix.options) { +export function linkifyElement(element: HTMLElement, options = linkifyMatrix.options): HTMLElement { return _linkifyElement(element, options); } diff --git a/src/utils/EditorStateTransfer.ts b/src/utils/EditorStateTransfer.ts index 42e1a316d6..ba303f9b73 100644 --- a/src/utils/EditorStateTransfer.ts +++ b/src/utils/EditorStateTransfer.ts @@ -30,24 +30,24 @@ export default class EditorStateTransfer { constructor(private readonly event: MatrixEvent) {} - setEditorState(caret: Caret, serializedParts: SerializedPart[]) { + public setEditorState(caret: Caret, serializedParts: SerializedPart[]) { this.caret = caret; this.serializedParts = serializedParts; } - hasEditorState() { + public hasEditorState() { return !!this.serializedParts; } - getSerializedParts() { + public getSerializedParts() { return this.serializedParts; } - getCaret() { + public getCaret() { return this.caret; } - getEvent() { + public getEvent() { return this.event; } } From ffaa19ef2c27f361c8fcc0367e491dca528e697b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Jun 2021 22:27:12 +0100 Subject: [PATCH 94/94] fix typing --- .../views/dialogs/ForwardDialog.tsx | 31 ++++++++++--------- .../views/elements/EventTilePreview.tsx | 9 +++--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index a83f3f177c..6fbed6fc8b 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -14,30 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {useMemo, useState, useEffect} from "react"; +import React, { useMemo, useState, useEffect } from "react"; import classnames from "classnames"; -import {MatrixEvent} from "matrix-js-sdk/src/models/event"; -import {Room} from "matrix-js-sdk/src/models/room"; -import {MatrixClient} from "matrix-js-sdk/src/client"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import {_t} from "../../../languageHandler"; +import { _t } from "../../../languageHandler"; import dis from "../../../dispatcher/dispatcher"; -import {useSettingValue, useFeatureEnabled} from "../../../hooks/useSettings"; -import {UIFeature} from "../../../settings/UIFeature"; -import {Layout} from "../../../settings/Layout"; -import {IDialogProps} from "./IDialogProps"; +import { useSettingValue, useFeatureEnabled } from "../../../hooks/useSettings"; +import { UIFeature } from "../../../settings/UIFeature"; +import { Layout } from "../../../settings/Layout"; +import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; -import {avatarUrlForUser} from "../../../Avatar"; +import { avatarUrlForUser } from "../../../Avatar"; import EventTile from "../rooms/EventTile"; import SearchBox from "../../structures/SearchBox"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; -import {Alignment} from '../elements/Tooltip'; +import { Alignment } from '../elements/Tooltip'; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; -import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState"; +import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; import NotificationBadge from "../rooms/NotificationBadge"; -import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; -import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm"; +import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; +import { sortRooms } from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm"; import QueryMatcher from "../../../autocomplete/QueryMatcher"; const AVATAR_SIZE = 30; @@ -171,7 +172,7 @@ const ForwardDialog: React.FC = ({ matrixClient: cli, event, permalinkCr ); }, getMxcAvatarUrl: () => profileInfo.avatar_url, - }; + } as RoomMember; const [query, setQuery] = useState(""); const lcQuery = query.toLowerCase(); diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index cf3b7a6e61..366d918bcf 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -17,13 +17,14 @@ limitations under the License. import React from 'react'; import classnames from 'classnames'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; import * as Avatar from '../../../Avatar'; import EventTile from '../rooms/EventTile'; import SettingsStore from "../../../settings/SettingsStore"; -import {Layout} from "../../../settings/Layout"; -import {UIFeature} from "../../../settings/UIFeature"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { Layout } from "../../../settings/Layout"; +import { UIFeature } from "../../../settings/UIFeature"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { /** @@ -110,7 +111,7 @@ export default class EventTilePreview extends React.Component { ); }, getMxcAvatarUrl: () => this.props.avatarUrl, - }; + } as RoomMember; return event; }