From 41d5865dd72e4a9fb9c67932d39531097ce909e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 16 Jul 2021 19:26:04 +0200 Subject: [PATCH 01/10] Cleanup _ReplyTile.scss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_ReplyTile.scss | 142 ++++++++++++++-------------- 1 file changed, 69 insertions(+), 73 deletions(-) diff --git a/res/css/views/rooms/_ReplyTile.scss b/res/css/views/rooms/_ReplyTile.scss index c8f76ee995..f3e204e415 100644 --- a/res/css/views/rooms/_ReplyTile.scss +++ b/res/css/views/rooms/_ReplyTile.scss @@ -15,10 +15,9 @@ limitations under the License. */ .mx_ReplyTile { - padding-top: 2px; - padding-bottom: 2px; - font-size: $font-14px; position: relative; + padding: 2px 0; + font-size: $font-14px; line-height: $font-16px; &.mx_ReplyTile_audio .mx_MFileBody_info_icon::before { @@ -38,86 +37,83 @@ limitations under the License. display: none; } } -} -.mx_ReplyTile > a { - display: flex; - flex-direction: column; - text-decoration: none; - color: $primary-fg-color; -} - -.mx_ReplyTile .mx_RedactedBody { - padding: 4px 0 2px 20px; - - &::before { - height: 13px; - width: 13px; - top: 5px; - } -} - -// We do reply size limiting with CSS to avoid duplicating the TextualBody component. -.mx_ReplyTile .mx_EventTile_content { - $reply-lines: 2; - $line-height: $font-22px; - - pointer-events: none; - - text-overflow: ellipsis; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: $reply-lines; - line-height: $line-height; - - .mx_EventTile_body.mx_EventTile_bigEmoji { - line-height: $line-height !important; - // Override the big emoji override - font-size: $font-14px !important; + > a { + display: flex; + flex-direction: column; + text-decoration: none; + color: $primary-fg-color; } - // Hide line numbers - .mx_EventTile_lineNumbers { - display: none; + .mx_RedactedBody { + padding: 4px 0 2px 20px; + + &::before { + height: 13px; + width: 13px; + top: 5px; + } } - // Hack to cut content in
 tags too
