From 4673e1aa49e508fded8869aa52d64b50fddd61dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 08:15:46 +0200 Subject: [PATCH 01/14] Convert SearchBox to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{SearchBox.js => SearchBox.tsx} | 101 ++++++++++-------- .../dialogs/AddExistingToSpaceDialog.tsx | 1 - .../views/dialogs/ForwardDialog.tsx | 1 - .../views/dialogs/LeaveSpaceDialog.tsx | 1 - .../ManageRestrictedJoinRuleDialog.tsx | 1 - 5 files changed, 55 insertions(+), 50 deletions(-) rename src/components/structures/{SearchBox.js => SearchBox.tsx} (62%) diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.tsx similarity index 62% rename from src/components/structures/SearchBox.js rename to src/components/structures/SearchBox.tsx index 6d310662e3..82fe689022 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React, { createRef } from 'react'; -import PropTypes from 'prop-types'; import { Key } from '../../Keyboard'; import dis from '../../dispatcher/dispatcher'; import { throttle } from 'lodash'; @@ -24,106 +23,116 @@ import AccessibleButton from '../../components/views/elements/AccessibleButton'; import classNames from 'classnames'; import { replaceableComponent } from "../../utils/replaceableComponent"; +interface IProps { + onSearch?: (query: string) => void; + onCleared?: (source?: string) => void; + onKeyDown?: (ev: React.KeyboardEvent) => void; + onFocus?: (ev: React.FocusEvent) => void; + onBlur?: (ev: React.FocusEvent) => void; + className?: string; + placeholder: string; + blurredPlaceholder?: string; + autoFocus?: boolean; + initialValue?: string; + collapsed?: boolean; + + // If true, the search box will focus and clear itself + // on room search focus action (it would be nicer to take + // this functionality out, but not obvious how that would work) + enableRoomSearchFocus?: boolean; +} + +interface IState { + searchTerm: string; + blurred: boolean; +} + @replaceableComponent("structures.SearchBox") -export default class SearchBox extends React.Component { - static propTypes = { - onSearch: PropTypes.func, - onCleared: PropTypes.func, - onKeyDown: PropTypes.func, - className: PropTypes.string, - placeholder: PropTypes.string.isRequired, - autoFocus: PropTypes.bool, - initialValue: PropTypes.string, +export default class SearchBox extends React.Component { + private dispatcherRef: string; + private search = createRef(); - // If true, the search box will focus and clear itself - // on room search focus action (it would be nicer to take - // this functionality out, but not obvious how that would work) - enableRoomSearchFocus: PropTypes.bool, - }; - - static defaultProps = { + static defaultProps: Partial = { enableRoomSearchFocus: false, }; - constructor(props) { + constructor(props: IProps) { super(props); - this._search = createRef(); - this.state = { - searchTerm: this.props.initialValue || "", + searchTerm: props.initialValue || "", blurred: true, }; } - componentDidMount() { + public componentDidMount(): void { this.dispatcherRef = dis.register(this.onAction); } - componentWillUnmount() { + public componentWillUnmount(): void { dis.unregister(this.dispatcherRef); } - onAction = payload => { + private onAction = (payload): void => { if (!this.props.enableRoomSearchFocus) return; switch (payload.action) { case 'view_room': - if (this._search.current && payload.clear_search) { - this._clearSearch(); + if (this.search.current && payload.clear_search) { + this.clearSearch(); } break; case 'focus_room_filter': - if (this._search.current) { - this._search.current.focus(); + if (this.search.current) { + this.search.current.focus(); } break; } }; - onChange = () => { - if (!this._search.current) return; - this.setState({ searchTerm: this._search.current.value }); + private onChange = (): void => { + if (!this.search.current) return; + this.setState({ searchTerm: this.search.current.value }); this.onSearch(); }; - onSearch = throttle(() => { - this.props.onSearch(this._search.current.value); + private onSearch = throttle((): void => { + this.props.onSearch(this.search.current.value); }, 200, { trailing: true, leading: true }); - _onKeyDown = ev => { + private onKeyDown = (ev: React.KeyboardEvent): void => { switch (ev.key) { case Key.ESCAPE: - this._clearSearch("keyboard"); + this.clearSearch("keyboard"); break; } if (this.props.onKeyDown) this.props.onKeyDown(ev); }; - _onFocus = ev => { + private onFocus = (ev: React.FocusEvent): void => { this.setState({ blurred: false }); - ev.target.select(); + (ev.target as HTMLInputElement).select(); if (this.props.onFocus) { this.props.onFocus(ev); } }; - _onBlur = ev => { + private onBlur = (ev: React.FocusEvent): void => { this.setState({ blurred: true }); if (this.props.onBlur) { this.props.onBlur(ev); } }; - _clearSearch(source) { - this._search.current.value = ""; + private clearSearch(source?: string): void { + this.search.current.value = ""; this.onChange(); if (this.props.onCleared) { this.props.onCleared(source); } } - render() { + public render(): JSX.Element { // check for collapsed here and // not at parent so we keep // searchTerm in our state @@ -136,7 +145,7 @@ export default class SearchBox extends React.Component { key="button" tabIndex={-1} className="mx_SearchBox_closeButton" - onClick={() => {this._clearSearch("button"); }} + onClick={() => {this.clearSearch("button"); }} />) : undefined; // show a shorter placeholder when blurred, if requested @@ -151,13 +160,13 @@ export default class SearchBox extends React.Component { = ({ className="mx_textinput_icon mx_textinput_search" placeholder={filterPlaceholder} onSearch={setQuery} - autoComplete={true} autoFocus={true} /> diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 77e2b6ae0c..7f08a3eb58 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -243,7 +243,6 @@ const ForwardDialog: React.FC = ({ matrixClient: cli, event, permalinkCr className="mx_textinput_icon mx_textinput_search" placeholder={_t("Search for rooms or people")} onSearch={setQuery} - autoComplete={true} autoFocus={true} /> diff --git a/src/components/views/dialogs/LeaveSpaceDialog.tsx b/src/components/views/dialogs/LeaveSpaceDialog.tsx index 3a8cd53945..841fa14407 100644 --- a/src/components/views/dialogs/LeaveSpaceDialog.tsx +++ b/src/components/views/dialogs/LeaveSpaceDialog.tsx @@ -57,7 +57,6 @@ const SpaceChildPicker = ({ filterPlaceholder, rooms, selected, onChange }) => { className="mx_textinput_icon mx_textinput_search" placeholder={filterPlaceholder} onSearch={setQuery} - autoComplete={true} autoFocus={true} /> diff --git a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx index c63fdc4c84..dd5c549bbe 100644 --- a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx +++ b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx @@ -126,7 +126,6 @@ const ManageRestrictedJoinRuleDialog: React.FC = ({ room, selected = [], className="mx_textinput_icon mx_textinput_search" placeholder={_t("Search spaces")} onSearch={setQuery} - autoComplete={true} autoFocus={true} /> From 716aba2a0e065b4c27bde8c33faac4cb36d3ab29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 08:40:56 +0200 Subject: [PATCH 02/14] Convert MainSplit to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{MainSplit.js => MainSplit.tsx} | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) rename src/components/structures/{MainSplit.js => MainSplit.tsx} (68%) diff --git a/src/components/structures/MainSplit.js b/src/components/structures/MainSplit.tsx similarity index 68% rename from src/components/structures/MainSplit.js rename to src/components/structures/MainSplit.tsx index 69d3bd0b51..0148bfca91 100644 --- a/src/components/structures/MainSplit.js +++ b/src/components/structures/MainSplit.tsx @@ -16,25 +16,35 @@ limitations under the License. */ import React from 'react'; -import { Resizable } from 're-resizable'; +import { NumberSize, Resizable } from 're-resizable'; import { replaceableComponent } from "../../utils/replaceableComponent"; +import ResizeNotifier from "../../utils/ResizeNotifier"; +import { Direction } from "re-resizable/lib/resizer"; + +interface IProps { + resizeNotifier: ResizeNotifier; + collapsedRhs?: boolean; + panel: JSX.Element; +} @replaceableComponent("structures.MainSplit") -export default class MainSplit extends React.Component { - _onResizeStart = () => { +export default class MainSplit extends React.Component { + private onResizeStart = (): void => { this.props.resizeNotifier.startResizing(); }; - _onResize = () => { + private onResize = (): void => { this.props.resizeNotifier.notifyRightHandleResized(); }; - _onResizeStop = (event, direction, refToElement, delta) => { + private onResizeStop = ( + event: MouseEvent | TouchEvent, direction: Direction, elementRef: HTMLElement, delta: NumberSize, + ): void => { this.props.resizeNotifier.stopResizing(); - window.localStorage.setItem("mx_rhs_size", this._loadSidePanelSize().width + delta.width); + window.localStorage.setItem("mx_rhs_size", (this.loadSidePanelSize().width + delta.width).toString()); }; - _loadSidePanelSize() { + private loadSidePanelSize(): {height: string | number, width: number} { let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size"), 10); if (isNaN(rhsSize)) { @@ -47,7 +57,7 @@ export default class MainSplit extends React.Component { }; } - render() { + public render(): JSX.Element { const bodyView = React.Children.only(this.props.children); const panelView = this.props.panel; @@ -56,7 +66,7 @@ export default class MainSplit extends React.Component { let children; if (hasResizer) { children = From e882576f86d1a97e95e3d5ba496f9ce5b799a7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 08:56:00 +0200 Subject: [PATCH 03/14] Convert EmbeddedPage to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{EmbeddedPage.js => EmbeddedPage.tsx} | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) rename src/components/structures/{EmbeddedPage.js => EmbeddedPage.tsx} (75%) diff --git a/src/components/structures/EmbeddedPage.js b/src/components/structures/EmbeddedPage.tsx similarity index 75% rename from src/components/structures/EmbeddedPage.js rename to src/components/structures/EmbeddedPage.tsx index 037a0eba2a..38b9668372 100644 --- a/src/components/structures/EmbeddedPage.js +++ b/src/components/structures/EmbeddedPage.tsx @@ -17,7 +17,6 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import request from 'browser-request'; import { _t } from '../../languageHandler'; import sanitizeHtml from 'sanitize-html'; @@ -27,37 +26,43 @@ import classnames from 'classnames'; import MatrixClientContext from "../../contexts/MatrixClientContext"; import AutoHideScrollbar from "./AutoHideScrollbar"; -export default class EmbeddedPage extends React.PureComponent { - static propTypes = { - // URL to request embedded page content from - url: PropTypes.string, - // Class name prefix to apply for a given instance - className: PropTypes.string, - // Whether to wrap the page in a scrollbar - scrollbar: PropTypes.bool, - // Map of keys to replace with values, e.g {$placeholder: "value"} - replaceMap: PropTypes.object, - }; +interface IProps { + // URL to request embedded page content from + url?: string; + // Class name prefix to apply for a given instance + className?: string; + // Whether to wrap the page in a scrollbar + scrollbar?: boolean; + // Map of keys to replace with values, e.g {$placeholder: "value"} + replaceMap?: Map; +} - static contextType = MatrixClientContext; +interface IState { + page: string; +} - constructor(props, context) { +export default class EmbeddedPage extends React.PureComponent { + public static contextType = MatrixClientContext; + private unmounted = true; + private dispatcherRef: string; + + constructor(props: IProps, context: typeof MatrixClientContext) { super(props, context); - this._dispatcherRef = null; + this.dispatcherRef = null; this.state = { page: '', }; } - translate(s) { + private translate(s: string): string { // default implementation - skins may wish to extend this return sanitizeHtml(_t(s)); } - componentDidMount() { - this._unmounted = false; + public componentDidMount(): void { + this.unmounted = false; if (!this.props.url) { return; @@ -70,7 +75,7 @@ export default class EmbeddedPage extends React.PureComponent { request( { method: "GET", url: this.props.url }, (err, response, body) => { - if (this._unmounted) { + if (this.unmounted) { return; } @@ -92,22 +97,22 @@ export default class EmbeddedPage extends React.PureComponent { }, ); - this._dispatcherRef = dis.register(this.onAction); + this.dispatcherRef = dis.register(this.onAction); } - componentWillUnmount() { - this._unmounted = true; - if (this._dispatcherRef !== null) dis.unregister(this._dispatcherRef); + public componentWillUnmount(): void { + this.unmounted = true; + if (this.dispatcherRef !== null) dis.unregister(this.dispatcherRef); } - onAction = (payload) => { + private onAction = (payload): void => { // HACK: Workaround for the context's MatrixClient not being set up at render time. if (payload.action === 'client_started') { this.forceUpdate(); } }; - render() { + public render(): JSX.Element { // HACK: Workaround for the context's MatrixClient not updating. const client = this.context || MatrixClientPeg.get(); const isGuest = client ? client.isGuest() : true; From a6a56b455b82dbe0da09b69427be0197c15eb454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 08:56:29 +0200 Subject: [PATCH 04/14] Convert GenericErrorPage to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{GenericErrorPage.js => GenericErrorPage.tsx} | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) rename src/components/structures/{GenericErrorPage.js => GenericErrorPage.tsx} (84%) diff --git a/src/components/structures/GenericErrorPage.js b/src/components/structures/GenericErrorPage.tsx similarity index 84% rename from src/components/structures/GenericErrorPage.js rename to src/components/structures/GenericErrorPage.tsx index 017d365273..e3b2133ae8 100644 --- a/src/components/structures/GenericErrorPage.js +++ b/src/components/structures/GenericErrorPage.tsx @@ -15,16 +15,15 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { replaceableComponent } from "../../utils/replaceableComponent"; -@replaceableComponent("structures.GenericErrorPage") -export default class GenericErrorPage extends React.PureComponent { - static propTypes = { - title: PropTypes.object.isRequired, // jsx for title - message: PropTypes.object.isRequired, // jsx to display - }; +interface IProps { + title: JSX.Element; + message: JSX.Element; +} +@replaceableComponent("structures.GenericErrorPage") +export default class GenericErrorPage extends React.PureComponent { render() { return
From ff7e32cdd1aaa85dfc8c973d6b7c0a04cf733447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 09:09:16 +0200 Subject: [PATCH 05/14] Convert UserView to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../structures/{UserView.js => UserView.tsx} | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) rename src/components/structures/{UserView.js => UserView.tsx} (68%) diff --git a/src/components/structures/UserView.js b/src/components/structures/UserView.tsx similarity index 68% rename from src/components/structures/UserView.js rename to src/components/structures/UserView.tsx index eb839be7be..81d9e83428 100644 --- a/src/components/structures/UserView.js +++ b/src/components/structures/UserView.tsx @@ -16,52 +16,61 @@ limitations under the License. */ import React from "react"; -import PropTypes from "prop-types"; import { MatrixClientPeg } from "../../MatrixClientPeg"; -import * as sdk from "../../index"; import Modal from '../../Modal'; import { _t } from '../../languageHandler'; import HomePage from "./HomePage"; import { replaceableComponent } from "../../utils/replaceableComponent"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import ErrorDialog from "../views/dialogs/ErrorDialog"; +import MainSplit from "./MainSplit"; +import RightPanel from "./RightPanel"; +import Spinner from "../views/elements/Spinner"; +import ResizeNotifier from "../../utils/ResizeNotifier"; +import { RoomState } from "matrix-js-sdk/src/models/room-state"; + +interface IProps { + userId?: string; + resizeNotifier: ResizeNotifier; +} + +interface IState { + loading: boolean; + member?: RoomMember; +} @replaceableComponent("structures.UserView") -export default class UserView extends React.Component { - static get propTypes() { - return { - userId: PropTypes.string, +export default class UserView extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + loading: true, }; } - constructor(props) { - super(props); - this.state = {}; - } - - componentDidMount() { + public componentDidMount(): void { if (this.props.userId) { - this._loadProfileInfo(); + this.loadProfileInfo(); } } - componentDidUpdate(prevProps) { + public componentDidUpdate(prevProps: IProps): void { // XXX: We shouldn't need to null check the userId here, but we declare // it as optional and MatrixChat sometimes fires in a way which results // in an NPE when we try to update the profile info. if (prevProps.userId !== this.props.userId && this.props.userId) { - this._loadProfileInfo(); + this.loadProfileInfo(); } } - async _loadProfileInfo() { + private async loadProfileInfo(): Promise { const cli = MatrixClientPeg.get(); this.setState({ loading: true }); let profileInfo; try { profileInfo = await cli.getProfileInfo(this.props.userId); } catch (err) { - const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog(_t('Could not load user profile'), '', ErrorDialog, { title: _t('Could not load user profile'), description: ((err && err.message) ? err.message : _t("Operation failed")), @@ -69,20 +78,18 @@ export default class UserView extends React.Component { this.setState({ loading: false }); return; } + const fakeRoomState = new RoomState("roomId"); const fakeEvent = new MatrixEvent({ type: "m.room.member", content: profileInfo }); const member = new RoomMember(null, this.props.userId); - member.setMembershipEvent(fakeEvent); + member.setMembershipEvent(fakeEvent, fakeRoomState); this.setState({ member, loading: false }); } - render() { + public render(): JSX.Element { if (this.state.loading) { - const Spinner = sdk.getComponent("elements.Spinner"); return ; - } else if (this.state.member) { - const RightPanel = sdk.getComponent('structures.RightPanel'); - const MainSplit = sdk.getComponent('structures.MainSplit'); - const panel = ; + } else if (this.state.member?.user) { + const panel = ; return ( ); From 9a7e2b31d4da2957ae7213f33bc9c3e2c6f3a4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 09:24:46 +0200 Subject: [PATCH 06/14] Convert IndicatorScrollbar to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...torScrollbar.js => IndicatorScrollbar.tsx} | 148 ++++++++---------- 1 file changed, 69 insertions(+), 79 deletions(-) rename src/components/structures/{IndicatorScrollbar.js => IndicatorScrollbar.tsx} (54%) diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.tsx similarity index 54% rename from src/components/structures/IndicatorScrollbar.js rename to src/components/structures/IndicatorScrollbar.tsx index 3e1940955b..56929763c9 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.tsx @@ -14,34 +14,39 @@ 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, { createRef } from "react"; import AutoHideScrollbar from "./AutoHideScrollbar"; import { replaceableComponent } from "../../utils/replaceableComponent"; +interface IProps { + // If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator + // and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning + // by the parent element. + trackHorizontalOverflow?: boolean; + + // If true, when the user tries to use their mouse wheel in the component it will + // scroll horizontally rather than vertically. This should only be used on components + // with no vertical scroll opportunity. + verticalScrollsHorizontally?: boolean; + + children: JSX.Element | JSX.Element[]; + className: string; +} + +interface IState { + leftIndicatorOffset: number | string; + rightIndicatorOffset: number | string; +} + @replaceableComponent("structures.IndicatorScrollbar") -export default class IndicatorScrollbar extends React.Component { - static propTypes = { - // If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator - // and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning - // by the parent element. - trackHorizontalOverflow: PropTypes.bool, +export default class IndicatorScrollbar extends React.Component { + private autoHideScrollbar = createRef(); + private scrollElement: HTMLDivElement; + private likelyTrackpadUser: boolean = null; + private checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser - // If true, when the user tries to use their mouse wheel in the component it will - // scroll horizontally rather than vertically. This should only be used on components - // with no vertical scroll opportunity. - verticalScrollsHorizontally: PropTypes.bool, - }; - - constructor(props) { + constructor(props: IProps) { super(props); - this._collectScroller = this._collectScroller.bind(this); - this._collectScrollerComponent = this._collectScrollerComponent.bind(this); - this.checkOverflow = this.checkOverflow.bind(this); - this._scrollElement = null; - this._autoHideScrollbar = null; - this._likelyTrackpadUser = null; - this._checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser this.state = { leftIndicatorOffset: 0, @@ -49,30 +54,19 @@ export default class IndicatorScrollbar extends React.Component { }; } - moveToOrigin() { - if (!this._scrollElement) return; - - this._scrollElement.scrollLeft = 0; - this._scrollElement.scrollTop = 0; - } - - _collectScroller(scroller) { - if (scroller && !this._scrollElement) { - this._scrollElement = scroller; + private collectScroller = (scroller: HTMLDivElement): void => { + if (scroller && !this.scrollElement) { + this.scrollElement = scroller; // Using the passive option to not block the main thread // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners - this._scrollElement.addEventListener("scroll", this.checkOverflow, { passive: true }); + this.scrollElement.addEventListener("scroll", this.checkOverflow, { passive: true }); this.checkOverflow(); } - } + }; - _collectScrollerComponent(autoHideScrollbar) { - this._autoHideScrollbar = autoHideScrollbar; - } - - componentDidUpdate(prevProps) { - const prevLen = prevProps && prevProps.children && prevProps.children.length || 0; - const curLen = this.props.children && this.props.children.length || 0; + public componentDidUpdate(prevProps: IProps): void { + const prevLen = ("length" in prevProps?.children) ? prevProps.children.length : 0; + const curLen = ("length" in this.props?.children) ? this.props.children.length : 0; // check overflow only if amount of children changes. // if we don't guard here, we end up with an infinite // render > componentDidUpdate > checkOverflow > setState > render loop @@ -81,62 +75,58 @@ export default class IndicatorScrollbar extends React.Component { } } - componentDidMount() { + public componentDidMount(): void { this.checkOverflow(); } - checkOverflow() { - const hasTopOverflow = this._scrollElement.scrollTop > 0; - const hasBottomOverflow = this._scrollElement.scrollHeight > - (this._scrollElement.scrollTop + this._scrollElement.clientHeight); - const hasLeftOverflow = this._scrollElement.scrollLeft > 0; - const hasRightOverflow = this._scrollElement.scrollWidth > - (this._scrollElement.scrollLeft + this._scrollElement.clientWidth); + private checkOverflow = (): void => { + const hasTopOverflow = this.scrollElement.scrollTop > 0; + const hasBottomOverflow = this.scrollElement.scrollHeight > + (this.scrollElement.scrollTop + this.scrollElement.clientHeight); + const hasLeftOverflow = this.scrollElement.scrollLeft > 0; + const hasRightOverflow = this.scrollElement.scrollWidth > + (this.scrollElement.scrollLeft + this.scrollElement.clientWidth); if (hasTopOverflow) { - this._scrollElement.classList.add("mx_IndicatorScrollbar_topOverflow"); + this.scrollElement.classList.add("mx_IndicatorScrollbar_topOverflow"); } else { - this._scrollElement.classList.remove("mx_IndicatorScrollbar_topOverflow"); + this.scrollElement.classList.remove("mx_IndicatorScrollbar_topOverflow"); } if (hasBottomOverflow) { - this._scrollElement.classList.add("mx_IndicatorScrollbar_bottomOverflow"); + this.scrollElement.classList.add("mx_IndicatorScrollbar_bottomOverflow"); } else { - this._scrollElement.classList.remove("mx_IndicatorScrollbar_bottomOverflow"); + this.scrollElement.classList.remove("mx_IndicatorScrollbar_bottomOverflow"); } if (hasLeftOverflow) { - this._scrollElement.classList.add("mx_IndicatorScrollbar_leftOverflow"); + this.scrollElement.classList.add("mx_IndicatorScrollbar_leftOverflow"); } else { - this._scrollElement.classList.remove("mx_IndicatorScrollbar_leftOverflow"); + this.scrollElement.classList.remove("mx_IndicatorScrollbar_leftOverflow"); } if (hasRightOverflow) { - this._scrollElement.classList.add("mx_IndicatorScrollbar_rightOverflow"); + this.scrollElement.classList.add("mx_IndicatorScrollbar_rightOverflow"); } else { - this._scrollElement.classList.remove("mx_IndicatorScrollbar_rightOverflow"); + this.scrollElement.classList.remove("mx_IndicatorScrollbar_rightOverflow"); } if (this.props.trackHorizontalOverflow) { this.setState({ // Offset from absolute position of the container - leftIndicatorOffset: hasLeftOverflow ? `${this._scrollElement.scrollLeft}px` : '0', + leftIndicatorOffset: hasLeftOverflow ? `${this.scrollElement.scrollLeft}px` : '0', // Negative because we're coming from the right - rightIndicatorOffset: hasRightOverflow ? `-${this._scrollElement.scrollLeft}px` : '0', + rightIndicatorOffset: hasRightOverflow ? `-${this.scrollElement.scrollLeft}px` : '0', }); } - } + }; - getScrollTop() { - return this._autoHideScrollbar.getScrollTop(); - } - - componentWillUnmount() { - if (this._scrollElement) { - this._scrollElement.removeEventListener("scroll", this.checkOverflow); + public componentWillUnmount(): void { + if (this.scrollElement) { + this.scrollElement.removeEventListener("scroll", this.checkOverflow); } } - onMouseWheel = (e) => { - if (this.props.verticalScrollsHorizontally && this._scrollElement) { + private onMouseWheel = (e: React.WheelEvent): void => { + if (this.props.verticalScrollsHorizontally && this.scrollElement) { // xyThreshold is the amount of horizontal motion required for the component to // ignore the vertical delta in a scroll. Used to stop trackpads from acting in // strange ways. Should be positive. @@ -150,19 +140,19 @@ export default class IndicatorScrollbar extends React.Component { // for at least the next 1 minute. const now = new Date().getTime(); if (Math.abs(e.deltaX) > 0) { - this._likelyTrackpadUser = true; - this._checkAgainForTrackpad = now + (1 * 60 * 1000); + this.likelyTrackpadUser = true; + this.checkAgainForTrackpad = now + (1 * 60 * 1000); } else { // if we haven't seen any horizontal scrolling for a while, assume // the user might have plugged in a mousewheel - if (this._likelyTrackpadUser && now >= this._checkAgainForTrackpad) { - this._likelyTrackpadUser = false; + if (this.likelyTrackpadUser && now >= this.checkAgainForTrackpad) { + this.likelyTrackpadUser = false; } } // don't mess with the horizontal scroll for trackpad users // See https://github.com/vector-im/element-web/issues/10005 - if (this._likelyTrackpadUser) { + if (this.likelyTrackpadUser) { return; } @@ -178,13 +168,13 @@ export default class IndicatorScrollbar extends React.Component { // noinspection JSSuspiciousNameCombination const val = Math.abs(e.deltaY) < 25 ? (e.deltaY + additionalScroll) : e.deltaY; - this._scrollElement.scrollLeft += val * yRetention; + this.scrollElement.scrollLeft += val * yRetention; } } }; - render() { - // eslint-disable-next-line no-unused-vars + public render(): JSX.Element { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props; const leftIndicatorStyle = { left: this.state.leftIndicatorOffset }; @@ -195,8 +185,8 @@ export default class IndicatorScrollbar extends React.Component { ?
: null; return ( From e48407a1d607eb29bc799626b1b74ee3fb0001d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 09:33:33 +0200 Subject: [PATCH 07/14] Convert ViewSource to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{ViewSource.js => ViewSource.tsx} | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) rename src/components/structures/{ViewSource.js => ViewSource.tsx} (90%) diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.tsx similarity index 90% rename from src/components/structures/ViewSource.js rename to src/components/structures/ViewSource.tsx index 2bfa20e892..c0bcaadf7f 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.tsx @@ -17,24 +17,29 @@ limitations under the License. */ import React from "react"; -import PropTypes from "prop-types"; import SyntaxHighlight from "../views/elements/SyntaxHighlight"; import { _t } from "../../languageHandler"; -import * as sdk from "../../index"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { SendCustomEvent } from "../views/dialogs/DevtoolsDialog"; import { canEditContent } from "../../utils/EventUtils"; import { MatrixClientPeg } from '../../MatrixClientPeg'; import { replaceableComponent } from "../../utils/replaceableComponent"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { IDialogProps } from "../views/dialogs/IDialogProps"; +import BaseDialog from "../views/dialogs/BaseDialog"; + +interface IProps extends IDialogProps { + onFinished: () => void; + mxEvent: MatrixEvent; // the MatrixEvent associated with the context menu +} + +interface IState { + isEditing: boolean; +} @replaceableComponent("structures.ViewSource") -export default class ViewSource extends React.Component { - static propTypes = { - onFinished: PropTypes.func.isRequired, - mxEvent: PropTypes.object.isRequired, // the MatrixEvent associated with the context menu - }; - - constructor(props) { +export default class ViewSource extends React.Component { + constructor(props: IProps) { super(props); this.state = { @@ -42,19 +47,20 @@ export default class ViewSource extends React.Component { }; } - onBack() { + private onBack(): void { // TODO: refresh the "Event ID:" modal header this.setState({ isEditing: false }); } - onEdit() { + private onEdit(): void { this.setState({ isEditing: true }); } // returns the dialog body for viewing the event source - viewSourceContent() { + private viewSourceContent(): JSX.Element { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isEncrypted = mxEvent.isEncrypted(); + // @ts-ignore const decryptedEventSource = mxEvent.clearEvent; // FIXME: clearEvent is private const originalEventSource = mxEvent.event; @@ -86,7 +92,7 @@ export default class ViewSource extends React.Component { } // returns the id of the initial message, not the id of the previous edit - getBaseEventId() { + private getBaseEventId(): string { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isEncrypted = mxEvent.isEncrypted(); const baseMxEvent = this.props.mxEvent; @@ -100,7 +106,7 @@ export default class ViewSource extends React.Component { } // returns the SendCustomEvent component prefilled with the correct details - editSourceContent() { + private editSourceContent(): JSX.Element { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isStateEvent = mxEvent.isState(); @@ -159,14 +165,13 @@ export default class ViewSource extends React.Component { } } - canSendStateEvent(mxEvent) { + private canSendStateEvent(mxEvent: MatrixEvent): boolean { const cli = MatrixClientPeg.get(); const room = cli.getRoom(mxEvent.getRoomId()); return room.currentState.mayClientSendStateEvent(mxEvent.getType(), cli); } - render() { - const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog"); + public render(): JSX.Element { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isEditing = this.state.isEditing; From 1590fe32cf75b031f3780e61a6c330f6518cc948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 12 Sep 2021 10:01:14 +0200 Subject: [PATCH 08/14] Convert RoomStatusBar to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{RoomStatusBar.js => RoomStatusBar.tsx} | 144 ++++++++++-------- 1 file changed, 78 insertions(+), 66 deletions(-) rename src/components/structures/{RoomStatusBar.js => RoomStatusBar.tsx} (73%) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.tsx similarity index 73% rename from src/components/structures/RoomStatusBar.js rename to src/components/structures/RoomStatusBar.tsx index 8b10c54cba..b38bb6cfa7 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.tsx @@ -15,7 +15,6 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { _t, _td } from '../../languageHandler'; import { MatrixClientPeg } from '../../MatrixClientPeg'; import Resend from '../../Resend'; @@ -23,87 +22,100 @@ import dis from '../../dispatcher/dispatcher'; import { messageForResourceLimitError } from '../../utils/ErrorUtils'; import { Action } from "../../dispatcher/actions"; import { replaceableComponent } from "../../utils/replaceableComponent"; -import { EventStatus } from "matrix-js-sdk/src/models/event"; +import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event"; import NotificationBadge from "../views/rooms/NotificationBadge"; import { StaticNotificationState } from "../../stores/notifications/StaticNotificationState"; import AccessibleButton from "../views/elements/AccessibleButton"; import InlineSpinner from "../views/elements/InlineSpinner"; +import { SyncState } from "matrix-js-sdk/src/sync.api"; +import { ISyncStateData } from "matrix-js-sdk/src/sync"; +import { Room } from "matrix-js-sdk/src/models/room"; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; const STATUS_BAR_EXPANDED_LARGE = 2; -export function getUnsentMessages(room) { +export function getUnsentMessages(room: Room): MatrixEvent[] { if (!room) { return []; } return room.getPendingEvents().filter(function(ev) { return ev.status === EventStatus.NOT_SENT; }); } +interface IProps { + // the room this statusbar is representing. + room: Room; + + // true if the room is being peeked at. This affects components that shouldn't + // logically be shown when peeking, such as a prompt to invite people to a room. + isPeeking?: boolean; + // callback for when the user clicks on the 'resend all' button in the + // 'unsent messages' bar + onResendAllClick?: () => void; + + // callback for when the user clicks on the 'cancel all' button in the + // 'unsent messages' bar + onCancelAllClick?: () => void; + + // callback for when the user clicks on the 'invite others' button in the + // 'you are alone' bar + onInviteClick?: () => void; + + // callback for when we do something that changes the size of the + // status bar. This is used to trigger a re-layout in the parent + // component. + onResize?: () => void; + + // callback for when the status bar can be hidden from view, as it is + // not displaying anything + onHidden?: () => void; + + // callback for when the status bar is displaying something and should + // be visible + onVisible?: () => void; +} + +interface IState { + syncState: SyncState; + syncStateData: ISyncStateData; + unsentMessages: MatrixEvent[]; + isResending: boolean; +} + @replaceableComponent("structures.RoomStatusBar") -export default class RoomStatusBar extends React.PureComponent { - static propTypes = { - // the room this statusbar is representing. - room: PropTypes.object.isRequired, +export default class RoomStatusBar extends React.PureComponent { + constructor(props: IProps) { + super(props); - // true if the room is being peeked at. This affects components that shouldn't - // logically be shown when peeking, such as a prompt to invite people to a room. - isPeeking: PropTypes.bool, + this.state = { + syncState: MatrixClientPeg.get().getSyncState(), + syncStateData: MatrixClientPeg.get().getSyncStateData(), + unsentMessages: getUnsentMessages(this.props.room), + isResending: false, + }; + } - // callback for when the user clicks on the 'resend all' button in the - // 'unsent messages' bar - onResendAllClick: PropTypes.func, - - // callback for when the user clicks on the 'cancel all' button in the - // 'unsent messages' bar - onCancelAllClick: PropTypes.func, - - // callback for when the user clicks on the 'invite others' button in the - // 'you are alone' bar - onInviteClick: PropTypes.func, - - // callback for when we do something that changes the size of the - // status bar. This is used to trigger a re-layout in the parent - // component. - onResize: PropTypes.func, - - // callback for when the status bar can be hidden from view, as it is - // not displaying anything - onHidden: PropTypes.func, - - // callback for when the status bar is displaying something and should - // be visible - onVisible: PropTypes.func, - }; - - state = { - syncState: MatrixClientPeg.get().getSyncState(), - syncStateData: MatrixClientPeg.get().getSyncStateData(), - unsentMessages: getUnsentMessages(this.props.room), - isResending: false, - }; - - componentDidMount() { + public componentDidMount(): void { MatrixClientPeg.get().on("sync", this.onSyncStateChange); - MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated); + MatrixClientPeg.get().on("Room.localEchoUpdated", this.onRoomLocalEchoUpdated); - this._checkSize(); + this.checkSize(); } - componentDidUpdate() { - this._checkSize(); + public componentDidUpdate(): void { + this.checkSize(); } - componentWillUnmount() { + public componentWillUnmount(): void { // we may have entirely lost our client as we're logging out before clicking login on the guest bar... const client = MatrixClientPeg.get(); if (client) { client.removeListener("sync", this.onSyncStateChange); - client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated); + client.removeListener("Room.localEchoUpdated", this.onRoomLocalEchoUpdated); } } - onSyncStateChange = (state, prevState, data) => { + private onSyncStateChange = (state: SyncState, prevState: SyncState, data: ISyncStateData): void => { if (state === "SYNCING" && prevState === "SYNCING") { return; } @@ -113,7 +125,7 @@ export default class RoomStatusBar extends React.PureComponent { }); }; - _onResendAllClick = () => { + private onResendAllClick = (): void => { Resend.resendUnsentEvents(this.props.room).then(() => { this.setState({ isResending: false }); }); @@ -121,12 +133,12 @@ export default class RoomStatusBar extends React.PureComponent { dis.fire(Action.FocusSendMessageComposer); }; - _onCancelAllClick = () => { + private onCancelAllClick = (): void => { Resend.cancelUnsentEvents(this.props.room); dis.fire(Action.FocusSendMessageComposer); }; - _onRoomLocalEchoUpdated = (event, room, oldEventId, oldStatus) => { + private onRoomLocalEchoUpdated = (ev: MatrixEvent, room: Room) => { if (room.roomId !== this.props.room.roomId) return; const messages = getUnsentMessages(this.props.room); this.setState({ @@ -136,8 +148,8 @@ export default class RoomStatusBar extends React.PureComponent { }; // Check whether current size is greater than 0, if yes call props.onVisible - _checkSize() { - if (this._getSize()) { + private checkSize(): void { + if (this.getSize()) { if (this.props.onVisible) this.props.onVisible(); } else { if (this.props.onHidden) this.props.onHidden(); @@ -147,8 +159,8 @@ export default class RoomStatusBar extends React.PureComponent { // We don't need the actual height - just whether it is likely to have // changed - so we use '0' to indicate normal size, and other values to // indicate other sizes. - _getSize() { - if (this._shouldShowConnectionError()) { + private getSize(): number { + if (this.shouldShowConnectionError()) { return STATUS_BAR_EXPANDED; } else if (this.state.unsentMessages.length > 0 || this.state.isResending) { return STATUS_BAR_EXPANDED_LARGE; @@ -156,7 +168,7 @@ export default class RoomStatusBar extends React.PureComponent { return STATUS_BAR_HIDDEN; } - _shouldShowConnectionError() { + private shouldShowConnectionError(): boolean { // no conn bar trumps the "some not sent" msg since you can't resend without // a connection! // There's one situation in which we don't show this 'no connection' bar, and that's @@ -164,12 +176,12 @@ export default class RoomStatusBar extends React.PureComponent { const errorIsMauError = Boolean( this.state.syncStateData && this.state.syncStateData.error && - this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED', + this.state.syncStateData.error.name === 'M_RESOURCE_LIMIT_EXCEEDED', ); return this.state.syncState === "ERROR" && !errorIsMauError; } - _getUnsentMessageContent() { + private getUnsentMessageContent(): JSX.Element { const unsentMessages = this.state.unsentMessages; let title; @@ -221,10 +233,10 @@ export default class RoomStatusBar extends React.PureComponent { } let buttonRow = <> - + { _t("Delete all") } - + { _t("Retry all") } ; @@ -260,8 +272,8 @@ export default class RoomStatusBar extends React.PureComponent { ; } - render() { - if (this._shouldShowConnectionError()) { + public render(): JSX.Element { + if (this.shouldShowConnectionError()) { return (
@@ -287,7 +299,7 @@ export default class RoomStatusBar extends React.PureComponent { } if (this.state.unsentMessages.length > 0 || this.state.isResending) { - return this._getUnsentMessageContent(); + return this.getUnsentMessageContent(); } return null; From e4cf7314f5280b203e6b796e55f884e9316eba64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 13 Sep 2021 18:14:55 +0200 Subject: [PATCH 09/14] Fix init value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/EmbeddedPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/EmbeddedPage.tsx b/src/components/structures/EmbeddedPage.tsx index 38b9668372..89c1582ee7 100644 --- a/src/components/structures/EmbeddedPage.tsx +++ b/src/components/structures/EmbeddedPage.tsx @@ -43,7 +43,7 @@ interface IState { export default class EmbeddedPage extends React.PureComponent { public static contextType = MatrixClientContext; - private unmounted = true; + private unmounted = false; private dispatcherRef: string; constructor(props: IProps, context: typeof MatrixClientContext) { From 44141bf048326ab45fe3c8857108ddcc92f8ee10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 13 Sep 2021 18:23:37 +0200 Subject: [PATCH 10/14] Iterate PR first round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/EmbeddedPage.tsx | 7 +++---- src/components/structures/GenericErrorPage.tsx | 4 ++-- src/components/structures/ViewSource.tsx | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/structures/EmbeddedPage.tsx b/src/components/structures/EmbeddedPage.tsx index 89c1582ee7..07c14475f2 100644 --- a/src/components/structures/EmbeddedPage.tsx +++ b/src/components/structures/EmbeddedPage.tsx @@ -25,6 +25,7 @@ import { MatrixClientPeg } from '../../MatrixClientPeg'; import classnames from 'classnames'; import MatrixClientContext from "../../contexts/MatrixClientContext"; import AutoHideScrollbar from "./AutoHideScrollbar"; +import { ActionPayload } from "../../dispatcher/payloads"; interface IProps { // URL to request embedded page content from @@ -44,13 +45,11 @@ interface IState { export default class EmbeddedPage extends React.PureComponent { public static contextType = MatrixClientContext; private unmounted = false; - private dispatcherRef: string; + private dispatcherRef: string = null; constructor(props: IProps, context: typeof MatrixClientContext) { super(props, context); - this.dispatcherRef = null; - this.state = { page: '', }; @@ -105,7 +104,7 @@ export default class EmbeddedPage extends React.PureComponent { if (this.dispatcherRef !== null) dis.unregister(this.dispatcherRef); } - private onAction = (payload): void => { + private onAction = (payload: ActionPayload): void => { // HACK: Workaround for the context's MatrixClient not being set up at render time. if (payload.action === 'client_started') { this.forceUpdate(); diff --git a/src/components/structures/GenericErrorPage.tsx b/src/components/structures/GenericErrorPage.tsx index e3b2133ae8..d124c7111a 100644 --- a/src/components/structures/GenericErrorPage.tsx +++ b/src/components/structures/GenericErrorPage.tsx @@ -18,8 +18,8 @@ import React from 'react'; import { replaceableComponent } from "../../utils/replaceableComponent"; interface IProps { - title: JSX.Element; - message: JSX.Element; + title: React.ReactNode; + message: React.ReactNode; } @replaceableComponent("structures.GenericErrorPage") diff --git a/src/components/structures/ViewSource.tsx b/src/components/structures/ViewSource.tsx index c0bcaadf7f..20bbece755 100644 --- a/src/components/structures/ViewSource.tsx +++ b/src/components/structures/ViewSource.tsx @@ -29,7 +29,6 @@ import { IDialogProps } from "../views/dialogs/IDialogProps"; import BaseDialog from "../views/dialogs/BaseDialog"; interface IProps extends IDialogProps { - onFinished: () => void; mxEvent: MatrixEvent; // the MatrixEvent associated with the context menu } From 807792dc693a0dc643203d76708250a092d94cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 13 Sep 2021 18:24:53 +0200 Subject: [PATCH 11/14] Revert some changes that are unncessary due to js-sdk changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/UserView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/structures/UserView.tsx b/src/components/structures/UserView.tsx index 81d9e83428..b717caf753 100644 --- a/src/components/structures/UserView.tsx +++ b/src/components/structures/UserView.tsx @@ -78,10 +78,9 @@ export default class UserView extends React.Component { this.setState({ loading: false }); return; } - const fakeRoomState = new RoomState("roomId"); const fakeEvent = new MatrixEvent({ type: "m.room.member", content: profileInfo }); const member = new RoomMember(null, this.props.userId); - member.setMembershipEvent(fakeEvent, fakeRoomState); + member.setMembershipEvent(fakeEvent); this.setState({ member, loading: false }); } From 8a86407a4cf59c02f2af5bfeacd4862f435d8528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 13 Sep 2021 18:28:52 +0200 Subject: [PATCH 12/14] Use MatrixClientContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomStatusBar.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/structures/RoomStatusBar.tsx b/src/components/structures/RoomStatusBar.tsx index b38bb6cfa7..82f68bc435 100644 --- a/src/components/structures/RoomStatusBar.tsx +++ b/src/components/structures/RoomStatusBar.tsx @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import { _t, _td } from '../../languageHandler'; -import { MatrixClientPeg } from '../../MatrixClientPeg'; import Resend from '../../Resend'; import dis from '../../dispatcher/dispatcher'; import { messageForResourceLimitError } from '../../utils/ErrorUtils'; @@ -30,6 +29,7 @@ import InlineSpinner from "../views/elements/InlineSpinner"; import { SyncState } from "matrix-js-sdk/src/sync.api"; import { ISyncStateData } from "matrix-js-sdk/src/sync"; import { Room } from "matrix-js-sdk/src/models/room"; +import MatrixClientContext from "../../contexts/MatrixClientContext"; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -84,20 +84,23 @@ interface IState { @replaceableComponent("structures.RoomStatusBar") export default class RoomStatusBar extends React.PureComponent { - constructor(props: IProps) { - super(props); + public static contextType = MatrixClientContext; + + constructor(props: IProps, context: typeof MatrixClientContext) { + super(props, context); this.state = { - syncState: MatrixClientPeg.get().getSyncState(), - syncStateData: MatrixClientPeg.get().getSyncStateData(), + syncState: this.context.getSyncState(), + syncStateData: this.context.getSyncStateData(), unsentMessages: getUnsentMessages(this.props.room), isResending: false, }; } public componentDidMount(): void { - MatrixClientPeg.get().on("sync", this.onSyncStateChange); - MatrixClientPeg.get().on("Room.localEchoUpdated", this.onRoomLocalEchoUpdated); + const client = this.context; + client.on("sync", this.onSyncStateChange); + client.on("Room.localEchoUpdated", this.onRoomLocalEchoUpdated); this.checkSize(); } @@ -108,7 +111,7 @@ export default class RoomStatusBar extends React.PureComponent { public componentWillUnmount(): void { // we may have entirely lost our client as we're logging out before clicking login on the guest bar... - const client = MatrixClientPeg.get(); + const client = this.context; if (client) { client.removeListener("sync", this.onSyncStateChange); client.removeListener("Room.localEchoUpdated", this.onRoomLocalEchoUpdated); From c460d56e504c0b1fb1899dc0a980adc54ca6f3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 14 Sep 2021 06:37:08 +0200 Subject: [PATCH 13/14] Remove unused import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/UserView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/UserView.tsx b/src/components/structures/UserView.tsx index b717caf753..0b686995fd 100644 --- a/src/components/structures/UserView.tsx +++ b/src/components/structures/UserView.tsx @@ -28,7 +28,6 @@ import MainSplit from "./MainSplit"; import RightPanel from "./RightPanel"; import Spinner from "../views/elements/Spinner"; import ResizeNotifier from "../../utils/ResizeNotifier"; -import { RoomState } from "matrix-js-sdk/src/models/room-state"; interface IProps { userId?: string; From b26bec949d309c09074e11fe99723e806407b88e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 14 Sep 2021 17:19:57 +0200 Subject: [PATCH 14/14] Use React.Children.count() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/IndicatorScrollbar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/IndicatorScrollbar.tsx b/src/components/structures/IndicatorScrollbar.tsx index 56929763c9..85de659481 100644 --- a/src/components/structures/IndicatorScrollbar.tsx +++ b/src/components/structures/IndicatorScrollbar.tsx @@ -29,7 +29,7 @@ interface IProps { // with no vertical scroll opportunity. verticalScrollsHorizontally?: boolean; - children: JSX.Element | JSX.Element[]; + children: React.ReactNode; className: string; } @@ -65,8 +65,8 @@ export default class IndicatorScrollbar extends React.Component }; public componentDidUpdate(prevProps: IProps): void { - const prevLen = ("length" in prevProps?.children) ? prevProps.children.length : 0; - const curLen = ("length" in this.props?.children) ? this.props.children.length : 0; + const prevLen = React.Children.count(prevProps.children); + const curLen = React.Children.count(this.props.children); // check overflow only if amount of children changes. // if we don't guard here, we end up with an infinite // render > componentDidUpdate > checkOverflow > setState > render loop