From 2494af37c862144742c34780e85464366113090f Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 17 Oct 2019 19:08:40 +0100
Subject: [PATCH 1/3] Break UserInfo:RoomAdminToolsContainer into smaller
 components

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
 src/components/views/right_panel/UserInfo.js | 452 ++++++++++---------
 1 file changed, 234 insertions(+), 218 deletions(-)

diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js
index 59cd61a583..292cf87f47 100644
--- a/src/components/views/right_panel/UserInfo.js
+++ b/src/components/views/right_panel/UserInfo.js
@@ -374,6 +374,230 @@ const useRoomPowerLevels = (room) => {
     return powerLevels;
 };
 
+const RoomKickButton = withLegacyMatrixClient(({cli, member, startUpdating, stopUpdating}) => {
+    const onKick = async () => {
+        const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
+        const {finished} = Modal.createTrackedDialog(
+            'Confirm User Action Dialog',
+            'onKick',
+            ConfirmUserActionDialog,
+            {
+                member,
+                action: member.membership === "invite" ? _t("Disinvite") : _t("Kick"),
+                title: member.membership === "invite" ? _t("Disinvite this user?") : _t("Kick this user?"),
+                askReason: member.membership === "join",
+                danger: true,
+            },
+        );
+
+        const [proceed, reason] = await finished;
+        if (!proceed) return;
+
+        startUpdating();
+        cli.kick(member.roomId, member.userId, reason || undefined).then(() => {
+            // NO-OP; rely on the m.room.member event coming down else we could
+            // get out of sync if we force setState here!
+            console.log("Kick success");
+        }, function(err) {
+            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            console.error("Kick error: " + err);
+            Modal.createTrackedDialog('Failed to kick', '', ErrorDialog, {
+                title: _t("Failed to kick"),
+                description: ((err && err.message) ? err.message : "Operation failed"),
+            });
+        }).finally(() => {
+            stopUpdating();
+        });
+    };
+
+    const kickLabel = member.membership === "invite" ? _t("Disinvite") : _t("Kick");
+    return <AccessibleButton className="mx_UserInfo_field" onClick={onKick}>
+        { kickLabel }
+    </AccessibleButton>;
+});
+
+const RedactMessagesButton = withLegacyMatrixClient(({cli, member}) => {
+    const onRedactAllMessages = async () => {
+        const {roomId, userId} = member;
+        const room = cli.getRoom(roomId);
+        if (!room) {
+            return;
+        }
+        let timeline = room.getLiveTimeline();
+        let eventsToRedact = [];
+        while (timeline) {
+            eventsToRedact = timeline.getEvents().reduce((events, event) => {
+                if (event.getSender() === userId && !event.isRedacted()) {
+                    return events.concat(event);
+                } else {
+                    return events;
+                }
+            }, eventsToRedact);
+            timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS);
+        }
+
+        const count = eventsToRedact.length;
+        const user = member.name;
+
+        if (count === 0) {
+            const InfoDialog = sdk.getComponent("dialogs.InfoDialog");
+            Modal.createTrackedDialog('No user messages found to remove', '', InfoDialog, {
+                title: _t("No recent messages by %(user)s found", {user}),
+                description:
+                    <div>
+                        <p>{ _t("Try scrolling up in the timeline to see if there are any earlier ones.") }</p>
+                    </div>,
+            });
+        } else {
+            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+
+            const {finished} = Modal.createTrackedDialog('Remove recent messages by user', '', QuestionDialog, {
+                title: _t("Remove recent messages by %(user)s", {user}),
+                description:
+                    <div>
+                        <p>{ _t("You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?", {count, user}) }</p>
+                        <p>{ _t("For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.") }</p>
+                    </div>,
+                button: _t("Remove %(count)s messages", {count}),
+            });
+
+            const [confirmed] = await finished;
+            if (!confirmed) {
+                return;
+            }
+
+            // Submitting a large number of redactions freezes the UI,
+            // so first yield to allow to rerender after closing the dialog.
+            await Promise.resolve();
+
+            console.info(`Started redacting recent ${count} messages for ${user} in ${roomId}`);
+            await Promise.all(eventsToRedact.map(async event => {
+                try {
+                    await cli.redactEvent(roomId, event.getId());
+                } catch (err) {
+                    // log and swallow errors
+                    console.error("Could not redact", event.getId());
+                    console.error(err);
+                }
+            }));
+            console.info(`Finished redacting recent ${count} messages for ${user} in ${roomId}`);
+        }
+    };
+
+    return <AccessibleButton className="mx_UserInfo_field" onClick={onRedactAllMessages}>
+        { _t("Remove recent messages") }
+    </AccessibleButton>;
+});
+
+const BanToggleButton = withLegacyMatrixClient(({cli, member, startUpdating, stopUpdating}) => {
+    const onBanOrUnban = async () => {
+        const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
+        const {finished} = Modal.createTrackedDialog(
+            'Confirm User Action Dialog',
+            'onBanOrUnban',
+            ConfirmUserActionDialog,
+            {
+                member,
+                action: member.membership === 'ban' ? _t("Unban") : _t("Ban"),
+                title: member.membership === 'ban' ? _t("Unban this user?") : _t("Ban this user?"),
+                askReason: member.membership !== 'ban',
+                danger: member.membership !== 'ban',
+            },
+        );
+
+        const [proceed, reason] = await finished;
+        if (!proceed) return;
+
+        startUpdating();
+        let promise;
+        if (member.membership === 'ban') {
+            promise = cli.unban(member.roomId, member.userId);
+        } else {
+            promise = cli.ban(member.roomId, member.userId, reason || undefined);
+        }
+        promise.then(() => {
+            // NO-OP; rely on the m.room.member event coming down else we could
+            // get out of sync if we force setState here!
+            console.log("Ban success");
+        }, function(err) {
+            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            console.error("Ban error: " + err);
+            Modal.createTrackedDialog('Failed to ban user', '', ErrorDialog, {
+                title: _t("Error"),
+                description: _t("Failed to ban user"),
+            });
+        }).finally(() => {
+            stopUpdating();
+        });
+    };
+
+    let label = _t("Ban");
+    if (member.membership === 'ban') {
+        label = _t("Unban");
+    }
+
+    return <AccessibleButton className="mx_UserInfo_field" onClick={onBanOrUnban}>
+        { label }
+    </AccessibleButton>;
+});
+
+const MuteToggleButton = withLegacyMatrixClient(({cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
+    const isMuted = _isMuted(member, powerLevels);
+    const onMuteToggle = async () => {
+        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+        const roomId = member.roomId;
+        const target = member.userId;
+
+        // if muting self, warn as it may be irreversible
+        if (target === cli.getUserId()) {
+            try {
+                if (!(await _warnSelfDemote())) return;
+            } catch (e) {
+                console.error("Failed to warn about self demotion: ", e);
+                return;
+            }
+        }
+
+        const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
+        if (!powerLevelEvent) return;
+
+        const powerLevels = powerLevelEvent.getContent();
+        const levelToSend = (
+            (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
+            powerLevels.events_default
+        );
+        let level;
+        if (isMuted) { // unmute
+            level = levelToSend;
+        } else { // mute
+            level = levelToSend - 1;
+        }
+        level = parseInt(level);
+
+        if (!isNaN(level)) {
+            startUpdating();
+            cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
+                // NO-OP; rely on the m.room.member event coming down else we could
+                // get out of sync if we force setState here!
+                console.log("Mute toggle success");
+            }, function(err) {
+                console.error("Mute error: " + err);
+                Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
+                    title: _t("Error"),
+                    description: _t("Failed to mute user"),
+                });
+            }).finally(() => {
+                stopUpdating();
+            });
+        }
+    };
+
+    const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
+    return <AccessibleButton className="mx_UserInfo_field" onClick={onMuteToggle}>
+        { muteLabel }
+    </AccessibleButton>;
+});
+
 const RoomAdminToolsContainer = withLegacyMatrixClient(({cli, room, children, member, startUpdating, stopUpdating}) => {
     let kickButton;
     let banButton;
@@ -389,235 +613,27 @@ const RoomAdminToolsContainer = withLegacyMatrixClient(({cli, room, children, me
     const me = room.getMember(cli.getUserId());
     const isMe = me.userId === member.userId;
     const canAffectUser = member.powerLevel < me.powerLevel || isMe;
-    const membership = member.membership;
 
     if (canAffectUser && me.powerLevel >= powerLevels.kick) {
-        const onKick = async () => {
-            const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
-            const {finished} = Modal.createTrackedDialog(
-                'Confirm User Action Dialog',
-                'onKick',
-                ConfirmUserActionDialog,
-                {
-                    member,
-                    action: membership === "invite" ? _t("Disinvite") : _t("Kick"),
-                    title: membership === "invite" ? _t("Disinvite this user?") : _t("Kick this user?"),
-                    askReason: membership === "join",
-                    danger: true,
-                },
-            );
-
-            const [proceed, reason] = await finished;
-            if (!proceed) return;
-
-            startUpdating();
-            cli.kick(member.roomId, member.userId, reason || undefined).then(() => {
-                // NO-OP; rely on the m.room.member event coming down else we could
-                // get out of sync if we force setState here!
-                console.log("Kick success");
-            }, function(err) {
-                const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                console.error("Kick error: " + err);
-                Modal.createTrackedDialog('Failed to kick', '', ErrorDialog, {
-                    title: _t("Failed to kick"),
-                    description: ((err && err.message) ? err.message : "Operation failed"),
-                });
-            }).finally(() => {
-                stopUpdating();
-            });
-        };
-
-        const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
-        kickButton = (
-            <AccessibleButton className="mx_UserInfo_field"
-                              onClick={onKick}>
-                { kickLabel }
-            </AccessibleButton>
-        );
+        kickButton = <RoomKickButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
     }
     if (me.powerLevel >= powerLevels.redact) {
-        const onRedactAllMessages = async () => {
-            const {roomId, userId} = member;
-            const room = cli.getRoom(roomId);
-            if (!room) {
-                return;
-            }
-            let timeline = room.getLiveTimeline();
-            let eventsToRedact = [];
-            while (timeline) {
-                eventsToRedact = timeline.getEvents().reduce((events, event) => {
-                    if (event.getSender() === userId && !event.isRedacted()) {
-                        return events.concat(event);
-                    } else {
-                        return events;
-                    }
-                }, eventsToRedact);
-                timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS);
-            }
-
-            const count = eventsToRedact.length;
-            const user = member.name;
-
-            if (count === 0) {
-                const InfoDialog = sdk.getComponent("dialogs.InfoDialog");
-                Modal.createTrackedDialog('No user messages found to remove', '', InfoDialog, {
-                    title: _t("No recent messages by %(user)s found", {user}),
-                    description:
-                        <div>
-                            <p>{ _t("Try scrolling up in the timeline to see if there are any earlier ones.") }</p>
-                        </div>,
-                });
-            } else {
-                const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
-
-                const {finished} = Modal.createTrackedDialog('Remove recent messages by user', '', QuestionDialog, {
-                    title: _t("Remove recent messages by %(user)s", {user}),
-                    description:
-                        <div>
-                            <p>{ _t("You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?", {count, user}) }</p>
-                            <p>{ _t("For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.") }</p>
-                        </div>,
-                    button: _t("Remove %(count)s messages", {count}),
-                });
-
-                const [confirmed] = await finished;
-                if (!confirmed) {
-                    return;
-                }
-
-                // Submitting a large number of redactions freezes the UI,
-                // so first yield to allow to rerender after closing the dialog.
-                await Promise.resolve();
-
-                console.info(`Started redacting recent ${count} messages for ${user} in ${roomId}`);
-                await Promise.all(eventsToRedact.map(async event => {
-                    try {
-                        await cli.redactEvent(roomId, event.getId());
-                    } catch (err) {
-                        // log and swallow errors
-                        console.error("Could not redact", event.getId());
-                        console.error(err);
-                    }
-                }));
-                console.info(`Finished redacting recent ${count} messages for ${user} in ${roomId}`);
-            }
-        };
-
         redactButton = (
-            <AccessibleButton className="mx_UserInfo_field" onClick={onRedactAllMessages}>
-                { _t("Remove recent messages") }
-            </AccessibleButton>
+            <RedactMessagesButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />
         );
     }
     if (canAffectUser && me.powerLevel >= powerLevels.ban) {
-        const onBanOrUnban = async () => {
-            const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
-            const {finished} = Modal.createTrackedDialog(
-                'Confirm User Action Dialog',
-                'onBanOrUnban',
-                ConfirmUserActionDialog,
-                {
-                    member,
-                    action: membership === 'ban' ? _t("Unban") : _t("Ban"),
-                    title: membership === 'ban' ? _t("Unban this user?") : _t("Ban this user?"),
-                    askReason: membership !== 'ban',
-                    danger: membership !== 'ban',
-                },
-            );
-
-            const [proceed, reason] = await finished;
-            if (!proceed) return;
-
-            startUpdating();
-            let promise;
-            if (membership === 'ban') {
-                promise = cli.unban(member.roomId, member.userId);
-            } else {
-                promise = cli.ban(member.roomId, member.userId, reason || undefined);
-            }
-            promise.then(() => {
-                // NO-OP; rely on the m.room.member event coming down else we could
-                // get out of sync if we force setState here!
-                console.log("Ban success");
-            }, function(err) {
-                const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                console.error("Ban error: " + err);
-                Modal.createTrackedDialog('Failed to ban user', '', ErrorDialog, {
-                    title: _t("Error"),
-                    description: _t("Failed to ban user"),
-                });
-            }).finally(() => {
-                stopUpdating();
-            });
-        };
-
-        let label = _t("Ban");
-        if (membership === 'ban') {
-            label = _t("Unban");
-        }
-        banButton = (
-            <AccessibleButton className="mx_UserInfo_field" onClick={onBanOrUnban}>
-                { label }
-            </AccessibleButton>
-        );
+        banButton = <BanToggleButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
     }
     if (canAffectUser && me.powerLevel >= editPowerLevel) {
-        const isMuted = _isMuted(member, powerLevels);
-        const onMuteToggle = async () => {
-            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-            const roomId = member.roomId;
-            const target = member.userId;
-
-            // if muting self, warn as it may be irreversible
-            if (target === cli.getUserId()) {
-                try {
-                    if (!(await _warnSelfDemote())) return;
-                } catch (e) {
-                    console.error("Failed to warn about self demotion: ", e);
-                    return;
-                }
-            }
-
-            const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
-            if (!powerLevelEvent) return;
-
-            const powerLevels = powerLevelEvent.getContent();
-            const levelToSend = (
-                (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
-                powerLevels.events_default
-            );
-            let level;
-            if (isMuted) { // unmute
-                level = levelToSend;
-            } else { // mute
-                level = levelToSend - 1;
-            }
-            level = parseInt(level);
-
-            if (!isNaN(level)) {
-                startUpdating();
-                cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
-                    // NO-OP; rely on the m.room.member event coming down else we could
-                    // get out of sync if we force setState here!
-                    console.log("Mute toggle success");
-                }, function(err) {
-                    console.error("Mute error: " + err);
-                    Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
-                        title: _t("Error"),
-                        description: _t("Failed to mute user"),
-                    });
-                }).finally(() => {
-                    stopUpdating();
-                });
-            }
-        };
-
-        const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
         muteButton = (
-            <AccessibleButton className="mx_UserInfo_field"
-                              onClick={onMuteToggle}>
-                { muteLabel }
-            </AccessibleButton>
+            <MuteToggleButton
+                member={member}
+                room={room}
+                powerLevels={powerLevels}
+                startUpdating={startUpdating}
+                stopUpdating={stopUpdating}
+            />
         );
     }
 

From 93429d7c2ee515872fc18c653c8cdae0636511cc Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 17 Oct 2019 19:13:37 +0100
Subject: [PATCH 2/3] Break withLegacyMatrixClient into a util module

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
 src/components/views/right_panel/UserInfo.js | 35 ++++++++------------
 src/utils/withLegacyMatrixClient.js          | 31 +++++++++++++++++
 2 files changed, 44 insertions(+), 22 deletions(-)
 create mode 100644 src/utils/withLegacyMatrixClient.js

diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js
index 292cf87f47..e5ac7d4665 100644
--- a/src/components/views/right_panel/UserInfo.js
+++ b/src/components/views/right_panel/UserInfo.js
@@ -21,7 +21,7 @@ import React, {useCallback, useMemo, useState, useEffect} from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import useEventListener from '@use-it/event-listener';
-import {Group, MatrixClient, RoomMember, User} from 'matrix-js-sdk';
+import {Group, RoomMember, User} from 'matrix-js-sdk';
 import dis from '../../../dispatcher';
 import Modal from '../../../Modal';
 import sdk from '../../../index';
@@ -39,6 +39,7 @@ import MultiInviter from "../../../utils/MultiInviter";
 import GroupStore from "../../../stores/GroupStore";
 import MatrixClientPeg from "../../../MatrixClientPeg";
 import E2EIcon from "../rooms/E2EIcon";
+import withLegacyMatrixClient from "../../../utils/withLegacyMatrixClient";
 
 const _disambiguateDevices = (devices) => {
     const names = Object.create(null);
@@ -57,22 +58,12 @@ const _disambiguateDevices = (devices) => {
     }
 };
 
-const withLegacyMatrixClient = (Component) => class extends React.PureComponent {
-    static contextTypes = {
-        matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-    };
-
-    render() {
-        return <Component {...this.props} cli={this.context.matrixClient} />;
-    }
-};
-
 const _getE2EStatus = (devices) => {
     const hasUnverifiedDevice = devices.some((device) => device.isUnverified());
     return hasUnverifiedDevice ? "warning" : "verified";
 };
 
-const DevicesSection = withLegacyMatrixClient(({devices, userId, loading}) => {
+const DevicesSection = ({devices, userId, loading}) => {
     const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
     const Spinner = sdk.getComponent("elements.Spinner");
 
@@ -95,7 +86,7 @@ const DevicesSection = withLegacyMatrixClient(({devices, userId, loading}) => {
             </div>
         </div>
     );
-});
+};
 
 const onRoomTileClick = (roomId) => {
     dis.dispatch({
@@ -104,7 +95,7 @@ const onRoomTileClick = (roomId) => {
     });
 };
 
-const DirectChatsSection = withLegacyMatrixClient(({cli, userId, startUpdating, stopUpdating}) => {
+const DirectChatsSection = withLegacyMatrixClient(({matrixClient: cli, userId, startUpdating, stopUpdating}) => {
     const onNewDMClick = async () => {
         startUpdating();
         await createRoom({dmUserId: userId});
@@ -195,7 +186,7 @@ const DirectChatsSection = withLegacyMatrixClient(({cli, userId, startUpdating,
     );
 });
 
-const UserOptionsSection = withLegacyMatrixClient(({cli, member, isIgnored, canInvite}) => {
+const UserOptionsSection = withLegacyMatrixClient(({matrixClient: cli, member, isIgnored, canInvite}) => {
     let ignoreButton = null;
     let insertPillButton = null;
     let inviteUserButton = null;
@@ -374,7 +365,7 @@ const useRoomPowerLevels = (room) => {
     return powerLevels;
 };
 
-const RoomKickButton = withLegacyMatrixClient(({cli, member, startUpdating, stopUpdating}) => {
+const RoomKickButton = withLegacyMatrixClient(({matrixClient: cli, member, startUpdating, stopUpdating}) => {
     const onKick = async () => {
         const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
         const {finished} = Modal.createTrackedDialog(
@@ -416,7 +407,7 @@ const RoomKickButton = withLegacyMatrixClient(({cli, member, startUpdating, stop
     </AccessibleButton>;
 });
 
-const RedactMessagesButton = withLegacyMatrixClient(({cli, member}) => {
+const RedactMessagesButton = withLegacyMatrixClient(({matrixClient: cli, member}) => {
     const onRedactAllMessages = async () => {
         const {roomId, userId} = member;
         const room = cli.getRoom(roomId);
@@ -489,7 +480,7 @@ const RedactMessagesButton = withLegacyMatrixClient(({cli, member}) => {
     </AccessibleButton>;
 });
 
-const BanToggleButton = withLegacyMatrixClient(({cli, member, startUpdating, stopUpdating}) => {
+const BanToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, startUpdating, stopUpdating}) => {
     const onBanOrUnban = async () => {
         const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
         const {finished} = Modal.createTrackedDialog(
@@ -541,7 +532,7 @@ const BanToggleButton = withLegacyMatrixClient(({cli, member, startUpdating, sto
     </AccessibleButton>;
 });
 
-const MuteToggleButton = withLegacyMatrixClient(({cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
+const MuteToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
     const isMuted = _isMuted(member, powerLevels);
     const onMuteToggle = async () => {
         const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -598,7 +589,7 @@ const MuteToggleButton = withLegacyMatrixClient(({cli, member, room, powerLevels
     </AccessibleButton>;
 });
 
-const RoomAdminToolsContainer = withLegacyMatrixClient(({cli, room, children, member, startUpdating, stopUpdating}) => {
+const RoomAdminToolsContainer = withLegacyMatrixClient(({matrixClient: cli, room, children, member, startUpdating, stopUpdating}) => {
     let kickButton;
     let banButton;
     let muteButton;
@@ -651,7 +642,7 @@ const RoomAdminToolsContainer = withLegacyMatrixClient(({cli, room, children, me
 });
 
 const GroupAdminToolsSection = withLegacyMatrixClient(
-    ({cli, children, groupId, groupMember, startUpdating, stopUpdating}) => {
+    ({matrixClient: cli, children, groupId, groupMember, startUpdating, stopUpdating}) => {
         const [isPrivileged, setIsPrivileged] = useState(false);
         const [isInvited, setIsInvited] = useState(false);
 
@@ -753,7 +744,7 @@ const useIsSynapseAdmin = (cli) => {
 };
 
 // cli is injected by withLegacyMatrixClient
-const UserInfo = withLegacyMatrixClient(({cli, user, groupId, roomId, onClose}) => {
+const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, roomId, onClose}) => {
     // Load room if we are given a room id and memoize it
     const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]);
 
diff --git a/src/utils/withLegacyMatrixClient.js b/src/utils/withLegacyMatrixClient.js
new file mode 100644
index 0000000000..af6a930a88
--- /dev/null
+++ b/src/utils/withLegacyMatrixClient.js
@@ -0,0 +1,31 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+import PropTypes from "prop-types";
+import {MatrixClient} from "matrix-js-sdk";
+
+// Higher Order Component to allow use of legacy MatrixClient React Context
+// in Functional Components which do not otherwise support legacy React Contexts
+export default (Component) => class extends React.PureComponent {
+    static contextTypes = {
+        matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+    };
+
+    render() {
+        return <Component {...this.props} matrixClient={this.context.matrixClient} />;
+    }
+};

From 7e4d429fa3b7918a0f40fd9e3379ebb9074315a4 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 17 Oct 2019 19:18:14 +0100
Subject: [PATCH 3/3] delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
 src/components/views/right_panel/UserInfo.js | 202 ++++++++++---------
 1 file changed, 103 insertions(+), 99 deletions(-)

diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js
index e5ac7d4665..3abe97dd75 100644
--- a/src/components/views/right_panel/UserInfo.js
+++ b/src/components/views/right_panel/UserInfo.js
@@ -532,114 +532,118 @@ const BanToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, star
     </AccessibleButton>;
 });
 
-const MuteToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
-    const isMuted = _isMuted(member, powerLevels);
-    const onMuteToggle = async () => {
-        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-        const roomId = member.roomId;
-        const target = member.userId;
+const MuteToggleButton = withLegacyMatrixClient(
+    ({matrixClient: cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
+        const isMuted = _isMuted(member, powerLevels);
+        const onMuteToggle = async () => {
+            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            const roomId = member.roomId;
+            const target = member.userId;
 
-        // if muting self, warn as it may be irreversible
-        if (target === cli.getUserId()) {
-            try {
-                if (!(await _warnSelfDemote())) return;
-            } catch (e) {
-                console.error("Failed to warn about self demotion: ", e);
-                return;
+            // if muting self, warn as it may be irreversible
+            if (target === cli.getUserId()) {
+                try {
+                    if (!(await _warnSelfDemote())) return;
+                } catch (e) {
+                    console.error("Failed to warn about self demotion: ", e);
+                    return;
+                }
             }
-        }
 
-        const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
-        if (!powerLevelEvent) return;
+            const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
+            if (!powerLevelEvent) return;
 
-        const powerLevels = powerLevelEvent.getContent();
-        const levelToSend = (
-            (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
-            powerLevels.events_default
-        );
-        let level;
-        if (isMuted) { // unmute
-            level = levelToSend;
-        } else { // mute
-            level = levelToSend - 1;
-        }
-        level = parseInt(level);
+            const powerLevels = powerLevelEvent.getContent();
+            const levelToSend = (
+                (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
+                powerLevels.events_default
+            );
+            let level;
+            if (isMuted) { // unmute
+                level = levelToSend;
+            } else { // mute
+                level = levelToSend - 1;
+            }
+            level = parseInt(level);
 
-        if (!isNaN(level)) {
-            startUpdating();
-            cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
-                // NO-OP; rely on the m.room.member event coming down else we could
-                // get out of sync if we force setState here!
-                console.log("Mute toggle success");
-            }, function(err) {
-                console.error("Mute error: " + err);
-                Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
-                    title: _t("Error"),
-                    description: _t("Failed to mute user"),
+            if (!isNaN(level)) {
+                startUpdating();
+                cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
+                    // NO-OP; rely on the m.room.member event coming down else we could
+                    // get out of sync if we force setState here!
+                    console.log("Mute toggle success");
+                }, function(err) {
+                    console.error("Mute error: " + err);
+                    Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
+                        title: _t("Error"),
+                        description: _t("Failed to mute user"),
+                    });
+                }).finally(() => {
+                    stopUpdating();
                 });
-            }).finally(() => {
-                stopUpdating();
-            });
+            }
+        };
+
+        const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
+        return <AccessibleButton className="mx_UserInfo_field" onClick={onMuteToggle}>
+            { muteLabel }
+        </AccessibleButton>;
+    },
+);
+
+const RoomAdminToolsContainer = withLegacyMatrixClient(
+    ({matrixClient: cli, room, children, member, startUpdating, stopUpdating}) => {
+        let kickButton;
+        let banButton;
+        let muteButton;
+        let redactButton;
+
+        const powerLevels = useRoomPowerLevels(room);
+        const editPowerLevel = (
+            (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
+            powerLevels.state_default
+        );
+
+        const me = room.getMember(cli.getUserId());
+        const isMe = me.userId === member.userId;
+        const canAffectUser = member.powerLevel < me.powerLevel || isMe;
+
+        if (canAffectUser && me.powerLevel >= powerLevels.kick) {
+            kickButton = <RoomKickButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
+        }
+        if (me.powerLevel >= powerLevels.redact) {
+            redactButton = (
+                <RedactMessagesButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />
+            );
+        }
+        if (canAffectUser && me.powerLevel >= powerLevels.ban) {
+            banButton = <BanToggleButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
+        }
+        if (canAffectUser && me.powerLevel >= editPowerLevel) {
+            muteButton = (
+                <MuteToggleButton
+                    member={member}
+                    room={room}
+                    powerLevels={powerLevels}
+                    startUpdating={startUpdating}
+                    stopUpdating={stopUpdating}
+                />
+            );
         }
-    };
 
-    const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
-    return <AccessibleButton className="mx_UserInfo_field" onClick={onMuteToggle}>
-        { muteLabel }
-    </AccessibleButton>;
-});
+        if (kickButton || banButton || muteButton || redactButton || children) {
+            return <GenericAdminToolsContainer>
+                { muteButton }
+                { kickButton }
+                { banButton }
+                { redactButton }
+                { children }
+            </GenericAdminToolsContainer>;
+        }
 
-const RoomAdminToolsContainer = withLegacyMatrixClient(({matrixClient: cli, room, children, member, startUpdating, stopUpdating}) => {
-    let kickButton;
-    let banButton;
-    let muteButton;
-    let redactButton;
-
-    const powerLevels = useRoomPowerLevels(room);
-    const editPowerLevel = (
-        (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
-        powerLevels.state_default
-    );
-
-    const me = room.getMember(cli.getUserId());
-    const isMe = me.userId === member.userId;
-    const canAffectUser = member.powerLevel < me.powerLevel || isMe;
-
-    if (canAffectUser && me.powerLevel >= powerLevels.kick) {
-        kickButton = <RoomKickButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
-    }
-    if (me.powerLevel >= powerLevels.redact) {
-        redactButton = (
-            <RedactMessagesButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />
-        );
-    }
-    if (canAffectUser && me.powerLevel >= powerLevels.ban) {
-        banButton = <BanToggleButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
-    }
-    if (canAffectUser && me.powerLevel >= editPowerLevel) {
-        muteButton = (
-            <MuteToggleButton
-                member={member}
-                room={room}
-                powerLevels={powerLevels}
-                startUpdating={startUpdating}
-                stopUpdating={stopUpdating}
-            />
-        );
-    }
-
-    if (kickButton || banButton || muteButton || redactButton || children) {
-        return <GenericAdminToolsContainer>
-            { muteButton }
-            { kickButton }
-            { banButton }
-            { redactButton }
-            { children }
-        </GenericAdminToolsContainer>;
-    }
-
-    return <div />;
-});
+        return <div />;
+    },
+);
 
 const GroupAdminToolsSection = withLegacyMatrixClient(
     ({matrixClient: cli, children, groupId, groupMember, startUpdating, stopUpdating}) => {