-    .mx_EventTile_pre_container > pre {
-        overflow: hidden;
+    // We do reply size limiting with CSS to avoid duplicating the TextualBody component.
+    .mx_EventTile_content {
+        $reply-lines: 2;
+        $line-height: $font-22px;
+
+        pointer-events: none;
+
         text-overflow: ellipsis;
         display: -webkit-box;
         -webkit-box-orient: vertical;
         -webkit-line-clamp: $reply-lines;
-        padding: 4px;
+        line-height: $line-height;
+
+        .mx_EventTile_body.mx_EventTile_bigEmoji {
+            line-height: $line-height !important;
+            font-size: $font-14px !important; // Override the big emoji override
+        }
+
+        // Hide line numbers
+        .mx_EventTile_lineNumbers {
+            display: none;
+        }
+
+        // Hack to cut content in 
 tags too
+        .mx_EventTile_pre_container > pre {
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-box-orient: vertical;
+            -webkit-line-clamp: $reply-lines;
+            padding: 4px;
+        }
+
+        .markdown-body blockquote,
+        .markdown-body dl,
+        .markdown-body ol,
+        .markdown-body p,
+        .markdown-body pre,
+        .markdown-body table,
+        .markdown-body ul {
+            margin-bottom: 4px;
+        }
     }
 
-    .markdown-body blockquote,
-    .markdown-body dl,
-    .markdown-body ol,
-    .markdown-body p,
-    .markdown-body pre,
-    .markdown-body table,
-    .markdown-body ul {
-        margin-bottom: 4px;
+    &.mx_ReplyTile_info {
+        padding-top: 0;
+    }
+
+    .mx_SenderProfile {
+        font-size: $font-14px;
+        line-height: $font-17px;
+
+        display: inline-block; // anti-zalgo, with overflow hidden
+        padding: 0;
+        margin: 0;
+
+        // truncate long display names
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
     }
 }
-
-.mx_ReplyTile.mx_ReplyTile_info {
-    padding-top: 0;
-}
-
-.mx_ReplyTile .mx_SenderProfile {
-    color: $primary-fg-color;
-    font-size: $font-14px;
-    display: inline-block; /* anti-zalgo, with overflow hidden */
-    overflow: hidden;
-    cursor: pointer;
-    padding-left: 0; /* left gutter */
-    padding-bottom: 0;
-    padding-top: 0;
-    margin: 0;
-    line-height: $font-17px;
-    /* the next three lines, along with overflow hidden, truncate long display names */
-    white-space: nowrap;
-    text-overflow: ellipsis;
-}

From 03ce480066b4b6c3f13febec1b3c6ed0abb20529 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 08:35:50 +0200
Subject: [PATCH 02/10] Convert ReplyThread to TS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 .../{ReplyThread.js => ReplyThread.tsx}       | 101 ++++++++++--------
 1 file changed, 56 insertions(+), 45 deletions(-)
 rename src/components/views/elements/{ReplyThread.js => ReplyThread.tsx} (85%)

diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.tsx
similarity index 85%
rename from src/components/views/elements/ReplyThread.js
rename to src/components/views/elements/ReplyThread.tsx
index 89427515e2..652707b5d9 100644
--- a/src/components/views/elements/ReplyThread.js
+++ b/src/components/views/elements/ReplyThread.tsx
@@ -14,14 +14,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
+
 import React from 'react';
 import { _t } from '../../../languageHandler';
-import PropTypes from 'prop-types';
 import dis from '../../../dispatcher/dispatcher';
 import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
 import { makeUserPermalink, RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
 import SettingsStore from "../../../settings/SettingsStore";
-import { LayoutPropType } from "../../../settings/Layout";
+import { Layout } from "../../../settings/Layout";
 import escapeHtml from "escape-html";
 import MatrixClientContext from "../../../contexts/MatrixClientContext";
 import { getUserNameColorClass } from "../../../utils/FormattingUtils";
@@ -32,51 +32,54 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
 import Spinner from './Spinner';
 import ReplyTile from "../rooms/ReplyTile";
 import Pill from './Pill';
+import { Room } from 'matrix-js-sdk/src/models/room';
+
+interface IProps {
+    // the latest event in this chain of replies
+    parentEv?: MatrixEvent,
+    // called when the ReplyThread contents has changed, including EventTiles thereof
+    onHeightChanged: () => void,
+    permalinkCreator: RoomPermalinkCreator,
+    // Specifies which layout to use.
+    layout?: Layout,
+    // Whether to always show a timestamp
+    alwaysShowTimestamps?: boolean,
+}
+
+interface IState {
+    // The loaded events to be rendered as linear-replies
+    events: MatrixEvent[],
+    // The latest loaded event which has not yet been shown
+    loadedEv: MatrixEvent,
+    // Whether the component is still loading more events
+    loading: boolean,
+    // Whether as error was encountered fetching a replied to event.
+    err: boolean,
+}
 
 // This component does no cycle detection, simply because the only way to make such a cycle would be to
 // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
 // be low as each event being loaded (after the first) is triggered by an explicit user action.
 @replaceableComponent("views.elements.ReplyThread")
-export default class ReplyThread extends React.Component {
-    static propTypes = {
-        // the latest event in this chain of replies
-        parentEv: PropTypes.instanceOf(MatrixEvent),
-        // called when the ReplyThread contents has changed, including EventTiles thereof
-        onHeightChanged: PropTypes.func.isRequired,
-        permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
-        // Specifies which layout to use.
-        layout: LayoutPropType,
-        // Whether to always show a timestamp
-        alwaysShowTimestamps: PropTypes.bool,
-    };
-
+export default class ReplyThread extends React.Component {
     static contextType = MatrixClientContext;
+    private unmounted = false;
+    private room: Room;
 
     constructor(props, context) {
         super(props, context);
 
         this.state = {
-            // The loaded events to be rendered as linear-replies
             events: [],
-
-            // The latest loaded event which has not yet been shown
             loadedEv: null,
-            // Whether the component is still loading more events
             loading: true,
-
-            // Whether as error was encountered fetching a replied to event.
             err: false,
         };
 
-        this.unmounted = false;
         this.room = this.context.getRoom(this.props.parentEv.getRoomId());
-
-        this.onQuoteClick = this.onQuoteClick.bind(this);
-        this.canCollapse = this.canCollapse.bind(this);
-        this.collapse = this.collapse.bind(this);
     }
 
-    static getParentEventId(ev) {
+    public static getParentEventId(ev: MatrixEvent): string {
         if (!ev || ev.isRedacted()) return;
 
         // XXX: For newer relations (annotations, replacements, etc.), we now
@@ -92,7 +95,7 @@ export default class ReplyThread extends React.Component {
     }
 
     // Part of Replies fallback support
-    static stripPlainReply(body) {
+    public static stripPlainReply(body: string): string {
         // Removes lines beginning with `> ` until you reach one that doesn't.
         const lines = body.split('\n');
         while (lines.length && lines[0].startsWith('> ')) lines.shift();
@@ -102,7 +105,7 @@ export default class ReplyThread extends React.Component {
     }
 
     // Part of Replies fallback support
-    static stripHTMLReply(html) {
+    public static stripHTMLReply(html: string): string {
         // Sanitize the original HTML for inclusion in .  We allow
         // any HTML, since the original sender could use special tags that we
         // don't recognize, but want to pass along to any recipients who do
@@ -124,7 +127,10 @@ export default class ReplyThread extends React.Component {
     }
 
     // Part of Replies fallback support
-    static getNestedReplyText(ev, permalinkCreator) {
+    public static getNestedReplyText(
+        ev: MatrixEvent,
+        permalinkCreator: RoomPermalinkCreator,
+    ): { body: string, html: string } {
         if (!ev) return null;
 
         let { body, formatted_body: html } = ev.getContent();
@@ -200,7 +206,7 @@ export default class ReplyThread extends React.Component {
         return { body, html };
     }
 
-    static makeReplyMixIn(ev) {
+    public static makeReplyMixIn(ev: MatrixEvent) {
         if (!ev) return {};
         return {
             'm.relates_to': {
@@ -211,10 +217,15 @@ export default class ReplyThread extends React.Component {
         };
     }
 
-    static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, layout, alwaysShowTimestamps) {
-        if (!ReplyThread.getParentEventId(parentEv)) {
-            return null;
-        }
+    public static makeThread(
+        parentEv: MatrixEvent,
+        onHeightChanged: () => void,
+        permalinkCreator: RoomPermalinkCreator,
+        ref: React.RefObject,
+        layout: Layout,
+        alwaysShowTimestamps: boolean,
+    ): JSX.Element {
+        if (!ReplyThread.getParentEventId(parentEv)) return null;
         return  {
         const { parentEv } = this.props;
         // at time of making this component we checked that props.parentEv has a parentEventId
         const ev = await this.getEvent(ReplyThread.getParentEventId(parentEv));
@@ -256,7 +267,7 @@ export default class ReplyThread extends React.Component {
         }
     }
 
-    async getNextEvent(ev) {
+    private async getNextEvent(ev: MatrixEvent): Promise {
         try {
             const inReplyToEventId = ReplyThread.getParentEventId(ev);
             return await this.getEvent(inReplyToEventId);
@@ -265,7 +276,7 @@ export default class ReplyThread extends React.Component {
         }
     }
 
-    async getEvent(eventId) {
+    private async getEvent(eventId: string): Promise {
         if (!eventId) return null;
         const event = this.room.findEventById(eventId);
         if (event) return event;
@@ -282,15 +293,15 @@ export default class ReplyThread extends React.Component {
         return this.room.findEventById(eventId);
     }
 
-    canCollapse() {
+    public canCollapse = (): boolean => {
         return this.state.events.length > 1;
-    }
+    };
 
-    collapse() {
+    public collapse = (): void => {
         this.initialize();
-    }
+    };
 
-    async onQuoteClick() {
+    private onQuoteClick = async (): Promise => {
         const events = [this.state.loadedEv, ...this.state.events];
 
         let loadedEv = null;
@@ -304,9 +315,9 @@ export default class ReplyThread extends React.Component {
         });
 
         dis.fire(Action.FocusSendMessageComposer);
-    }
+    };
 
-    getReplyThreadColorClass(ev) {
+    private getReplyThreadColorClass(ev: MatrixEvent): string {
         return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyThread");
     }
 

From 96acd6c9efd06f18f4b73bf3a6ba8f8d113b7ab2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:03:52 +0200
Subject: [PATCH 03/10] Cleanup _ReplyThread.scss
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/elements/_ReplyThread.scss | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyThread.scss
index af8ca956ba..44532ea6a7 100644
--- a/res/css/views/elements/_ReplyThread.scss
+++ b/res/css/views/elements/_ReplyThread.scss
@@ -16,19 +16,16 @@ limitations under the License.
 
 .mx_ReplyThread {
     margin-top: 0;
-}
-
-.mx_ReplyThread_show {
-    cursor: pointer;
-}
-
-blockquote.mx_ReplyThread {
     margin-left: 0;
     margin-right: 0;
     margin-bottom: 8px;
     padding-left: 10px;
     border-left: 4px solid $button-bg-color;
 
+    .mx_ReplyThread_show {
+        cursor: pointer;
+    }
+
     &.mx_ReplyThread_color1 {
         border-left-color: $username-variant1-color;
     }

From 2a7787e12dccfc16c0533414eaa2d7c0563d8c5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:09:13 +0200
Subject: [PATCH 04/10] Convert ReplyPreview to TS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 .../{ReplyPreview.js => ReplyPreview.tsx}     | 29 ++++++++++++-------
 1 file changed, 18 insertions(+), 11 deletions(-)
 rename src/components/views/rooms/{ReplyPreview.js => ReplyPreview.tsx} (81%)

diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.tsx
similarity index 81%
rename from src/components/views/rooms/ReplyPreview.js
rename to src/components/views/rooms/ReplyPreview.tsx
index c7d19e58db..9682ce2bfe 100644
--- a/src/components/views/rooms/ReplyPreview.js
+++ b/src/components/views/rooms/ReplyPreview.tsx
@@ -22,6 +22,8 @@ import PropTypes from "prop-types";
 import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
 import { replaceableComponent } from "../../../utils/replaceableComponent";
 import ReplyTile from './ReplyTile';
+import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
+import { EventSubscription } from 'fbemitter';
 
 function cancelQuoting() {
     dis.dispatch({
@@ -30,41 +32,46 @@ function cancelQuoting() {
     });
 }
 
+interface IProps {
+    permalinkCreator: RoomPermalinkCreator,
+}
+
+interface IState {
+    event: MatrixEvent
+}
+
 @replaceableComponent("views.rooms.ReplyPreview")
-export default class ReplyPreview extends React.Component {
-    static propTypes = {
-        permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
-    };
+export default class ReplyPreview extends React.Component {
+    private unmounted = false;
+    private roomStoreToken: EventSubscription;
 
     constructor(props) {
         super(props);
-        this.unmounted = false;
 
         this.state = {
             event: RoomViewStore.getQuotingEvent(),
         };
 
-        this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
-        this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
+        this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
     }
 
     componentWillUnmount() {
         this.unmounted = true;
 
         // Remove RoomStore listener
-        if (this._roomStoreToken) {
-            this._roomStoreToken.remove();
+        if (this.roomStoreToken) {
+            this.roomStoreToken.remove();
         }
     }
 
-    _onRoomViewStoreUpdate() {
+    private onRoomViewStoreUpdate = (): void => {
         if (this.unmounted) return;
 
         const event = RoomViewStore.getQuotingEvent();
         if (this.state.event !== event) {
             this.setState({ event });
         }
-    }
+    };
 
     render() {
         if (!this.state.event) return null;

From c9a11af26be3f51b31e8e5f727eaf5479c30ae4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:20:46 +0200
Subject: [PATCH 05/10] Give singletonRoomViewStore a type
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/stores/RoomViewStore.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx
index 10f42f3166..1a85ff59b1 100644
--- a/src/stores/RoomViewStore.tsx
+++ b/src/stores/RoomViewStore.tsx
@@ -429,7 +429,7 @@ class RoomViewStore extends Store {
     }
 }
 
-let singletonRoomViewStore = null;
+let singletonRoomViewStore: RoomViewStore = null;
 if (!singletonRoomViewStore) {
     singletonRoomViewStore = new RoomViewStore();
 }

From e3eac48d053d69489b73bd3e4334049da3455177 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:25:12 +0200
Subject: [PATCH 06/10] Cleanup _ReplyPreview.scss
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_ReplyPreview.scss | 57 +++++++++++++-------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/res/css/views/rooms/_ReplyPreview.scss b/res/css/views/rooms/_ReplyPreview.scss
index c1fe1d9a8b..60feb39d11 100644
--- a/res/css/views/rooms/_ReplyPreview.scss
+++ b/res/css/views/rooms/_ReplyPreview.scss
@@ -22,33 +22,34 @@ limitations under the License.
     max-height: 50vh;
     overflow: auto;
     box-shadow: 0px -16px 32px $composer-shadow-color;
+
+    .mx_ReplyPreview_section {
+        border-bottom: 1px solid $primary-hairline-color;
+
+        .mx_ReplyPreview_header {
+            margin: 8px;
+            color: $primary-fg-color;
+            font-weight: 400;
+            opacity: 0.4;
+        }
+
+        .mx_ReplyPreview_tile {
+            margin: 0 8px;
+        }
+
+        .mx_ReplyPreview_title {
+            float: left;
+        }
+
+        .mx_ReplyPreview_cancel {
+            float: right;
+            cursor: pointer;
+            display: flex;
+        }
+
+        .mx_ReplyPreview_clear {
+            clear: both;
+        }
+    }
 }
 
-.mx_ReplyPreview_section {
-    border-bottom: 1px solid $primary-hairline-color;
-}
-
-.mx_ReplyPreview_header {
-    margin: 8px;
-    color: $primary-fg-color;
-    font-weight: 400;
-    opacity: 0.4;
-}
-
-.mx_ReplyPreview_tile {
-    margin: 0 8px;
-}
-
-.mx_ReplyPreview_title {
-    float: left;
-}
-
-.mx_ReplyPreview_cancel {
-    float: right;
-    cursor: pointer;
-    display: flex;
-}
-
-.mx_ReplyPreview_clear {
-    clear: both;
-}

From 7b45efc9e97970323128c3d38ee22d2ade284289 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:28:02 +0200
Subject: [PATCH 07/10] Fix EventTile typing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/rooms/EventTile.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx
index e0a924f1e7..1bdcccd77f 100644
--- a/src/components/views/rooms/EventTile.tsx
+++ b/src/components/views/rooms/EventTile.tsx
@@ -320,7 +320,7 @@ export default class EventTile extends React.Component {
     private suppressReadReceiptAnimation: boolean;
     private isListeningForReceipts: boolean;
     private tile = React.createRef();
-    private replyThread = React.createRef();
+    private replyThread = React.createRef();
 
     public readonly ref = createRef();
 

From e439d2e9112013ce0a6bae46983cbadda88707bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:29:18 +0200
Subject: [PATCH 08/10] Delint
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/elements/ReplyThread.tsx | 18 +++++++++---------
 src/components/views/rooms/ReplyPreview.tsx   |  5 ++---
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/components/views/elements/ReplyThread.tsx b/src/components/views/elements/ReplyThread.tsx
index 652707b5d9..0eb795e257 100644
--- a/src/components/views/elements/ReplyThread.tsx
+++ b/src/components/views/elements/ReplyThread.tsx
@@ -36,25 +36,25 @@ import { Room } from 'matrix-js-sdk/src/models/room';
 
 interface IProps {
     // the latest event in this chain of replies
-    parentEv?: MatrixEvent,
+    parentEv?: MatrixEvent;
     // called when the ReplyThread contents has changed, including EventTiles thereof
-    onHeightChanged: () => void,
-    permalinkCreator: RoomPermalinkCreator,
+    onHeightChanged: () => void;
+    permalinkCreator: RoomPermalinkCreator;
     // Specifies which layout to use.
-    layout?: Layout,
+    layout?: Layout;
     // Whether to always show a timestamp
-    alwaysShowTimestamps?: boolean,
+    alwaysShowTimestamps?: boolean;
 }
 
 interface IState {
     // The loaded events to be rendered as linear-replies
-    events: MatrixEvent[],
+    events: MatrixEvent[];
     // The latest loaded event which has not yet been shown
-    loadedEv: MatrixEvent,
+    loadedEv: MatrixEvent;
     // Whether the component is still loading more events
-    loading: boolean,
+    loading: boolean;
     // Whether as error was encountered fetching a replied to event.
-    err: boolean,
+    err: boolean;
 }
 
 // This component does no cycle detection, simply because the only way to make such a cycle would be to
diff --git a/src/components/views/rooms/ReplyPreview.tsx b/src/components/views/rooms/ReplyPreview.tsx
index 9682ce2bfe..a3d018ec2d 100644
--- a/src/components/views/rooms/ReplyPreview.tsx
+++ b/src/components/views/rooms/ReplyPreview.tsx
@@ -18,7 +18,6 @@ import React from 'react';
 import dis from '../../../dispatcher/dispatcher';
 import { _t } from '../../../languageHandler';
 import RoomViewStore from '../../../stores/RoomViewStore';
-import PropTypes from "prop-types";
 import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
 import { replaceableComponent } from "../../../utils/replaceableComponent";
 import ReplyTile from './ReplyTile';
@@ -33,11 +32,11 @@ function cancelQuoting() {
 }
 
 interface IProps {
-    permalinkCreator: RoomPermalinkCreator,
+    permalinkCreator: RoomPermalinkCreator;
 }
 
 interface IState {
-    event: MatrixEvent
+    event: MatrixEvent;
 }
 
 @replaceableComponent("views.rooms.ReplyPreview")

From d7e685661423c31546d92d37e7197ee022329741 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sat, 17 Jul 2021 15:37:52 +0200
Subject: [PATCH 09/10] More delint
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/ActiveRoomObserver.ts                 | 3 ++-
 src/components/views/voip/CallPreview.tsx | 5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/ActiveRoomObserver.ts b/src/ActiveRoomObserver.ts
index 1126dc9496..0be49a24ea 100644
--- a/src/ActiveRoomObserver.ts
+++ b/src/ActiveRoomObserver.ts
@@ -15,6 +15,7 @@ limitations under the License.
 */
 
 import RoomViewStore from './stores/RoomViewStore';
+import { EventSubscription } from 'fbemitter';
 
 type Listener = (isActive: boolean) => void;
 
@@ -30,7 +31,7 @@ type Listener = (isActive: boolean) => void;
 export class ActiveRoomObserver {
     private listeners: {[key: string]: Listener[]} = {};
     private _activeRoomId = RoomViewStore.getRoomId();
-    private readonly roomStoreToken: string;
+    private readonly roomStoreToken: EventSubscription;
 
     constructor() {
         // TODO: We could self-destruct when the last listener goes away, or at least stop listening.
diff --git a/src/components/views/voip/CallPreview.tsx b/src/components/views/voip/CallPreview.tsx
index ddcb9057ec..895d9773e4 100644
--- a/src/components/views/voip/CallPreview.tsx
+++ b/src/components/views/voip/CallPreview.tsx
@@ -30,6 +30,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
 import UIStore from '../../../stores/UIStore';
 import { lerp } from '../../../utils/AnimationUtils';
 import { MarkedExecution } from '../../../utils/MarkedExecution';
+import { EventSubscription } from 'fbemitter';
 
 const PIP_VIEW_WIDTH = 336;
 const PIP_VIEW_HEIGHT = 232;
@@ -108,7 +109,7 @@ function getPrimarySecondaryCalls(calls: MatrixCall[]): [MatrixCall, MatrixCall[
  */
 @replaceableComponent("views.voip.CallPreview")
 export default class CallPreview extends React.Component {
-    private roomStoreToken: any;
+    private roomStoreToken: EventSubscription;
     private dispatcherRef: string;
     private settingsWatcherRef: string;
     private callViewWrapper = createRef();
@@ -240,7 +241,7 @@ export default class CallPreview extends React.Component {
         this.scheduledUpdate.mark();
     };
 
-    private onRoomViewStoreUpdate = (payload) => {
+    private onRoomViewStoreUpdate = () => {
         if (RoomViewStore.getRoomId() === this.state.roomId) return;
 
         const roomId = RoomViewStore.getRoomId();

From 21eb299eff245c7e6b4e2c2884d6cbe854b8a3cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Sun, 18 Jul 2021 14:32:24 +0200
Subject: [PATCH 10/10] Make roomStoreToken readonly
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/rooms/ReplyPreview.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/rooms/ReplyPreview.tsx b/src/components/views/rooms/ReplyPreview.tsx
index a3d018ec2d..41b3d2460c 100644
--- a/src/components/views/rooms/ReplyPreview.tsx
+++ b/src/components/views/rooms/ReplyPreview.tsx
@@ -42,7 +42,7 @@ interface IState {
 @replaceableComponent("views.rooms.ReplyPreview")
 export default class ReplyPreview extends React.Component {
     private unmounted = false;
-    private roomStoreToken: EventSubscription;
+    private readonly roomStoreToken: EventSubscription;
 
     constructor(props) {
         super(props);