From 0b0fe92b1767771b1666c0c4e6fdd5a58b534be0 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Tue, 17 Dec 2019 12:51:09 -0700
Subject: [PATCH 1/4] Convert resizer to ES6

---
 src/resizer/index.js | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/src/resizer/index.js b/src/resizer/index.js
index bc4c8f388c..7c4b2bd493 100644
--- a/src/resizer/index.js
+++ b/src/resizer/index.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,14 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import FixedDistributor from "./distributors/fixed";
-import CollapseDistributor from "./distributors/collapse";
-import RoomSubListDistributor from "./distributors/roomsublist";
-import Resizer from "./resizer";
-
-module.exports = {
-    Resizer,
-    FixedDistributor,
-    CollapseDistributor,
-    RoomSubListDistributor,
-};
+export FixedDistributor from "./distributors/fixed";
+export CollapseDistributor from "./distributors/collapse";
+export RoomSubListDistributor from "./distributors/roomsublist";
+export Resizer from "./resizer";

From 344dac4fb984f42e9881f5d6267e1448fe3e319e Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Thu, 19 Dec 2019 17:45:24 -0700
Subject: [PATCH 2/4] Convert CommonJS exports to ES6-compatible exports

We use `export default` begrudgingly here. Ideally we'd use just `export`, though this entire SDK expects things to be exported as a default. Instead of breaking everything, we'll sacrifice our export pattern for a smaller diff - a later commit can always do the default export -> regular export conversion.
---
 src/Analytics.js                              |   2 +-
 src/Avatar.js                                 | 204 +++++++++---------
 src/CallHandler.js                            |   2 +-
 src/Entities.js                               |  59 +++--
 src/ImageUtils.js                             |  71 +++---
 src/Notifier.js                               |   2 +-
 src/PasswordReset.js                          |   3 +-
 src/PlatformPeg.js                            |   2 +-
 src/Presence.js                               |   3 +-
 src/RoomListSorter.js                         |   6 +-
 src/ScalarMessaging.js                        |  49 +++--
 src/Skinner.js                                |   2 +-
 src/TextForEvent.js                           |  12 +-
 src/Unread.js                                 | 140 ++++++------
 src/WhoIsTyping.js                            | 108 +++++-----
 .../views/dialogs/EncryptedEventDialog.js     |   2 +-
 .../structures/CompatibilityPage.js           |   3 +-
 src/components/structures/FilePanel.js        |   3 +-
 src/components/structures/LeftPanel.js        |   2 +-
 .../structures/NotificationPanel.js           |   3 +-
 src/components/structures/RoomDirectory.js    |   2 +-
 src/components/structures/RoomStatusBar.js    |   3 +-
 src/components/structures/RoomView.js         |   2 +-
 src/components/structures/ScrollPanel.js      |   2 +-
 src/components/structures/SearchBox.js        |   2 +-
 src/components/structures/TimelinePanel.js    |   2 +-
 src/components/structures/UploadBar.js        |   3 +-
 src/components/structures/ViewSource.js       |   3 +-
 .../structures/auth/ForgotPassword.js         |   2 +-
 src/components/structures/auth/Login.js       |   2 +-
 .../structures/auth/PostRegistration.js       |   2 +-
 .../structures/auth/Registration.js           |   2 +-
 src/components/views/auth/AuthFooter.js       |   3 +-
 src/components/views/auth/AuthHeader.js       |   2 +-
 src/components/views/auth/CaptchaForm.js      |   2 +-
 .../views/auth/CustomServerDialog.js          |   2 +-
 src/components/views/auth/RegistrationForm.js |   2 +-
 src/components/views/avatars/BaseAvatar.js    |   3 +-
 src/components/views/avatars/MemberAvatar.js  |   3 +-
 src/components/views/avatars/RoomAvatar.js    |   2 +-
 .../views/context_menus/MessageContextMenu.js |   2 +-
 .../context_menus/RoomTileContextMenu.js      |   2 +-
 .../views/create_room/CreateRoomButton.js     |   3 +-
 src/components/views/create_room/Presets.js   |   3 +-
 src/components/views/create_room/RoomAlias.js |   3 +-
 .../views/dialogs/AddressPickerDialog.js      |   2 +-
 .../views/elements/DialogButtons.js           |   3 +-
 src/components/views/elements/EditableText.js |   2 +-
 .../views/elements/InlineSpinner.js           |   2 +-
 .../views/elements/MemberEventListSummary.js  |   2 +-
 .../views/elements/MessageSpinner.js          |   2 +-
 .../views/elements/PersistentApp.js           |   3 +-
 .../views/elements/PowerSelector.js           |   2 +-
 src/components/views/elements/ProgressBar.js  |   3 +-
 src/components/views/elements/SettingsFlag.js |   3 +-
 src/components/views/elements/Spinner.js      |   3 +-
 src/components/views/elements/TintableSvg.js  |   3 +-
 src/components/views/elements/Tooltip.js      |   3 +-
 .../views/elements/TooltipButton.js           |   2 +-
 .../views/elements/TruncatedList.js           |   2 +-
 src/components/views/elements/UserSelector.js |   3 +-
 src/components/views/globals/MatrixToolbar.js |   2 +-
 .../views/groups/GroupMemberInfo.js           |   3 +-
 src/components/views/groups/GroupRoomInfo.js  |   3 +-
 src/components/views/messages/MFileBody.js    |   2 +-
 src/components/views/messages/MVideoBody.js   |   3 +-
 src/components/views/messages/MessageEvent.js |   2 +-
 .../views/messages/RoomAvatarEvent.js         |   3 +-
 src/components/views/messages/RoomCreate.js   |   3 +-
 src/components/views/messages/TextualBody.js  |   2 +-
 src/components/views/messages/TextualEvent.js |   3 +-
 src/components/views/messages/UnknownBody.js  |   2 +-
 .../views/room_settings/ColorSettings.js      |   2 +-
 .../views/room_settings/UrlPreviewSettings.js |   5 +-
 src/components/views/rooms/AppsDrawer.js      |   2 +-
 src/components/views/rooms/AuxPanel.js        |   2 +-
 src/components/views/rooms/EventTile.js       |   2 +-
 src/components/views/rooms/ForwardMessage.js  |   2 +-
 .../views/rooms/LinkPreviewWidget.js          |   3 +-
 src/components/views/rooms/MemberInfo.js      |   2 +-
 src/components/views/rooms/MemberList.js      |   2 +-
 src/components/views/rooms/MemberTile.js      |   3 +-
 src/components/views/rooms/PinnedEventTile.js |   2 +-
 .../views/rooms/PinnedEventsPanel.js          |   3 +-
 src/components/views/rooms/PresenceLabel.js   |   2 +-
 .../views/rooms/ReadReceiptMarker.js          |   3 +-
 src/components/views/rooms/RoomDropTarget.js  |   3 +-
 src/components/views/rooms/RoomHeader.js      |   3 +-
 src/components/views/rooms/RoomList.js        |   2 +-
 src/components/views/rooms/RoomNameEditor.js  |   2 +-
 src/components/views/rooms/RoomPreviewBar.js  |   2 +-
 src/components/views/rooms/RoomTile.js        |   2 +-
 src/components/views/rooms/RoomTopicEditor.js |   2 +-
 .../views/rooms/RoomUpgradeWarningBar.js      |   2 +-
 src/components/views/rooms/SearchBar.js       |   2 +-
 .../views/rooms/SearchResultTile.js           |   3 +-
 .../views/rooms/SearchableEntityList.js       |   3 +-
 .../views/rooms/TopUnreadMessagesBar.js       |   2 +-
 src/components/views/rooms/UserTile.js        |   3 +-
 src/components/views/rooms/WhoIsTypingTile.js |   2 +-
 src/components/views/settings/ChangeAvatar.js |   2 +-
 .../views/settings/ChangeDisplayName.js       |   3 +-
 .../views/settings/ChangePassword.js          |   2 +-
 .../settings/EnableNotificationsButton.js     |   2 +-
 .../views/settings/Notifications.js           |   2 +-
 src/components/views/voip/CallPreview.js      |   3 +-
 src/components/views/voip/CallView.js         |   3 +-
 src/components/views/voip/IncomingCallBox.js  |   3 +-
 src/components/views/voip/VideoFeed.js        |   3 +-
 src/components/views/voip/VideoView.js        |   3 +-
 src/createRoom.js                             |   4 +-
 src/dispatcher.js                             |   2 +-
 src/email.js                                  |   8 +-
 src/extend.js                                 |   4 +-
 src/indexing/EventIndexPeg.js                 |   2 +-
 src/notifications/ContentRules.js             |  15 +-
 src/notifications/NotificationUtils.js        |  13 +-
 src/notifications/PushRuleVectorState.js      |  55 +++--
 src/notifications/StandardActions.js          |  21 +-
 .../VectorPushRulesDefinitions.js             |  14 +-
 src/notifications/index.js                    |  11 +-
 src/rageshake/rageshake.js                    | 136 ++++++------
 src/rageshake/submit-rageshake.js             |   2 +-
 src/stores/GroupStore.js                      |   2 +-
 src/stores/LifecycleStore.js                  |   3 +-
 src/stores/MessageComposerStore.js            |   3 +-
 src/stores/RoomViewStore.js                   |   3 +-
 src/stores/SessionStore.js                    |   3 +-
 src/stores/WidgetEchoStore.js                 |   3 +-
 test/components/stub-component.js             |   2 +-
 test/end-to-end-tests/src/logbuffer.js        |   3 +-
 test/end-to-end-tests/src/logger.js           |   3 +-
 test/end-to-end-tests/src/rest/creator.js     |   3 +-
 test/end-to-end-tests/src/rest/multi.js       |   3 +-
 test/end-to-end-tests/src/rest/room.js        |   3 +-
 test/end-to-end-tests/src/rest/session.js     |   2 +-
 .../src/scenarios/directory.js                |   3 +-
 .../src/scenarios/e2e-encryption.js           |   3 +-
 .../src/scenarios/lazy-loading.js             |   3 +-
 test/end-to-end-tests/src/session.js          |   3 +-
 .../src/usecases/accept-invite.js             |   3 +-
 .../src/usecases/create-room.js               |   7 +-
 test/end-to-end-tests/src/usecases/dialog.js  |  13 +-
 test/end-to-end-tests/src/usecases/invite.js  |   3 +-
 test/end-to-end-tests/src/usecases/join.js    |   3 +-
 .../src/usecases/room-settings.js             |   3 +-
 test/end-to-end-tests/src/usecases/signup.js  |   3 +-
 147 files changed, 649 insertions(+), 620 deletions(-)

diff --git a/src/Analytics.js b/src/Analytics.js
index 3e208ad6bd..3d28a236b0 100644
--- a/src/Analytics.js
+++ b/src/Analytics.js
@@ -306,4 +306,4 @@ class Analytics {
 if (!global.mxAnalytics) {
     global.mxAnalytics = new Analytics();
 }
-module.exports = global.mxAnalytics;
+export default global.mxAnalytics;
diff --git a/src/Avatar.js b/src/Avatar.js
index 17860698cb..3377849eca 100644
--- a/src/Avatar.js
+++ b/src/Avatar.js
@@ -19,116 +19,114 @@ import {ContentRepo} from 'matrix-js-sdk';
 import MatrixClientPeg from './MatrixClientPeg';
 import DMRoomMap from './utils/DMRoomMap';
 
-module.exports = {
-    avatarUrlForMember: function(member, width, height, resizeMethod) {
-        let url = member.getAvatarUrl(
-            MatrixClientPeg.get().getHomeserverUrl(),
-            Math.floor(width * window.devicePixelRatio),
-            Math.floor(height * window.devicePixelRatio),
-            resizeMethod,
-            false,
-            false,
-        );
-        if (!url) {
-            // member can be null here currently since on invites, the JS SDK
-            // does not have enough info to build a RoomMember object for
-            // the inviter.
-            url = this.defaultAvatarUrlForString(member ? member.userId : '');
+export function avatarUrlForMember(member, width, height, resizeMethod) {
+    let url = member.getAvatarUrl(
+        MatrixClientPeg.get().getHomeserverUrl(),
+        Math.floor(width * window.devicePixelRatio),
+        Math.floor(height * window.devicePixelRatio),
+        resizeMethod,
+        false,
+        false,
+    );
+    if (!url) {
+        // member can be null here currently since on invites, the JS SDK
+        // does not have enough info to build a RoomMember object for
+        // the inviter.
+        url = this.defaultAvatarUrlForString(member ? member.userId : '');
+    }
+    return url;
+}
+
+export function avatarUrlForUser(user, width, height, resizeMethod) {
+    const url = ContentRepo.getHttpUriForMxc(
+        MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl,
+        Math.floor(width * window.devicePixelRatio),
+        Math.floor(height * window.devicePixelRatio),
+        resizeMethod,
+    );
+    if (!url || url.length === 0) {
+        return null;
+    }
+    return url;
+}
+
+export function defaultAvatarUrlForString(s) {
+    const images = ['03b381', '368bd6', 'ac3ba8'];
+    let total = 0;
+    for (let i = 0; i < s.length; ++i) {
+        total += s.charCodeAt(i);
+    }
+    return require('../res/img/' + images[total % images.length] + '.png');
+}
+
+/**
+ * returns the first (non-sigil) character of 'name',
+ * converted to uppercase
+ * @param {string} name
+ * @return {string} the first letter
+ */
+export function getInitialLetter(name) {
+    if (!name) {
+        // XXX: We should find out what causes the name to sometimes be falsy.
+        console.trace("`name` argument to `getInitialLetter` not supplied");
+        return undefined;
+    }
+    if (name.length < 1) {
+        return undefined;
+    }
+
+    let idx = 0;
+    const initial = name[0];
+    if ((initial === '@' || initial === '#' || initial === '+') && name[1]) {
+        idx++;
+    }
+
+    // string.codePointAt(0) would do this, but that isn't supported by
+    // some browsers (notably PhantomJS).
+    let chars = 1;
+    const first = name.charCodeAt(idx);
+
+    // check if it’s the start of a surrogate pair
+    if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
+        const second = name.charCodeAt(idx+1);
+        if (second >= 0xDC00 && second <= 0xDFFF) {
+            chars++;
         }
-        return url;
-    },
+    }
 
-    avatarUrlForUser: function(user, width, height, resizeMethod) {
-        const url = ContentRepo.getHttpUriForMxc(
-            MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl,
-            Math.floor(width * window.devicePixelRatio),
-            Math.floor(height * window.devicePixelRatio),
-            resizeMethod,
-        );
-        if (!url || url.length === 0) {
-            return null;
-        }
-        return url;
-    },
+    const firstChar = name.substring(idx, idx+chars);
+    return firstChar.toUpperCase();
+}
 
-    defaultAvatarUrlForString: function(s) {
-        const images = ['03b381', '368bd6', 'ac3ba8'];
-        let total = 0;
-        for (let i = 0; i < s.length; ++i) {
-            total += s.charCodeAt(i);
-        }
-        return require('../res/img/' + images[total % images.length] + '.png');
-    },
+export function avatarUrlForRoom(room, width, height, resizeMethod) {
+    const explicitRoomAvatar = room.getAvatarUrl(
+        MatrixClientPeg.get().getHomeserverUrl(),
+        width,
+        height,
+        resizeMethod,
+        false,
+    );
+    if (explicitRoomAvatar) {
+        return explicitRoomAvatar;
+    }
 
-    /**
-     * returns the first (non-sigil) character of 'name',
-     * converted to uppercase
-     * @param {string} name
-     * @return {string} the first letter
-     */
-    getInitialLetter(name) {
-        if (!name) {
-            // XXX: We should find out what causes the name to sometimes be falsy.
-            console.trace("`name` argument to `getInitialLetter` not supplied");
-            return undefined;
-        }
-        if (name.length < 1) {
-            return undefined;
-        }
-
-        let idx = 0;
-        const initial = name[0];
-        if ((initial === '@' || initial === '#' || initial === '+') && name[1]) {
-            idx++;
-        }
-
-        // string.codePointAt(0) would do this, but that isn't supported by
-        // some browsers (notably PhantomJS).
-        let chars = 1;
-        const first = name.charCodeAt(idx);
-
-        // check if it’s the start of a surrogate pair
-        if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
-            const second = name.charCodeAt(idx+1);
-            if (second >= 0xDC00 && second <= 0xDFFF) {
-                chars++;
-            }
-        }
-
-        const firstChar = name.substring(idx, idx+chars);
-        return firstChar.toUpperCase();
-    },
-
-    avatarUrlForRoom(room, width, height, resizeMethod) {
-        const explicitRoomAvatar = room.getAvatarUrl(
+    let otherMember = null;
+    const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
+    if (otherUserId) {
+        otherMember = room.getMember(otherUserId);
+    } else {
+        // if the room is not marked as a 1:1, but only has max 2 members
+        // then still try to show any avatar (pref. other member)
+        otherMember = room.getAvatarFallbackMember();
+    }
+    if (otherMember) {
+        return otherMember.getAvatarUrl(
             MatrixClientPeg.get().getHomeserverUrl(),
             width,
             height,
             resizeMethod,
             false,
         );
-        if (explicitRoomAvatar) {
-            return explicitRoomAvatar;
-        }
-
-        let otherMember = null;
-        const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
-        if (otherUserId) {
-            otherMember = room.getMember(otherUserId);
-        } else {
-            // if the room is not marked as a 1:1, but only has max 2 members
-            // then still try to show any avatar (pref. other member)
-            otherMember = room.getAvatarFallbackMember();
-        }
-        if (otherMember) {
-            return otherMember.getAvatarUrl(
-                MatrixClientPeg.get().getHomeserverUrl(),
-                width,
-                height,
-                resizeMethod,
-                false,
-            );
-        }
-        return null;
-    },
-};
+    }
+    return null;
+}
diff --git a/src/CallHandler.js b/src/CallHandler.js
index ecbf6c2c12..eb5a5c1c8e 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -583,4 +583,4 @@ if (global.mxCallHandler === undefined) {
     global.mxCallHandler = callHandler;
 }
 
-module.exports = global.mxCallHandler;
+export default global.mxCallHandler;
diff --git a/src/Entities.js b/src/Entities.js
index 8be1da0db8..0906bef5de 100644
--- a/src/Entities.js
+++ b/src/Entities.js
@@ -105,36 +105,33 @@ class UserEntity extends Entity {
     }
 }
 
+export function newEntity(jsx, matchFn) {
+    const entity = new Entity();
+    entity.getJsx = function() {
+        return jsx;
+    };
+    entity.matches = matchFn;
+    return entity;
+}
 
-module.exports = {
-    newEntity: function(jsx, matchFn) {
-        const entity = new Entity();
-        entity.getJsx = function() {
-            return jsx;
-        };
-        entity.matches = matchFn;
-        return entity;
-    },
+/**
+ * @param {RoomMember[]} members
+ * @return {Entity[]}
+ */
+export function fromRoomMembers(members) {
+    return members.map(function(m) {
+        return new MemberEntity(m);
+    });
+}
 
-    /**
-     * @param {RoomMember[]} members
-     * @return {Entity[]}
-     */
-    fromRoomMembers: function(members) {
-        return members.map(function(m) {
-            return new MemberEntity(m);
-        });
-    },
-
-    /**
-     * @param {User[]} users
-     * @param {boolean} showInviteButton
-     * @param {Function} inviteFn Called with the user ID.
-     * @return {Entity[]}
-     */
-    fromUsers: function(users, showInviteButton, inviteFn) {
-        return users.map(function(u) {
-            return new UserEntity(u, showInviteButton, inviteFn);
-        });
-    },
-};
+/**
+ * @param {User[]} users
+ * @param {boolean} showInviteButton
+ * @param {Function} inviteFn Called with the user ID.
+ * @return {Entity[]}
+ */
+export function fromUsers(users, showInviteButton, inviteFn) {
+    return users.map(function(u) {
+        return new UserEntity(u, showInviteButton, inviteFn);
+    });
+}
diff --git a/src/ImageUtils.js b/src/ImageUtils.js
index a83d94a633..c0f7b94b81 100644
--- a/src/ImageUtils.js
+++ b/src/ImageUtils.js
@@ -16,41 +16,38 @@ limitations under the License.
 
 'use strict';
 
-module.exports = {
-
-    /**
-     * Returns the actual height that an image of dimensions (fullWidth, fullHeight)
-     * will occupy if resized to fit inside a thumbnail bounding box of size
-     * (thumbWidth, thumbHeight).
-     *
-     * If the aspect ratio of the source image is taller than the aspect ratio of
-     * the thumbnail bounding box, then we return the thumbHeight parameter unchanged.
-     * Otherwise we return the thumbHeight parameter scaled down appropriately to
-     * reflect the actual height the scaled thumbnail occupies.
-     *
-     * This is very useful for calculating how much height a thumbnail will actually
-     * consume in the timeline, when performing scroll offset calcuations
-     * (e.g. scroll locking)
-     */
-    thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
-        if (!fullWidth || !fullHeight) {
-            // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
-            // log this because it's spammy
-            return undefined;
-        }
-        if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
-            // no scaling needs to be applied
-            return fullHeight;
-        }
-        const widthMulti = thumbWidth / fullWidth;
-        const heightMulti = thumbHeight / fullHeight;
-        if (widthMulti < heightMulti) {
-            // width is the dominant dimension so scaling will be fixed on that
-            return Math.floor(widthMulti * fullHeight);
-        } else {
-            // height is the dominant dimension so scaling will be fixed on that
-            return Math.floor(heightMulti * fullHeight);
-        }
-    },
-};
+/**
+ * Returns the actual height that an image of dimensions (fullWidth, fullHeight)
+ * will occupy if resized to fit inside a thumbnail bounding box of size
+ * (thumbWidth, thumbHeight).
+ *
+ * If the aspect ratio of the source image is taller than the aspect ratio of
+ * the thumbnail bounding box, then we return the thumbHeight parameter unchanged.
+ * Otherwise we return the thumbHeight parameter scaled down appropriately to
+ * reflect the actual height the scaled thumbnail occupies.
+ *
+ * This is very useful for calculating how much height a thumbnail will actually
+ * consume in the timeline, when performing scroll offset calcuations
+ * (e.g. scroll locking)
+ */
+export function thumbHeight(fullWidth, fullHeight, thumbWidth, thumbHeight) {
+    if (!fullWidth || !fullHeight) {
+        // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
+        // log this because it's spammy
+        return undefined;
+    }
+    if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
+        // no scaling needs to be applied
+        return fullHeight;
+    }
+    const widthMulti = thumbWidth / fullWidth;
+    const heightMulti = thumbHeight / fullHeight;
+    if (widthMulti < heightMulti) {
+        // width is the dominant dimension so scaling will be fixed on that
+        return Math.floor(widthMulti * fullHeight);
+    } else {
+        // height is the dominant dimension so scaling will be fixed on that
+        return Math.floor(heightMulti * fullHeight);
+    }
+}
 
diff --git a/src/Notifier.js b/src/Notifier.js
index dd691d8ca7..10d2048f45 100644
--- a/src/Notifier.js
+++ b/src/Notifier.js
@@ -364,4 +364,4 @@ if (!global.mxNotifier) {
     global.mxNotifier = Notifier;
 }
 
-module.exports = global.mxNotifier;
+export default global.mxNotifier;
diff --git a/src/PasswordReset.js b/src/PasswordReset.js
index 31339eb4e5..320599f6d9 100644
--- a/src/PasswordReset.js
+++ b/src/PasswordReset.js
@@ -25,7 +25,7 @@ import { _t } from './languageHandler';
  * the client owns the given email address, which is then passed to the password
  * API on the homeserver in question with the new password.
  */
-class PasswordReset {
+export default class PasswordReset {
     /**
      * Configure the endpoints for password resetting.
      * @param {string} homeserverUrl The URL to the HS which has the account to reset.
@@ -101,4 +101,3 @@ class PasswordReset {
     }
 }
 
-module.exports = PasswordReset;
diff --git a/src/PlatformPeg.js b/src/PlatformPeg.js
index 5c1112e23b..34131fde7d 100644
--- a/src/PlatformPeg.js
+++ b/src/PlatformPeg.js
@@ -47,4 +47,4 @@ class PlatformPeg {
 if (!global.mxPlatformPeg) {
     global.mxPlatformPeg = new PlatformPeg();
 }
-module.exports = global.mxPlatformPeg;
+export default global.mxPlatformPeg;
diff --git a/src/Presence.js b/src/Presence.js
index 8ef988f171..4776157514 100644
--- a/src/Presence.js
+++ b/src/Presence.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2018 New Vector Ltd
+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.
@@ -104,4 +105,4 @@ class Presence {
     }
 }
 
-module.exports = new Presence();
+export default new Presence();
diff --git a/src/RoomListSorter.js b/src/RoomListSorter.js
index c06cc60c97..0ff37a6af2 100644
--- a/src/RoomListSorter.js
+++ b/src/RoomListSorter.js
@@ -24,12 +24,8 @@ function tsOfNewestEvent(room) {
     }
 }
 
-function mostRecentActivityFirst(roomList) {
+export function mostRecentActivityFirst(roomList) {
     return roomList.sort(function(a, b) {
         return tsOfNewestEvent(b) - tsOfNewestEvent(a);
     });
 }
-
-module.exports = {
-    mostRecentActivityFirst,
-};
diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js
index c0ffc3022d..a449c2c2b9 100644
--- a/src/ScalarMessaging.js
+++ b/src/ScalarMessaging.js
@@ -658,30 +658,29 @@ const onMessage = function(event) {
 
 let listenerCount = 0;
 let openManagerUrl = null;
-module.exports = {
-    startListening: function() {
-        if (listenerCount === 0) {
-            window.addEventListener("message", onMessage, false);
-        }
-        listenerCount += 1;
-    },
 
-    stopListening: function() {
-        listenerCount -= 1;
-        if (listenerCount === 0) {
-            window.removeEventListener("message", onMessage);
-        }
-        if (listenerCount < 0) {
-            // Make an error so we get a stack trace
-            const e = new Error(
-                "ScalarMessaging: mismatched startListening / stopListening detected." +
-                " Negative count",
-            );
-            console.error(e);
-        }
-    },
+export function startListening() {
+    if (listenerCount === 0) {
+        window.addEventListener("message", onMessage, false);
+    }
+    listenerCount += 1;
+}
 
-    setOpenManagerUrl: function(url) {
-        openManagerUrl = url;
-    },
-};
+export function stopListening() {
+    listenerCount -= 1;
+    if (listenerCount === 0) {
+        window.removeEventListener("message", onMessage);
+    }
+    if (listenerCount < 0) {
+        // Make an error so we get a stack trace
+        const e = new Error(
+            "ScalarMessaging: mismatched startListening / stopListening detected." +
+            " Negative count",
+        );
+        console.error(e);
+    }
+}
+
+export function setOpenManagerUrl(url) {
+    openManagerUrl = url;
+}
diff --git a/src/Skinner.js b/src/Skinner.js
index 7235d55937..fee234d77e 100644
--- a/src/Skinner.js
+++ b/src/Skinner.js
@@ -106,5 +106,5 @@ class Skinner {
 if (global.mxSkinner === undefined) {
     global.mxSkinner = new Skinner();
 }
-module.exports = global.mxSkinner;
+export default global.mxSkinner;
 
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index c3c8396e26..fe936d7d21 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -620,10 +620,8 @@ for (const evType of ALL_RULE_TYPES) {
     stateHandlers[evType] = textForMjolnirEvent;
 }
 
-module.exports = {
-    textForEvent: function(ev) {
-        const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
-        if (handler) return handler(ev);
-        return '';
-    },
-};
+export function textForEvent(ev) {
+    const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
+    if (handler) return handler(ev);
+    return '';
+}
diff --git a/src/Unread.js b/src/Unread.js
index d5c5993974..c2eaa468f5 100644
--- a/src/Unread.js
+++ b/src/Unread.js
@@ -18,76 +18,74 @@ const MatrixClientPeg = require('./MatrixClientPeg');
 import shouldHideEvent from './shouldHideEvent';
 const sdk = require('./index');
 
-module.exports = {
-    /**
-     * Returns true iff this event arriving in a room should affect the room's
-     * count of unread messages
-     */
-    eventTriggersUnreadCount: function(ev) {
-        if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
-            return false;
-        } else if (ev.getType() == 'm.room.member') {
-            return false;
-        } else if (ev.getType() == 'm.room.third_party_invite') {
-            return false;
-        } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
-            return false;
-        } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
-            return false;
-        } else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') {
-            return false;
-        } else if (ev.getType() == 'm.room.server_acl') {
+/**
+ * Returns true iff this event arriving in a room should affect the room's
+ * count of unread messages
+ */
+export function eventTriggersUnreadCount(ev) {
+    if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
+        return false;
+    } else if (ev.getType() == 'm.room.member') {
+        return false;
+    } else if (ev.getType() == 'm.room.third_party_invite') {
+        return false;
+    } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
+        return false;
+    } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
+        return false;
+    } else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') {
+        return false;
+    } else if (ev.getType() == 'm.room.server_acl') {
+        return false;
+    }
+    const EventTile = sdk.getComponent('rooms.EventTile');
+    return EventTile.haveTileForEvent(ev);
+}
+
+export function doesRoomHaveUnreadMessages(room) {
+    const myUserId = MatrixClientPeg.get().credentials.userId;
+
+    // get the most recent read receipt sent by our account.
+    // N.B. this is NOT a read marker (RM, aka "read up to marker"),
+    // despite the name of the method :((
+    const readUpToId = room.getEventReadUpTo(myUserId);
+
+    // as we don't send RRs for our own messages, make sure we special case that
+    // if *we* sent the last message into the room, we consider it not unread!
+    // Should fix: https://github.com/vector-im/riot-web/issues/3263
+    //             https://github.com/vector-im/riot-web/issues/2427
+    // ...and possibly some of the others at
+    //             https://github.com/vector-im/riot-web/issues/3363
+    if (room.timeline.length &&
+        room.timeline[room.timeline.length - 1].sender &&
+        room.timeline[room.timeline.length - 1].sender.userId === myUserId) {
+        return false;
+    }
+
+    // this just looks at whatever history we have, which if we've only just started
+    // up probably won't be very much, so if the last couple of events are ones that
+    // don't count, we don't know if there are any events that do count between where
+    // we have and the read receipt. We could fetch more history to try & find out,
+    // but currently we just guess.
+
+    // Loop through messages, starting with the most recent...
+    for (let i = room.timeline.length - 1; i >= 0; --i) {
+        const ev = room.timeline[i];
+
+        if (ev.getId() == readUpToId) {
+            // If we've read up to this event, there's nothing more recent
+            // that counts and we can stop looking because the user's read
+            // this and everything before.
             return false;
+        } else if (!shouldHideEvent(ev) && this.eventTriggersUnreadCount(ev)) {
+            // We've found a message that counts before we hit
+            // the user's read receipt, so this room is definitely unread.
+            return true;
         }
-        const EventTile = sdk.getComponent('rooms.EventTile');
-        return EventTile.haveTileForEvent(ev);
-    },
-
-    doesRoomHaveUnreadMessages: function(room) {
-        const myUserId = MatrixClientPeg.get().credentials.userId;
-
-        // get the most recent read receipt sent by our account.
-        // N.B. this is NOT a read marker (RM, aka "read up to marker"),
-        // despite the name of the method :((
-        const readUpToId = room.getEventReadUpTo(myUserId);
-
-        // as we don't send RRs for our own messages, make sure we special case that
-        // if *we* sent the last message into the room, we consider it not unread!
-        // Should fix: https://github.com/vector-im/riot-web/issues/3263
-        //             https://github.com/vector-im/riot-web/issues/2427
-        // ...and possibly some of the others at
-        //             https://github.com/vector-im/riot-web/issues/3363
-        if (room.timeline.length &&
-            room.timeline[room.timeline.length - 1].sender &&
-            room.timeline[room.timeline.length - 1].sender.userId === myUserId) {
-            return false;
-        }
-
-        // this just looks at whatever history we have, which if we've only just started
-        // up probably won't be very much, so if the last couple of events are ones that
-        // don't count, we don't know if there are any events that do count between where
-        // we have and the read receipt. We could fetch more history to try & find out,
-        // but currently we just guess.
-
-        // Loop through messages, starting with the most recent...
-        for (let i = room.timeline.length - 1; i >= 0; --i) {
-            const ev = room.timeline[i];
-
-            if (ev.getId() == readUpToId) {
-                // If we've read up to this event, there's nothing more recent
-                // that counts and we can stop looking because the user's read
-                // this and everything before.
-                return false;
-            } else if (!shouldHideEvent(ev) && this.eventTriggersUnreadCount(ev)) {
-                // We've found a message that counts before we hit
-                // the user's read receipt, so this room is definitely unread.
-                return true;
-            }
-        }
-        // If we got here, we didn't find a message that counted but didn't find
-        // the user's read receipt either, so we guess and say that the room is
-        // unread on the theory that false positives are better than false
-        // negatives here.
-        return true;
-    },
-};
+    }
+    // If we got here, we didn't find a message that counted but didn't find
+    // the user's read receipt either, so we guess and say that the room is
+    // unread on the theory that false positives are better than false
+    // negatives here.
+    return true;
+}
diff --git a/src/WhoIsTyping.js b/src/WhoIsTyping.js
index eb09685cbe..d690466127 100644
--- a/src/WhoIsTyping.js
+++ b/src/WhoIsTyping.js
@@ -17,68 +17,66 @@ limitations under the License.
 import MatrixClientPeg from "./MatrixClientPeg";
 import { _t } from './languageHandler';
 
-module.exports = {
-    usersTypingApartFromMeAndIgnored: function(room) {
-        return this.usersTyping(
-            room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers()),
-        );
-    },
+export function usersTypingApartFromMeAndIgnored(room) {
+    return usersTyping(
+        room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers()),
+    );
+}
 
-    usersTypingApartFromMe: function(room) {
-        return this.usersTyping(
-            room, [MatrixClientPeg.get().credentials.userId],
-        );
-    },
+export function usersTypingApartFromMe(room) {
+    return usersTyping(
+        room, [MatrixClientPeg.get().credentials.userId],
+    );
+}
 
-    /**
-     * Given a Room object and, optionally, a list of userID strings
-     * to exclude, return a list of user objects who are typing.
-     * @param {Room} room: room object to get users from.
-     * @param {string[]} exclude: list of user mxids to exclude.
-     * @returns {string[]} list of user objects who are typing.
-     */
-    usersTyping: function(room, exclude) {
-        const whoIsTyping = [];
+/**
+ * Given a Room object and, optionally, a list of userID strings
+ * to exclude, return a list of user objects who are typing.
+ * @param {Room} room: room object to get users from.
+ * @param {string[]} exclude: list of user mxids to exclude.
+ * @returns {string[]} list of user objects who are typing.
+ */
+export function usersTyping(room, exclude) {
+    const whoIsTyping = [];
 
-        if (exclude === undefined) {
-            exclude = [];
-        }
+    if (exclude === undefined) {
+        exclude = [];
+    }
 
-        const memberKeys = Object.keys(room.currentState.members);
-        for (let i = 0; i < memberKeys.length; ++i) {
-            const userId = memberKeys[i];
+    const memberKeys = Object.keys(room.currentState.members);
+    for (let i = 0; i < memberKeys.length; ++i) {
+        const userId = memberKeys[i];
 
-            if (room.currentState.members[userId].typing) {
-                if (exclude.indexOf(userId) === -1) {
-                    whoIsTyping.push(room.currentState.members[userId]);
-                }
+        if (room.currentState.members[userId].typing) {
+            if (exclude.indexOf(userId) === -1) {
+                whoIsTyping.push(room.currentState.members[userId]);
             }
         }
+    }
 
-        return whoIsTyping;
-    },
+    return whoIsTyping;
+}
 
-    whoIsTypingString: function(whoIsTyping, limit) {
-        let othersCount = 0;
-        if (whoIsTyping.length > limit) {
-            othersCount = whoIsTyping.length - limit + 1;
-        }
-        if (whoIsTyping.length === 0) {
-            return '';
-        } else if (whoIsTyping.length === 1) {
-            return _t('%(displayName)s is typing …', {displayName: whoIsTyping[0].name});
-        }
-        const names = whoIsTyping.map(function(m) {
-            return m.name;
+export function whoIsTypingString(whoIsTyping, limit) {
+    let othersCount = 0;
+    if (whoIsTyping.length > limit) {
+        othersCount = whoIsTyping.length - limit + 1;
+    }
+    if (whoIsTyping.length === 0) {
+        return '';
+    } else if (whoIsTyping.length === 1) {
+        return _t('%(displayName)s is typing …', {displayName: whoIsTyping[0].name});
+    }
+    const names = whoIsTyping.map(function(m) {
+        return m.name;
+    });
+    if (othersCount>=1) {
+        return _t('%(names)s and %(count)s others are typing …', {
+            names: names.slice(0, limit - 1).join(', '),
+            count: othersCount,
         });
-        if (othersCount>=1) {
-            return _t('%(names)s and %(count)s others are typing …', {
-                names: names.slice(0, limit - 1).join(', '),
-                count: othersCount,
-            });
-        } else {
-            const lastPerson = names.pop();
-            return _t('%(names)s and %(lastPerson)s are typing …', {names: names.join(', '), lastPerson: lastPerson});
-        }
-    },
-};
+    } else {
+        const lastPerson = names.pop();
+        return _t('%(names)s and %(lastPerson)s are typing …', {names: names.join(', '), lastPerson: lastPerson});
+    }
+}
diff --git a/src/async-components/views/dialogs/EncryptedEventDialog.js b/src/async-components/views/dialogs/EncryptedEventDialog.js
index 145203136a..b6383c9dab 100644
--- a/src/async-components/views/dialogs/EncryptedEventDialog.js
+++ b/src/async-components/views/dialogs/EncryptedEventDialog.js
@@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler';
 const sdk = require('../../../index');
 const MatrixClientPeg = require("../../../MatrixClientPeg");
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'EncryptedEventDialog',
 
     propTypes: {
diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js
index 28c86f8dd8..9a3fdb5f39 100644
--- a/src/components/structures/CompatibilityPage.js
+++ b/src/components/structures/CompatibilityPage.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+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.
@@ -20,7 +21,7 @@ import createReactClass from 'create-react-class';
 import PropTypes from 'prop-types';
 import { _t } from '../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CompatibilityPage',
     propTypes: {
         onAccept: PropTypes.func,
diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js
index f5a5912dd5..b6fbf6d45d 100644
--- a/src/components/structures/FilePanel.js
+++ b/src/components/structures/FilePanel.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -126,4 +127,4 @@ const FilePanel = createReactClass({
     },
 });
 
-module.exports = FilePanel;
+export default FilePanel;
diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js
index a0ad2b5c81..57cbd6ec59 100644
--- a/src/components/structures/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -308,4 +308,4 @@ const LeftPanel = createReactClass({
     },
 });
 
-module.exports = LeftPanel;
+export default LeftPanel;
diff --git a/src/components/structures/NotificationPanel.js b/src/components/structures/NotificationPanel.js
index 470c7c8728..b65365b7e4 100644
--- a/src/components/structures/NotificationPanel.js
+++ b/src/components/structures/NotificationPanel.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2016 OpenMarket Ltd
 Copyright 2019 New Vector Ltd
+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.
@@ -60,4 +61,4 @@ const NotificationPanel = createReactClass({
     },
 });
 
-module.exports = NotificationPanel;
+export default NotificationPanel;
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index 4823b0976c..16333d51c0 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -38,7 +38,7 @@ function track(action) {
     Analytics.trackEvent('RoomDirectory', action);
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomDirectory',
 
     propTypes: {
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index b0aa4cb59b..e7a0771962 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2017, 2018 New Vector Ltd
+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.
@@ -38,7 +39,7 @@ function getUnsentMessages(room) {
     });
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomStatusBar',
 
     propTypes: {
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 2f8d274866..3f9d680503 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -72,7 +72,7 @@ const RoomContext = PropTypes.shape({
     room: PropTypes.instanceOf(Room),
 });
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomView',
     propTypes: {
         ConferenceHandler: PropTypes.any,
diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js
index 17583a22ed..c5725e1343 100644
--- a/src/components/structures/ScrollPanel.js
+++ b/src/components/structures/ScrollPanel.js
@@ -84,7 +84,7 @@ if (DEBUG_SCROLL) {
  * offset as normal.
  */
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ScrollPanel',
 
     propTypes: {
diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js
index 0aa2e15f4c..faa20a68ba 100644
--- a/src/components/structures/SearchBox.js
+++ b/src/components/structures/SearchBox.js
@@ -24,7 +24,7 @@ import { throttle } from 'lodash';
 import AccessibleButton from '../../components/views/elements/AccessibleButton';
 import classNames from 'classnames';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'SearchBox',
 
     propTypes: {
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 41283b5308..d0f99aec91 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -1347,4 +1347,4 @@ const TimelinePanel = createReactClass({
     },
 });
 
-module.exports = TimelinePanel;
+export default TimelinePanel;
diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js
index da0ca7fe99..ce5441089c 100644
--- a/src/components/structures/UploadBar.js
+++ b/src/components/structures/UploadBar.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -22,7 +23,7 @@ const dis = require('../../dispatcher');
 const filesize = require('filesize');
 import { _t } from '../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'UploadBar',
     propTypes: {
         room: PropTypes.object,
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index ef4ede517a..e946b5afbf 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+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.
@@ -23,7 +24,7 @@ import {_t} from "../../languageHandler";
 import sdk from "../../index";
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ViewSource',
 
     propTypes: {
diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js
index ada7d4449b..6a84913f36 100644
--- a/src/components/structures/auth/ForgotPassword.js
+++ b/src/components/structures/auth/ForgotPassword.js
@@ -40,7 +40,7 @@ const PHASE_EMAIL_SENT = 3;
 // User has clicked the link in email and completed reset
 const PHASE_DONE = 4;
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ForgotPassword',
 
     propTypes: {
diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js
index ade417d156..dc2755be8e 100644
--- a/src/components/structures/auth/Login.js
+++ b/src/components/structures/auth/Login.js
@@ -54,7 +54,7 @@ _td("General failure");
 /**
  * A wire component which glues together login UI components and Login logic
  */
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'Login',
 
     propTypes: {
diff --git a/src/components/structures/auth/PostRegistration.js b/src/components/structures/auth/PostRegistration.js
index a77b4d0d56..128c061cc4 100644
--- a/src/components/structures/auth/PostRegistration.js
+++ b/src/components/structures/auth/PostRegistration.js
@@ -22,7 +22,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
 import { _t } from '../../../languageHandler';
 import AuthPage from "../../views/auth/AuthPage";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PostRegistration',
 
     propTypes: {
diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js
index 69f34f764a..fce77631f8 100644
--- a/src/components/structures/auth/Registration.js
+++ b/src/components/structures/auth/Registration.js
@@ -41,7 +41,7 @@ const PHASE_REGISTRATION = 1;
 // Enable phases for registration
 const PHASES_ENABLED = true;
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'Registration',
 
     propTypes: {
diff --git a/src/components/views/auth/AuthFooter.js b/src/components/views/auth/AuthFooter.js
index 39d636f9cc..4076141606 100644
--- a/src/components/views/auth/AuthFooter.js
+++ b/src/components/views/auth/AuthFooter.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2019 New Vector Ltd
+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.
@@ -19,7 +20,7 @@ import { _t } from '../../../languageHandler';
 import React from 'react';
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'AuthFooter',
 
     render: function() {
diff --git a/src/components/views/auth/AuthHeader.js b/src/components/views/auth/AuthHeader.js
index 193f347857..7048472681 100644
--- a/src/components/views/auth/AuthHeader.js
+++ b/src/components/views/auth/AuthHeader.js
@@ -19,7 +19,7 @@ import React from 'react';
 import createReactClass from 'create-react-class';
 import sdk from '../../../index';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'AuthHeader',
 
     render: function() {
diff --git a/src/components/views/auth/CaptchaForm.js b/src/components/views/auth/CaptchaForm.js
index f907a58026..2da837f029 100644
--- a/src/components/views/auth/CaptchaForm.js
+++ b/src/components/views/auth/CaptchaForm.js
@@ -24,7 +24,7 @@ const DIV_ID = 'mx_recaptcha';
 /**
  * A pure UI component which displays a captcha form.
  */
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CaptchaForm',
 
     propTypes: {
diff --git a/src/components/views/auth/CustomServerDialog.js b/src/components/views/auth/CustomServerDialog.js
index a9a3a53f02..024951e6c0 100644
--- a/src/components/views/auth/CustomServerDialog.js
+++ b/src/components/views/auth/CustomServerDialog.js
@@ -19,7 +19,7 @@ import React from 'react';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CustomServerDialog',
 
     render: function() {
diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js
index 03fb74462c..28efe60d0e 100644
--- a/src/components/views/auth/RegistrationForm.js
+++ b/src/components/views/auth/RegistrationForm.js
@@ -41,7 +41,7 @@ const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from of
 /**
  * A pure UI component which displays a registration form.
  */
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RegistrationForm',
 
     propTypes: {
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js
index 82db78615e..ae9eaba488 100644
--- a/src/components/views/avatars/BaseAvatar.js
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -2,6 +2,7 @@
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2018 New Vector Ltd
 Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+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.
@@ -24,7 +25,7 @@ import AvatarLogic from '../../../Avatar';
 import SettingsStore from "../../../settings/SettingsStore";
 import AccessibleButton from '../elements/AccessibleButton';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'BaseAvatar',
 
     propTypes: {
diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js
index 383bab5e79..4f84191ccd 100644
--- a/src/components/views/avatars/MemberAvatar.js
+++ b/src/components/views/avatars/MemberAvatar.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -21,7 +22,7 @@ const Avatar = require('../../../Avatar');
 const sdk = require("../../../index");
 const dispatcher = require("../../../dispatcher");
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MemberAvatar',
 
     propTypes: {
diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js
index 6f8f236afc..3e02e439ff 100644
--- a/src/components/views/avatars/RoomAvatar.js
+++ b/src/components/views/avatars/RoomAvatar.js
@@ -22,7 +22,7 @@ import Modal from '../../../Modal';
 import sdk from "../../../index";
 import Avatar from '../../../Avatar';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomAvatar',
 
     // Room may be left unset here, but if it is,
diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js
index efbfc4322f..1011b6ce46 100644
--- a/src/components/views/context_menus/MessageContextMenu.js
+++ b/src/components/views/context_menus/MessageContextMenu.js
@@ -37,7 +37,7 @@ function canCancel(eventStatus) {
     return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MessageContextMenu',
 
     propTypes: {
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index f5e68bd20b..902506b015 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -63,7 +63,7 @@ const NotifOption = ({active, onClick, src, label}) => {
     );
 };
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomTileContextMenu',
 
     propTypes: {
diff --git a/src/components/views/create_room/CreateRoomButton.js b/src/components/views/create_room/CreateRoomButton.js
index 1c44aed78c..adf3972eff 100644
--- a/src/components/views/create_room/CreateRoomButton.js
+++ b/src/components/views/create_room/CreateRoomButton.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -19,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CreateRoomButton',
     propTypes: {
         onCreateRoom: PropTypes.func,
diff --git a/src/components/views/create_room/Presets.js b/src/components/views/create_room/Presets.js
index f512c3e2fd..0f18d11511 100644
--- a/src/components/views/create_room/Presets.js
+++ b/src/components/views/create_room/Presets.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -25,7 +26,7 @@ const Presets = {
     Custom: "custom",
 };
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CreateRoomPresets',
     propTypes: {
         onChange: PropTypes.func,
diff --git a/src/components/views/create_room/RoomAlias.js b/src/components/views/create_room/RoomAlias.js
index fd3e3365f7..bc5dec1468 100644
--- a/src/components/views/create_room/RoomAlias.js
+++ b/src/components/views/create_room/RoomAlias.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -19,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomAlias',
     propTypes: {
         // Specifying a homeserver will make magical things happen when you,
diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js
index 2be505a798..b7d8f3f55f 100644
--- a/src/components/views/dialogs/AddressPickerDialog.js
+++ b/src/components/views/dialogs/AddressPickerDialog.js
@@ -43,7 +43,7 @@ const addressTypeName = {
 };
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: "AddressPickerDialog",
 
     propTypes: {
diff --git a/src/components/views/elements/DialogButtons.js b/src/components/views/elements/DialogButtons.js
index e7b3a9c7eb..4e47e73052 100644
--- a/src/components/views/elements/DialogButtons.js
+++ b/src/components/views/elements/DialogButtons.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 Aidan Gauland
 Copyright 2018 New Vector Ltd.
+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.
@@ -23,7 +24,7 @@ import { _t } from '../../../languageHandler';
 /**
  * Basic container for buttons in modal dialogs.
  */
-module.exports = createReactClass({
+export default createReactClass({
     displayName: "DialogButtons",
 
     propTypes: {
diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js
index 5913682255..fbac63cbba 100644
--- a/src/components/views/elements/EditableText.js
+++ b/src/components/views/elements/EditableText.js
@@ -20,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import {Key} from "../../../Keyboard";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'EditableText',
 
     propTypes: {
diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js
index 18711f90d3..ad70471d89 100644
--- a/src/components/views/elements/InlineSpinner.js
+++ b/src/components/views/elements/InlineSpinner.js
@@ -17,7 +17,7 @@ limitations under the License.
 import React from "react";
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'InlineSpinner',
 
     render: function() {
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index ef80efaa68..4db0ad1c68 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -24,7 +24,7 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
 import sdk from "../../../index";
 import {MatrixEvent} from "matrix-js-sdk";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MemberEventListSummary',
 
     propTypes: {
diff --git a/src/components/views/elements/MessageSpinner.js b/src/components/views/elements/MessageSpinner.js
index f00fdcf576..1775fdd4d7 100644
--- a/src/components/views/elements/MessageSpinner.js
+++ b/src/components/views/elements/MessageSpinner.js
@@ -17,7 +17,7 @@ limitations under the License.
 import React from 'react';
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MessageSpinner',
 
     render: function() {
diff --git a/src/components/views/elements/PersistentApp.js b/src/components/views/elements/PersistentApp.js
index 19e4be6083..996dbf2283 100644
--- a/src/components/views/elements/PersistentApp.js
+++ b/src/components/views/elements/PersistentApp.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -22,7 +23,7 @@ import WidgetUtils from '../../../utils/WidgetUtils';
 import sdk from '../../../index';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PersistentApp',
 
     getInitialState: function() {
diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js
index e6babded32..2f4c08922a 100644
--- a/src/components/views/elements/PowerSelector.js
+++ b/src/components/views/elements/PowerSelector.js
@@ -22,7 +22,7 @@ import { _t } from '../../../languageHandler';
 import Field from "./Field";
 import {Key} from "../../../Keyboard";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PowerSelector',
 
     propTypes: {
diff --git a/src/components/views/elements/ProgressBar.js b/src/components/views/elements/ProgressBar.js
index 3561763e51..045731ba38 100644
--- a/src/components/views/elements/ProgressBar.js
+++ b/src/components/views/elements/ProgressBar.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -18,7 +19,7 @@ import React from "react";
 import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ProgressBar',
     propTypes: {
         value: PropTypes.number,
diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.js
index a3a6d18d33..15f17805a8 100644
--- a/src/components/views/elements/SettingsFlag.js
+++ b/src/components/views/elements/SettingsFlag.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017 Travis Ralston
+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.
@@ -21,7 +22,7 @@ import SettingsStore from "../../../settings/SettingsStore";
 import { _t } from '../../../languageHandler';
 import ToggleSwitch from "./ToggleSwitch";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'SettingsFlag',
     propTypes: {
         name: PropTypes.string.isRequired,
diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js
index 5d43e836cc..b1fe97d5d2 100644
--- a/src/components/views/elements/Spinner.js
+++ b/src/components/views/elements/Spinner.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -17,7 +18,7 @@ limitations under the License.
 import React from "react";
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'Spinner',
 
     render: function() {
diff --git a/src/components/views/elements/TintableSvg.js b/src/components/views/elements/TintableSvg.js
index 73ba375d59..3e0e41f411 100644
--- a/src/components/views/elements/TintableSvg.js
+++ b/src/components/views/elements/TintableSvg.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015 OpenMarket Ltd
+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.
@@ -83,4 +84,4 @@ Tinter.registerTintable(function() {
     }
 });
 
-module.exports = TintableSvg;
+export default TintableSvg;
diff --git a/src/components/views/elements/Tooltip.js b/src/components/views/elements/Tooltip.js
index 8ff3ce9bdb..fd845d9db3 100644
--- a/src/components/views/elements/Tooltip.js
+++ b/src/components/views/elements/Tooltip.js
@@ -2,6 +2,7 @@
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2019 New Vector Ltd
 Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+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.
@@ -26,7 +27,7 @@ import classNames from 'classnames';
 
 const MIN_TOOLTIP_HEIGHT = 25;
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'Tooltip',
 
     propTypes: {
diff --git a/src/components/views/elements/TooltipButton.js b/src/components/views/elements/TooltipButton.js
index 0cabf776a4..29a18d4b34 100644
--- a/src/components/views/elements/TooltipButton.js
+++ b/src/components/views/elements/TooltipButton.js
@@ -19,7 +19,7 @@ import React from 'react';
 import createReactClass from 'create-react-class';
 import sdk from '../../../index';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'TooltipButton',
 
     getInitialState: function() {
diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js
index e6a5e2ae32..9ce2395638 100644
--- a/src/components/views/elements/TruncatedList.js
+++ b/src/components/views/elements/TruncatedList.js
@@ -20,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'TruncatedList',
 
     propTypes: {
diff --git a/src/components/views/elements/UserSelector.js b/src/components/views/elements/UserSelector.js
index 1010d4144c..706c6ed2e5 100644
--- a/src/components/views/elements/UserSelector.js
+++ b/src/components/views/elements/UserSelector.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -19,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'UserSelector',
 
     propTypes: {
diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js
index aabf0810f8..ac449c39d7 100644
--- a/src/components/views/globals/MatrixToolbar.js
+++ b/src/components/views/globals/MatrixToolbar.js
@@ -20,7 +20,7 @@ import { _t } from '../../../languageHandler';
 import Notifier from '../../../Notifier';
 import AccessibleButton from '../../../components/views/elements/AccessibleButton';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MatrixToolbar',
 
     hideToolbar: function() {
diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js
index 3dac90fc35..e028f5a000 100644
--- a/src/components/views/groups/GroupMemberInfo.js
+++ b/src/components/views/groups/GroupMemberInfo.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 Vector Creations Ltd
 Copyright 2017 New Vector Ltd
+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.
@@ -27,7 +28,7 @@ import { GroupMemberType } from '../../../groups';
 import GroupStore from '../../../stores/GroupStore';
 import AccessibleButton from '../elements/AccessibleButton';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'GroupMemberInfo',
 
     contextTypes: {
diff --git a/src/components/views/groups/GroupRoomInfo.js b/src/components/views/groups/GroupRoomInfo.js
index f9f7324e23..1460c7bf0b 100644
--- a/src/components/views/groups/GroupRoomInfo.js
+++ b/src/components/views/groups/GroupRoomInfo.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017 New Vector Ltd
+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.
@@ -24,7 +25,7 @@ import sdk from '../../../index';
 import { _t } from '../../../languageHandler';
 import GroupStore from '../../../stores/GroupStore';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'GroupRoomInfo',
 
     contextTypes: {
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index 552b1108d2..37f1401a58 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -194,7 +194,7 @@ function computedStyle(element) {
     return cssText;
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MFileBody',
 
     getInitialState: function() {
diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js
index 8366d0dd01..7d0c782bdc 100644
--- a/src/components/views/messages/MVideoBody.js
+++ b/src/components/views/messages/MVideoBody.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -23,7 +24,7 @@ import { decryptFile } from '../../../utils/DecryptFile';
 import { _t } from '../../../languageHandler';
 import SettingsStore from "../../../settings/SettingsStore";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MVideoBody',
 
     propTypes: {
diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js
index ba271f95b5..61170111c7 100644
--- a/src/components/views/messages/MessageEvent.js
+++ b/src/components/views/messages/MessageEvent.js
@@ -21,7 +21,7 @@ import sdk from '../../../index';
 import SettingsStore from "../../../settings/SettingsStore";
 import {Mjolnir} from "../../../mjolnir/Mjolnir";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MessageEvent',
 
     propTypes: {
diff --git a/src/components/views/messages/RoomAvatarEvent.js b/src/components/views/messages/RoomAvatarEvent.js
index 513e104d12..faae53776d 100644
--- a/src/components/views/messages/RoomAvatarEvent.js
+++ b/src/components/views/messages/RoomAvatarEvent.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 Vector Creations Ltd
 Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+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.
@@ -24,7 +25,7 @@ import sdk from '../../../index';
 import Modal from '../../../Modal';
 import AccessibleButton from '../elements/AccessibleButton';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomAvatarEvent',
 
     propTypes: {
diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.js
index 9bb6fcc0d8..8689a636d8 100644
--- a/src/components/views/messages/RoomCreate.js
+++ b/src/components/views/messages/RoomCreate.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -23,7 +24,7 @@ import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
 import { _t } from '../../../languageHandler';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomCreate',
 
     propTypes: {
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 6bf45d9193..48144fbb2d 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -35,7 +35,7 @@ import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
 import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
 import {toRightOf} from "../../structures/ContextMenu";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'TextualBody',
 
     propTypes: {
diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js
index be9adeed77..83b3c84f1e 100644
--- a/src/components/views/messages/TextualEvent.js
+++ b/src/components/views/messages/TextualEvent.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -20,7 +21,7 @@ import createReactClass from 'create-react-class';
 
 const TextForEvent = require('../../../TextForEvent');
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'TextualEvent',
 
     propTypes: {
diff --git a/src/components/views/messages/UnknownBody.js b/src/components/views/messages/UnknownBody.js
index ed2306de4f..2a19f324e8 100644
--- a/src/components/views/messages/UnknownBody.js
+++ b/src/components/views/messages/UnknownBody.js
@@ -18,7 +18,7 @@ import React from 'react';
 import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'UnknownBody',
 
     render: function() {
diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js
index 952c49828b..1e06da0cd8 100644
--- a/src/components/views/room_settings/ColorSettings.js
+++ b/src/components/views/room_settings/ColorSettings.js
@@ -40,7 +40,7 @@ const ROOM_COLORS = [
 // has a high possibility of being used in the nearish future.
 // Ref: https://github.com/vector-im/riot-web/issues/8421
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ColorSettings',
 
     propTypes: {
diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js
index 7a8332cc9f..30420b193f 100644
--- a/src/components/views/room_settings/UrlPreviewSettings.js
+++ b/src/components/views/room_settings/UrlPreviewSettings.js
@@ -1,7 +1,8 @@
 /*
 Copyright 2016 OpenMarket Ltd
 Copyright 2017 Travis Ralston
-Copyright 2018-2019 New Vector Ltd
+Copyright 2018, 2019 New Vector Ltd
+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.
@@ -26,7 +27,7 @@ import dis from "../../../dispatcher";
 import MatrixClientPeg from "../../../MatrixClientPeg";
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'UrlPreviewSettings',
 
     propTypes: {
diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js
index e53570dc5b..5cf7923df3 100644
--- a/src/components/views/rooms/AppsDrawer.js
+++ b/src/components/views/rooms/AppsDrawer.js
@@ -34,7 +34,7 @@ import SettingsStore from "../../../settings/SettingsStore";
 // The maximum number of widgets that can be added in a room
 const MAX_WIDGETS = 2;
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'AppsDrawer',
 
     propTypes: {
diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js
index a83160ddbf..498a7131a2 100644
--- a/src/components/views/rooms/AuxPanel.js
+++ b/src/components/views/rooms/AuxPanel.js
@@ -29,7 +29,7 @@ import RateLimitedFunc from '../../../ratelimitedfunc';
 import SettingsStore from "../../../settings/SettingsStore";
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'AuxPanel',
 
     propTypes: {
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 988482df7f..8f645522a3 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -118,7 +118,7 @@ const MAX_READ_AVATARS = 5;
 // |    '--------------------------------------'              |
 // '----------------------------------------------------------'
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'EventTile',
 
     propTypes: {
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
index 4a6c560d2c..7e48071fe5 100644
--- a/src/components/views/rooms/ForwardMessage.js
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -23,7 +23,7 @@ import dis from '../../../dispatcher';
 import { KeyCode } from '../../../Keyboard';
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ForwardMessage',
 
     propTypes: {
diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js
index 2e3a3915d0..985ddc43bd 100644
--- a/src/components/views/rooms/LinkPreviewWidget.js
+++ b/src/components/views/rooms/LinkPreviewWidget.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -25,7 +26,7 @@ const MatrixClientPeg = require('../../../MatrixClientPeg');
 const ImageUtils = require('../../../ImageUtils');
 const Modal = require('../../../Modal');
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'LinkPreviewWidget',
 
     propTypes: {
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index 1a2c8e2212..d6e23bd87c 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -50,7 +50,7 @@ import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 import MatrixClientPeg from "../../../MatrixClientPeg";
 import {EventTimeline} from "matrix-js-sdk";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MemberInfo',
 
     propTypes: {
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index 05464b43c9..a74d34c02c 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -32,7 +32,7 @@ const INITIAL_LOAD_NUM_MEMBERS = 30;
 const INITIAL_LOAD_NUM_INVITED = 5;
 const SHOW_MORE_INCREMENT = 100;
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MemberList',
 
     getInitialState: function() {
diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js
index c002849450..eacb35c69d 100644
--- a/src/components/views/rooms/MemberTile.js
+++ b/src/components/views/rooms/MemberTile.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -24,7 +25,7 @@ const sdk = require('../../../index');
 const dis = require('../../../dispatcher');
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'MemberTile',
 
     propTypes: {
diff --git a/src/components/views/rooms/PinnedEventTile.js b/src/components/views/rooms/PinnedEventTile.js
index 1279c01049..ca03fd787c 100644
--- a/src/components/views/rooms/PinnedEventTile.js
+++ b/src/components/views/rooms/PinnedEventTile.js
@@ -25,7 +25,7 @@ import MemberAvatar from "../avatars/MemberAvatar";
 import { _t } from '../../../languageHandler';
 import {formatFullDate} from '../../../DateUtils';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PinnedEventTile',
     propTypes: {
         mxRoom: PropTypes.object.isRequired,
diff --git a/src/components/views/rooms/PinnedEventsPanel.js b/src/components/views/rooms/PinnedEventsPanel.js
index dd2febdf39..3f07e25588 100644
--- a/src/components/views/rooms/PinnedEventsPanel.js
+++ b/src/components/views/rooms/PinnedEventsPanel.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017 Travis Ralston
+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.
@@ -23,7 +24,7 @@ import PinnedEventTile from "./PinnedEventTile";
 import { _t } from '../../../languageHandler';
 import PinningUtils from "../../../utils/PinningUtils";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PinnedEventsPanel',
     propTypes: {
         // The Room from the js-sdk we're going to show pinned events for
diff --git a/src/components/views/rooms/PresenceLabel.js b/src/components/views/rooms/PresenceLabel.js
index 5cb34b473f..f9dcd7e89d 100644
--- a/src/components/views/rooms/PresenceLabel.js
+++ b/src/components/views/rooms/PresenceLabel.js
@@ -21,7 +21,7 @@ import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'PresenceLabel',
 
     propTypes: {
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js
index 27c5e8c20e..7dda4651b5 100644
--- a/src/components/views/rooms/ReadReceiptMarker.js
+++ b/src/components/views/rooms/ReadReceiptMarker.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -35,7 +36,7 @@ try {
 } catch (e) {
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ReadReceiptMarker',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomDropTarget.js b/src/components/views/rooms/RoomDropTarget.js
index 1012b23105..61b7ca6d59 100644
--- a/src/components/views/rooms/RoomDropTarget.js
+++ b/src/components/views/rooms/RoomDropTarget.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -17,7 +18,7 @@ limitations under the License.
 import React from 'react';
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomDropTarget',
 
     render: function() {
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index eaf2e733ca..8567c32563 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -32,7 +33,7 @@ import SettingsStore from "../../../settings/SettingsStore";
 import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
 import E2EIcon from './E2EIcon';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomHeader',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 210c9394dc..496f942245 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -49,7 +49,7 @@ function labelForTagName(tagName) {
     return tagName;
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomList',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomNameEditor.js b/src/components/views/rooms/RoomNameEditor.js
index 375a4b42b1..d227574ed1 100644
--- a/src/components/views/rooms/RoomNameEditor.js
+++ b/src/components/views/rooms/RoomNameEditor.js
@@ -21,7 +21,7 @@ const sdk = require('../../../index');
 const MatrixClientPeg = require('../../../MatrixClientPeg');
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomNameEditor',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js
index a43a4df158..e460c2583f 100644
--- a/src/components/views/rooms/RoomPreviewBar.js
+++ b/src/components/views/rooms/RoomPreviewBar.js
@@ -43,7 +43,7 @@ const MessageCase = Object.freeze({
     OtherError: "OtherError",
 });
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomPreviewBar',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index 817ada9706..1ae0af29c2 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -33,7 +33,7 @@ import RoomViewStore from '../../../stores/RoomViewStore';
 import SettingsStore from "../../../settings/SettingsStore";
 import {_t} from "../../../languageHandler";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomTile',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomTopicEditor.js b/src/components/views/rooms/RoomTopicEditor.js
index a7d11313ff..2a58dcef39 100644
--- a/src/components/views/rooms/RoomTopicEditor.js
+++ b/src/components/views/rooms/RoomTopicEditor.js
@@ -20,7 +20,7 @@ import createReactClass from 'create-react-class';
 import sdk from '../../../index';
 import { _t } from "../../../languageHandler";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomTopicEditor',
 
     propTypes: {
diff --git a/src/components/views/rooms/RoomUpgradeWarningBar.js b/src/components/views/rooms/RoomUpgradeWarningBar.js
index 58d959ddcc..f1117ff8c6 100644
--- a/src/components/views/rooms/RoomUpgradeWarningBar.js
+++ b/src/components/views/rooms/RoomUpgradeWarningBar.js
@@ -23,7 +23,7 @@ import Modal from '../../../Modal';
 import { _t } from '../../../languageHandler';
 import MatrixClientPeg from "../../../MatrixClientPeg";
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'RoomUpgradeWarningBar',
 
     propTypes: {
diff --git a/src/components/views/rooms/SearchBar.js b/src/components/views/rooms/SearchBar.js
index 492c29a621..4edcab3d16 100644
--- a/src/components/views/rooms/SearchBar.js
+++ b/src/components/views/rooms/SearchBar.js
@@ -20,7 +20,7 @@ const classNames = require('classnames');
 const AccessibleButton = require('../../../components/views/elements/AccessibleButton');
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'SearchBar',
 
     getInitialState: function() {
diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.js
index 19ed490683..f5e77879b2 100644
--- a/src/components/views/rooms/SearchResultTile.js
+++ b/src/components/views/rooms/SearchResultTile.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015 OpenMarket Ltd
+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.
@@ -19,7 +20,7 @@ import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 import sdk from '../../../index';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'SearchResult',
 
     propTypes: {
diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js
index 024816c6fc..bbc98cedef 100644
--- a/src/components/views/rooms/SearchableEntityList.js
+++ b/src/components/views/rooms/SearchableEntityList.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -182,4 +183,4 @@ const SearchableEntityList = createReactClass({
     },
 });
 
- module.exports = SearchableEntityList;
+export default SearchableEntityList;
diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js
index c7a1a22580..04805c799f 100644
--- a/src/components/views/rooms/TopUnreadMessagesBar.js
+++ b/src/components/views/rooms/TopUnreadMessagesBar.js
@@ -22,7 +22,7 @@ import createReactClass from 'create-react-class';
 import { _t } from '../../../languageHandler';
 import AccessibleButton from '../elements/AccessibleButton';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'TopUnreadMessagesBar',
 
     propTypes: {
diff --git a/src/components/views/rooms/UserTile.js b/src/components/views/rooms/UserTile.js
index 76afda6dd7..0234d1ed85 100644
--- a/src/components/views/rooms/UserTile.js
+++ b/src/components/views/rooms/UserTile.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -21,7 +22,7 @@ import createReactClass from 'create-react-class';
 const Avatar = require("../../../Avatar");
 const sdk = require('../../../index');
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'UserTile',
 
     propTypes: {
diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js
index 0e23286eb6..567d821a5e 100644
--- a/src/components/views/rooms/WhoIsTypingTile.js
+++ b/src/components/views/rooms/WhoIsTypingTile.js
@@ -23,7 +23,7 @@ import Timer from '../../../utils/Timer';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 import MemberAvatar from '../avatars/MemberAvatar';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'WhoIsTypingTile',
 
     propTypes: {
diff --git a/src/components/views/settings/ChangeAvatar.js b/src/components/views/settings/ChangeAvatar.js
index 904b17b15f..3718e8d2db 100644
--- a/src/components/views/settings/ChangeAvatar.js
+++ b/src/components/views/settings/ChangeAvatar.js
@@ -21,7 +21,7 @@ import MatrixClientPeg from "../../../MatrixClientPeg";
 import sdk from '../../../index';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ChangeAvatar',
     propTypes: {
         initialAvatarUrl: PropTypes.string,
diff --git a/src/components/views/settings/ChangeDisplayName.js b/src/components/views/settings/ChangeDisplayName.js
index 90c761ba3d..65aa8a9af6 100644
--- a/src/components/views/settings/ChangeDisplayName.js
+++ b/src/components/views/settings/ChangeDisplayName.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2018 New Vector Ltd
+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.
@@ -21,7 +22,7 @@ import sdk from '../../../index';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ChangeDisplayName',
 
     _getDisplayName: async function() {
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index a317c46cec..db38fa065a 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -30,7 +30,7 @@ import { _t } from '../../../languageHandler';
 
 import sessionStore from '../../../stores/SessionStore';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'ChangePassword',
 
     propTypes: {
diff --git a/src/components/views/settings/EnableNotificationsButton.js b/src/components/views/settings/EnableNotificationsButton.js
index 1f65c39e6e..9ca591f30e 100644
--- a/src/components/views/settings/EnableNotificationsButton.js
+++ b/src/components/views/settings/EnableNotificationsButton.js
@@ -20,7 +20,7 @@ import Notifier from "../../../Notifier";
 import dis from "../../../dispatcher";
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'EnableNotificationsButton',
 
     componentDidMount: function() {
diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js
index 7345980bff..cc0841011f 100644
--- a/src/components/views/settings/Notifications.js
+++ b/src/components/views/settings/Notifications.js
@@ -63,7 +63,7 @@ function portLegacyActions(actions) {
     }
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'Notifications',
 
     phases: {
diff --git a/src/components/views/voip/CallPreview.js b/src/components/views/voip/CallPreview.js
index 15c30dcb5b..d049e23dcf 100644
--- a/src/components/views/voip/CallPreview.js
+++ b/src/components/views/voip/CallPreview.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017, 2018 New Vector Ltd
+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.
@@ -22,7 +23,7 @@ import CallHandler from '../../../CallHandler';
 import dis from '../../../dispatcher';
 import sdk from '../../../index';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CallPreview',
 
     propTypes: {
diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js
index 3a62ffbac2..79c1c13c30 100644
--- a/src/components/views/voip/CallView.js
+++ b/src/components/views/voip/CallView.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -22,7 +23,7 @@ import sdk from '../../../index';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 import { _t } from '../../../languageHandler';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'CallView',
 
     propTypes: {
diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js
index 2a2839d103..7317e0a63f 100644
--- a/src/components/views/voip/IncomingCallBox.js
+++ b/src/components/views/voip/IncomingCallBox.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
 Copyright 2018 New Vector Ltd
+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.
@@ -22,7 +23,7 @@ import dis from '../../../dispatcher';
 import { _t } from '../../../languageHandler';
 import sdk from '../../../index';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'IncomingCallBox',
 
     propTypes: {
diff --git a/src/components/views/voip/VideoFeed.js b/src/components/views/voip/VideoFeed.js
index 0faa227088..4210c60177 100644
--- a/src/components/views/voip/VideoFeed.js
+++ b/src/components/views/voip/VideoFeed.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -18,7 +19,7 @@ import React, {createRef} from 'react';
 import PropTypes from 'prop-types';
 import createReactClass from 'create-react-class';
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'VideoFeed',
 
     propTypes: {
diff --git a/src/components/views/voip/VideoView.js b/src/components/views/voip/VideoView.js
index 83584bcc68..4409e1aad5 100644
--- a/src/components/views/voip/VideoView.js
+++ b/src/components/views/voip/VideoView.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -34,7 +35,7 @@ function getFullScreenElement() {
     );
 }
 
-module.exports = createReactClass({
+export default createReactClass({
     displayName: 'VideoView',
 
     propTypes: {
diff --git a/src/createRoom.js b/src/createRoom.js
index 0ee90beba8..d47343abf6 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -35,7 +35,7 @@ import {getAddressType} from "./UserAddress";
  * @returns {Promise} which resolves to the room id, or null if the
  * action was aborted or failed.
  */
-function createRoom(opts) {
+export default function createRoom(opts) {
     opts = opts || {};
     if (opts.spinner === undefined) opts.spinner = true;
 
@@ -139,5 +139,3 @@ function createRoom(opts) {
         return null;
     });
 }
-
-module.exports = createRoom;
diff --git a/src/dispatcher.js b/src/dispatcher.js
index 48c8dc86e9..f6bbea97d8 100644
--- a/src/dispatcher.js
+++ b/src/dispatcher.js
@@ -55,4 +55,4 @@ class MatrixDispatcher extends flux.Dispatcher {
 if (global.mxDispatcher === undefined) {
     global.mxDispatcher = new MatrixDispatcher();
 }
-module.exports = global.mxDispatcher;
+export default global.mxDispatcher;
diff --git a/src/email.js b/src/email.js
index 3fd535c849..6e2ed69bb7 100644
--- a/src/email.js
+++ b/src/email.js
@@ -16,8 +16,6 @@ limitations under the License.
 
 const EMAIL_ADDRESS_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
 
-module.exports = {
-    looksValid: function(email) {
-        return EMAIL_ADDRESS_REGEX.test(email);
-    },
-};
+export function looksValid(email) {
+    return EMAIL_ADDRESS_REGEX.test(email);
+}
diff --git a/src/extend.js b/src/extend.js
index 4b3f028a94..263d802ab6 100644
--- a/src/extend.js
+++ b/src/extend.js
@@ -16,11 +16,11 @@ limitations under the License.
 
 'use strict';
 
-module.exports = function(dest, src) {
+export default function(dest, src) {
     for (const i in src) {
         if (src.hasOwnProperty(i)) {
             dest[i] = src[i];
         }
     }
     return dest;
-};
+}
diff --git a/src/indexing/EventIndexPeg.js b/src/indexing/EventIndexPeg.js
index 75f0fa66ba..3746591b1f 100644
--- a/src/indexing/EventIndexPeg.js
+++ b/src/indexing/EventIndexPeg.js
@@ -110,4 +110,4 @@ class EventIndexPeg {
 if (!global.mxEventIndexPeg) {
     global.mxEventIndexPeg = new EventIndexPeg();
 }
-module.exports = global.mxEventIndexPeg;
+export default global.mxEventIndexPeg;
diff --git a/src/notifications/ContentRules.js b/src/notifications/ContentRules.js
index f7e722dbfe..8c285220c7 100644
--- a/src/notifications/ContentRules.js
+++ b/src/notifications/ContentRules.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -16,9 +17,9 @@ limitations under the License.
 
 'use strict';
 
-const PushRuleVectorState = require('./PushRuleVectorState');
+import {PushRuleVectorState} from "./PushRuleVectorState";
 
-module.exports = {
+export class ContentRules {
     /**
      * Extract the keyword rules from a list of rules, and parse them
      * into a form which is useful for Vector's UI.
@@ -30,7 +31,7 @@ module.exports = {
      *   externalRules: a list of other keyword rules, with states other than
      *      vectorState
      */
-    parseContentRules: function(rulesets) {
+    static parseContentRules(rulesets) {
         // first categorise the keyword rules in terms of their actions
         const contentRules = this._categoriseContentRules(rulesets);
 
@@ -79,9 +80,9 @@ module.exports = {
                 externalRules: contentRules.other,
             };
         }
-    },
+    }
 
-    _categoriseContentRules: function(rulesets) {
+    static _categoriseContentRules(rulesets) {
         const contentRules = {on: [], on_but_disabled: [], loud: [], loud_but_disabled: [], other: []};
         for (const kind in rulesets.global) {
             for (let i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) {
@@ -116,5 +117,5 @@ module.exports = {
             }
         }
         return contentRules;
-    },
-};
+    }
+}
diff --git a/src/notifications/NotificationUtils.js b/src/notifications/NotificationUtils.js
index 79c1b38f6d..bf393da060 100644
--- a/src/notifications/NotificationUtils.js
+++ b/src/notifications/NotificationUtils.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -16,14 +17,14 @@ limitations under the License.
 
 'use strict';
 
-module.exports = {
+export class NotificationUtils {
     // Encodes a dictionary of {
     //   "notify": true/false,
     //   "sound": string or undefined,
     //   "highlight: true/false,
     // }
     // to a list of push actions.
-    encodeActions: function(action) {
+    static encodeActions(action) {
         const notify = action.notify;
         const sound = action.sound;
         const highlight = action.highlight;
@@ -41,7 +42,7 @@ module.exports = {
         } else {
             return ["dont_notify"];
         }
-    },
+    }
 
     // Decode a list of actions to a dictionary of {
     //   "notify": true/false,
@@ -49,7 +50,7 @@ module.exports = {
     //   "highlight: true/false,
     // }
     // If the actions couldn't be decoded then returns null.
-    decodeActions: function(actions) {
+    static decodeActions(actions) {
         let notify = false;
         let sound = null;
         let highlight = false;
@@ -85,5 +86,5 @@ module.exports = {
             result.sound = sound;
         }
         return result;
-    },
-};
+    }
+}
diff --git a/src/notifications/PushRuleVectorState.js b/src/notifications/PushRuleVectorState.js
index f4ba365b6d..263226ce1c 100644
--- a/src/notifications/PushRuleVectorState.js
+++ b/src/notifications/PushRuleVectorState.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -16,42 +17,44 @@ limitations under the License.
 
 'use strict';
 
-const StandardActions = require('./StandardActions');
-const NotificationUtils = require('./NotificationUtils');
+import {StandardActions} from "./StandardActions";
+import {NotificationUtils} from "./NotificationUtils";
 
-const states = {
-    /** The push rule is disabled */
-    OFF: "off",
+export class PushRuleVectorState {
+    // Backwards compatibility (things should probably be using .states instead)
+    static OFF = "off";
+    static ON = "on";
+    static LOUD = "loud";
 
-    /** The user will receive push notification for this rule */
-    ON: "on",
-
-    /** The user will receive push notification for this rule with sound and
-        highlight if this is legitimate */
-    LOUD: "loud",
-};
-
-
-module.exports = {
     /**
      * Enum for state of a push rule as defined by the Vector UI.
      * @readonly
      * @enum {string}
      */
-    states: states,
+    static states = {
+        /** The push rule is disabled */
+        OFF: PushRuleVectorState.OFF,
+
+        /** The user will receive push notification for this rule */
+        ON: PushRuleVectorState.ON,
+
+        /** The user will receive push notification for this rule with sound and
+         highlight if this is legitimate */
+        LOUD: PushRuleVectorState.LOUD,
+    };
 
     /**
      * Convert a PushRuleVectorState to a list of actions
      *
      * @return [object] list of push-rule actions
      */
-    actionsFor: function(pushRuleVectorState) {
-        if (pushRuleVectorState === this.ON) {
+    static actionsFor(pushRuleVectorState) {
+        if (pushRuleVectorState === PushRuleVectorState.ON) {
             return StandardActions.ACTION_NOTIFY;
-        } else if (pushRuleVectorState === this.LOUD) {
+        } else if (pushRuleVectorState === PushRuleVectorState.LOUD) {
             return StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND;
         }
-    },
+    }
 
     /**
      * Convert a pushrule's actions to a PushRuleVectorState.
@@ -60,7 +63,7 @@ module.exports = {
      * category or in PushRuleVectorState.LOUD, regardless of its enabled
      * state. Returns null if it does not match these categories.
      */
-    contentRuleVectorStateKind: function(rule) {
+    static contentRuleVectorStateKind(rule) {
         const decoded = NotificationUtils.decodeActions(rule.actions);
 
         if (!decoded) {
@@ -78,16 +81,12 @@ module.exports = {
         let stateKind = null;
         switch (tweaks) {
             case 0:
-                stateKind = this.ON;
+                stateKind = PushRuleVectorState.ON;
                 break;
             case 2:
-                stateKind = this.LOUD;
+                stateKind = PushRuleVectorState.LOUD;
                 break;
         }
         return stateKind;
-    },
-};
-
-for (const k in states) {
-    module.exports[k] = states[k];
+    }
 }
diff --git a/src/notifications/StandardActions.js b/src/notifications/StandardActions.js
index 15f645d5f7..b54cea332a 100644
--- a/src/notifications/StandardActions.js
+++ b/src/notifications/StandardActions.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -16,16 +17,16 @@ limitations under the License.
 
 'use strict';
 
-const NotificationUtils = require('./NotificationUtils');
+import {NotificationUtils} from "./NotificationUtils";
 
 const encodeActions = NotificationUtils.encodeActions;
 
-module.exports = {
-    ACTION_NOTIFY: encodeActions({notify: true}),
-    ACTION_NOTIFY_DEFAULT_SOUND: encodeActions({notify: true, sound: "default"}),
-    ACTION_NOTIFY_RING_SOUND: encodeActions({notify: true, sound: "ring"}),
-    ACTION_HIGHLIGHT: encodeActions({notify: true, highlight: true}),
-    ACTION_HIGHLIGHT_DEFAULT_SOUND: encodeActions({notify: true, sound: "default", highlight: true}),
-    ACTION_DONT_NOTIFY: encodeActions({notify: false}),
-    ACTION_DISABLED: null,
-};
+export class StandardActions {
+    static ACTION_NOTIFY = encodeActions({notify: true});
+    static ACTION_NOTIFY_DEFAULT_SOUND = encodeActions({notify: true, sound: "default"});
+    static ACTION_NOTIFY_RING_SOUND = encodeActions({notify: true, sound: "ring"});
+    static ACTION_HIGHLIGHT = encodeActions({notify: true, highlight: true});
+    static ACTION_HIGHLIGHT_DEFAULT_SOUND = encodeActions({notify: true, sound: "default", highlight: true});
+    static ACTION_DONT_NOTIFY = encodeActions({notify: false});
+    static ACTION_DISABLED = null;
+}
diff --git a/src/notifications/VectorPushRulesDefinitions.js b/src/notifications/VectorPushRulesDefinitions.js
index b15fb4ccd7..98d197a004 100644
--- a/src/notifications/VectorPushRulesDefinitions.js
+++ b/src/notifications/VectorPushRulesDefinitions.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -17,10 +18,9 @@ limitations under the License.
 'use strict';
 
 import { _td } from '../languageHandler';
-
-const StandardActions = require('./StandardActions');
-const PushRuleVectorState = require('./PushRuleVectorState');
-const { decodeActions } = require('./NotificationUtils');
+import {StandardActions} from "./StandardActions";
+import {PushRuleVectorState} from "./PushRuleVectorState";
+import {NotificationUtils} from "./NotificationUtils";
 
 class VectorPushRuleDefinition {
     constructor(opts) {
@@ -51,8 +51,8 @@ class VectorPushRuleDefinition {
                 // value: true vs. unspecified for highlight (which defaults to
                 // true, making them equivalent).
                 if (enabled &&
-                        JSON.stringify(decodeActions(rule.actions)) ===
-                        JSON.stringify(decodeActions(vectorStateToActions))) {
+                        JSON.stringify(NotificationUtils.decodeActions(rule.actions)) ===
+                        JSON.stringify(NotificationUtils.decodeActions(vectorStateToActions))) {
                     return state;
                 }
             }
@@ -68,7 +68,7 @@ class VectorPushRuleDefinition {
 /**
  * The descriptions of rules managed by the Vector UI.
  */
-module.exports = {
+export const VectorPushRulesDefinitions = {
     // Messages containing user's display name
     ".m.rule.contains_display_name": new VectorPushRuleDefinition({
         kind: "override",
diff --git a/src/notifications/index.js b/src/notifications/index.js
index 8ed77e9d41..7c400ad8b3 100644
--- a/src/notifications/index.js
+++ b/src/notifications/index.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -16,9 +17,7 @@ limitations under the License.
 
 'use strict';
 
-module.exports = {
-    NotificationUtils: require('./NotificationUtils'),
-    PushRuleVectorState: require('./PushRuleVectorState'),
-    VectorPushRulesDefinitions: require('./VectorPushRulesDefinitions'),
-    ContentRules: require('./ContentRules'),
-};
+export * from "./NotificationUtils";
+export * from "./PushRuleVectorState";
+export * from "./VectorPushRulesDefinitions";
+export * from "./ContentRules";
diff --git a/src/rageshake/rageshake.js b/src/rageshake/rageshake.js
index 47bab38079..a9d17e77c9 100644
--- a/src/rageshake/rageshake.js
+++ b/src/rageshake/rageshake.js
@@ -432,77 +432,73 @@ function selectQuery(store, keyRange, resultMapper) {
     });
 }
 
-
-module.exports = {
-
-    /**
-     * Configure rage shaking support for sending bug reports.
-     * Modifies globals.
-     * @return {Promise} Resolves when set up.
-     */
-    init: function() {
-        if (global.mx_rage_initPromise) {
-            return global.mx_rage_initPromise;
-        }
-        global.mx_rage_logger = new ConsoleLogger();
-        global.mx_rage_logger.monkeyPatch(window.console);
-
-        // just *accessing* indexedDB throws an exception in firefox with
-        // indexeddb disabled.
-        let indexedDB;
-        try {
-            indexedDB = window.indexedDB;
-        } catch (e) {}
-
-        if (indexedDB) {
-            global.mx_rage_store = new IndexedDBLogStore(indexedDB, global.mx_rage_logger);
-            global.mx_rage_initPromise = global.mx_rage_store.connect();
-            return global.mx_rage_initPromise;
-        }
-        global.mx_rage_initPromise = Promise.resolve();
+/**
+ * Configure rage shaking support for sending bug reports.
+ * Modifies globals.
+ * @return {Promise} Resolves when set up.
+ */
+export function init() {
+    if (global.mx_rage_initPromise) {
         return global.mx_rage_initPromise;
-    },
+    }
+    global.mx_rage_logger = new ConsoleLogger();
+    global.mx_rage_logger.monkeyPatch(window.console);
 
-    flush: function() {
-        if (!global.mx_rage_store) {
-            return;
-        }
-        global.mx_rage_store.flush();
-    },
+    // just *accessing* indexedDB throws an exception in firefox with
+    // indexeddb disabled.
+    let indexedDB;
+    try {
+        indexedDB = window.indexedDB;
+    } catch (e) {}
 
-    /**
-     * Clean up old logs.
-     * @return Promise Resolves if cleaned logs.
-     */
-    cleanup: async function() {
-        if (!global.mx_rage_store) {
-            return;
-        }
-        await global.mx_rage_store.consume();
-    },
+    if (indexedDB) {
+        global.mx_rage_store = new IndexedDBLogStore(indexedDB, global.mx_rage_logger);
+        global.mx_rage_initPromise = global.mx_rage_store.connect();
+        return global.mx_rage_initPromise;
+    }
+    global.mx_rage_initPromise = Promise.resolve();
+    return global.mx_rage_initPromise;
+}
 
-    /**
-     * Get a recent snapshot of the logs, ready for attaching to a bug report
-     *
-     * @return {Array<{lines: string, id, string}>}  list of log data
-     */
-    getLogsForReport: async function() {
-        if (!global.mx_rage_logger) {
-            throw new Error(
-                "No console logger, did you forget to call init()?",
-            );
-        }
-        // If in incognito mode, store is null, but we still want bug report
-        // sending to work going off the in-memory console logs.
-        if (global.mx_rage_store) {
-            // flush most recent logs
-            await global.mx_rage_store.flush();
-            return await global.mx_rage_store.consume();
-        } else {
-            return [{
-                lines: global.mx_rage_logger.flush(true),
-                id: "-",
-            }];
-        }
-    },
-};
+export function flush() {
+    if (!global.mx_rage_store) {
+        return;
+    }
+    global.mx_rage_store.flush();
+}
+
+/**
+ * Clean up old logs.
+ * @return Promise Resolves if cleaned logs.
+ */
+export async function cleanup() {
+    if (!global.mx_rage_store) {
+        return;
+    }
+    await global.mx_rage_store.consume();
+}
+
+/**
+ * Get a recent snapshot of the logs, ready for attaching to a bug report
+ *
+ * @return {Array<{lines: string, id, string}>}  list of log data
+ */
+export async function getLogsForReport() {
+    if (!global.mx_rage_logger) {
+        throw new Error(
+            "No console logger, did you forget to call init()?",
+        );
+    }
+    // If in incognito mode, store is null, but we still want bug report
+    // sending to work going off the in-memory console logs.
+    if (global.mx_rage_store) {
+        // flush most recent logs
+        await global.mx_rage_store.flush();
+        return await global.mx_rage_store.consume();
+    } else {
+        return [{
+            lines: global.mx_rage_logger.flush(true),
+            id: "-",
+        }];
+    }
+}
diff --git a/src/rageshake/submit-rageshake.js b/src/rageshake/submit-rageshake.js
index 457958eb82..0e5db7c54c 100644
--- a/src/rageshake/submit-rageshake.js
+++ b/src/rageshake/submit-rageshake.js
@@ -22,7 +22,7 @@ import MatrixClientPeg from '../MatrixClientPeg';
 import PlatformPeg from '../PlatformPeg';
 import { _t } from '../languageHandler';
 
-import rageshake from './rageshake';
+import * as rageshake from './rageshake';
 
 
 // polyfill textencoder if necessary
diff --git a/src/stores/GroupStore.js b/src/stores/GroupStore.js
index 637e87b728..3ba0e04631 100644
--- a/src/stores/GroupStore.js
+++ b/src/stores/GroupStore.js
@@ -340,4 +340,4 @@ let singletonGroupStore = null;
 if (!singletonGroupStore) {
     singletonGroupStore = new GroupStore();
 }
-module.exports = singletonGroupStore;
+export default singletonGroupStore;
diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js
index 91dcf0aebb..904f29f7b3 100644
--- a/src/stores/LifecycleStore.js
+++ b/src/stores/LifecycleStore.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 Vector Creations Ltd
 Copyright 2018 New Vector Ltd
+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.
@@ -79,4 +80,4 @@ let singletonLifecycleStore = null;
 if (!singletonLifecycleStore) {
     singletonLifecycleStore = new LifecycleStore();
 }
-module.exports = singletonLifecycleStore;
+export default singletonLifecycleStore;
diff --git a/src/stores/MessageComposerStore.js b/src/stores/MessageComposerStore.js
index ab2dbfedec..3c7440e10b 100644
--- a/src/stores/MessageComposerStore.js
+++ b/src/stores/MessageComposerStore.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017, 2018 Vector Creations Ltd
+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.
@@ -63,4 +64,4 @@ let singletonMessageComposerStore = null;
 if (!singletonMessageComposerStore) {
     singletonMessageComposerStore = new MessageComposerStore();
 }
-module.exports = singletonMessageComposerStore;
+export default singletonMessageComposerStore;
diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js
index a3caf876ef..eb2d539bb1 100644
--- a/src/stores/RoomViewStore.js
+++ b/src/stores/RoomViewStore.js
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 Vector Creations Ltd
 Copyright 2017, 2018 New Vector Ltd
+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.
@@ -357,4 +358,4 @@ let singletonRoomViewStore = null;
 if (!singletonRoomViewStore) {
     singletonRoomViewStore = new RoomViewStore();
 }
-module.exports = singletonRoomViewStore;
+export default singletonRoomViewStore;
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index ad58f1e93d..f38bc046d0 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2017 Vector Creations Ltd
+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.
@@ -86,4 +87,4 @@ let singletonSessionStore = null;
 if (!singletonSessionStore) {
     singletonSessionStore = new SessionStore();
 }
-module.exports = singletonSessionStore;
+export default singletonSessionStore;
diff --git a/src/stores/WidgetEchoStore.js b/src/stores/WidgetEchoStore.js
index 0d14ed1d60..33fa45c635 100644
--- a/src/stores/WidgetEchoStore.js
+++ b/src/stores/WidgetEchoStore.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -105,4 +106,4 @@ let singletonWidgetEchoStore = null;
 if (!singletonWidgetEchoStore) {
     singletonWidgetEchoStore = new WidgetEchoStore();
 }
-module.exports = singletonWidgetEchoStore;
+export default singletonWidgetEchoStore;
diff --git a/test/components/stub-component.js b/test/components/stub-component.js
index 9264792ffb..5638ada09d 100644
--- a/test/components/stub-component.js
+++ b/test/components/stub-component.js
@@ -4,7 +4,7 @@
 import React from 'react';
 import createReactClass from 'create-react-class';
 
-module.exports = function(opts) {
+export default function(opts) {
     opts = opts || {};
     if (!opts.displayName) {
         opts.displayName = 'StubComponent';
diff --git a/test/end-to-end-tests/src/logbuffer.js b/test/end-to-end-tests/src/logbuffer.js
index d586dc8b84..db4be33e8d 100644
--- a/test/end-to-end-tests/src/logbuffer.js
+++ b/test/end-to-end-tests/src/logbuffer.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-module.exports = class LogBuffer {
+export default class LogBuffer {
     constructor(page, eventName, eventMapper, reduceAsync=false, initialValue = "") {
         this.buffer = initialValue;
         page.on(eventName, (arg) => {
diff --git a/test/end-to-end-tests/src/logger.js b/test/end-to-end-tests/src/logger.js
index 283d07f163..42a9544e4d 100644
--- a/test/end-to-end-tests/src/logger.js
+++ b/test/end-to-end-tests/src/logger.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-module.exports = class Logger {
+export default class Logger {
     constructor(username) {
         this.indent = 0;
         this.username = username;
diff --git a/test/end-to-end-tests/src/rest/creator.js b/test/end-to-end-tests/src/rest/creator.js
index fde54014b2..c4ddee0581 100644
--- a/test/end-to-end-tests/src/rest/creator.js
+++ b/test/end-to-end-tests/src/rest/creator.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -31,7 +32,7 @@ function execAsync(command, options) {
     });
 }
 
-module.exports = class RestSessionCreator {
+export default class RestSessionCreator {
     constructor(synapseSubdir, hsUrl, cwd) {
         this.synapseSubdir = synapseSubdir;
         this.hsUrl = hsUrl;
diff --git a/test/end-to-end-tests/src/rest/multi.js b/test/end-to-end-tests/src/rest/multi.js
index e58b9f3f57..d4cd5c765c 100644
--- a/test/end-to-end-tests/src/rest/multi.js
+++ b/test/end-to-end-tests/src/rest/multi.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,7 +17,7 @@ limitations under the License.
 
 const Logger = require('../logger');
 
-module.exports = class RestMultiSession {
+export default class RestMultiSession {
     constructor(sessions, groupName) {
         this.log = new Logger(groupName);
         this.sessions = sessions;
diff --git a/test/end-to-end-tests/src/rest/room.js b/test/end-to-end-tests/src/rest/room.js
index 429a29c31a..b3ba725336 100644
--- a/test/end-to-end-tests/src/rest/room.js
+++ b/test/end-to-end-tests/src/rest/room.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -17,7 +18,7 @@ limitations under the License.
 const uuidv4 = require('uuid/v4');
 
 /* no pun intented */
-module.exports = class RestRoom {
+export default class RestRoom {
     constructor(session, roomId, log) {
         this.session = session;
         this._roomId = roomId;
diff --git a/test/end-to-end-tests/src/rest/session.js b/test/end-to-end-tests/src/rest/session.js
index 5b97824f5c..344493968c 100644
--- a/test/end-to-end-tests/src/rest/session.js
+++ b/test/end-to-end-tests/src/rest/session.js
@@ -19,7 +19,7 @@ const Logger = require('../logger');
 const RestRoom = require('./room');
 const {approveConsent} = require('./consent');
 
-module.exports = class RestSession {
+export default class RestSession {
     constructor(credentials) {
         this.log = new Logger(credentials.userId);
         this._credentials = credentials;
diff --git a/test/end-to-end-tests/src/scenarios/directory.js b/test/end-to-end-tests/src/scenarios/directory.js
index 3ae728a5b7..cf995ae1a8 100644
--- a/test/end-to-end-tests/src/scenarios/directory.js
+++ b/test/end-to-end-tests/src/scenarios/directory.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -21,7 +22,7 @@ const {receiveMessage} = require('../usecases/timeline');
 const {createRoom} = require('../usecases/create-room');
 const changeRoomSettings = require('../usecases/room-settings');
 
-module.exports = async function roomDirectoryScenarios(alice, bob) {
+export default async function roomDirectoryScenarios(alice, bob) {
     console.log(" creating a public room and join through directory:");
     const room = 'test';
     await createRoom(alice, room);
diff --git a/test/end-to-end-tests/src/scenarios/e2e-encryption.js b/test/end-to-end-tests/src/scenarios/e2e-encryption.js
index 8df374bacb..2a002da66b 100644
--- a/test/end-to-end-tests/src/scenarios/e2e-encryption.js
+++ b/test/end-to-end-tests/src/scenarios/e2e-encryption.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -23,7 +24,7 @@ const changeRoomSettings = require('../usecases/room-settings');
 const {startSasVerifcation, acceptSasVerification} = require('../usecases/verify');
 const assert = require('assert');
 
-module.exports = async function e2eEncryptionScenarios(alice, bob) {
+export default async function e2eEncryptionScenarios(alice, bob) {
     console.log(" creating an e2e encrypted room and join through invite:");
     const room = "secrets";
     await createRoom(bob, room);
diff --git a/test/end-to-end-tests/src/scenarios/lazy-loading.js b/test/end-to-end-tests/src/scenarios/lazy-loading.js
index be5a91bb71..651397e426 100644
--- a/test/end-to-end-tests/src/scenarios/lazy-loading.js
+++ b/test/end-to-end-tests/src/scenarios/lazy-loading.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -27,7 +28,7 @@ const {getMembersInMemberlist} = require('../usecases/memberlist');
 const changeRoomSettings = require('../usecases/room-settings');
 const assert = require('assert');
 
-module.exports = async function lazyLoadingScenarios(alice, bob, charlies) {
+export default async function lazyLoadingScenarios(alice, bob, charlies) {
     console.log(" creating a room for lazy loading member scenarios:");
     const charly1to5 = charlies.slice("charly-1..5", 0, 5);
     const charly6to10 = charlies.slice("charly-6..10", 5);
diff --git a/test/end-to-end-tests/src/session.js b/test/end-to-end-tests/src/session.js
index 65cec6fef0..b17e55efa4 100644
--- a/test/end-to-end-tests/src/session.js
+++ b/test/end-to-end-tests/src/session.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -21,7 +22,7 @@ const {delay} = require('./util');
 
 const DEFAULT_TIMEOUT = 20000;
 
-module.exports = class RiotSession {
+export default class RiotSession {
     constructor(browser, page, username, riotserver, hsUrl) {
         this.browser = browser;
         this.page = page;
diff --git a/test/end-to-end-tests/src/usecases/accept-invite.js b/test/end-to-end-tests/src/usecases/accept-invite.js
index 085c60aa6a..d17f583a77 100644
--- a/test/end-to-end-tests/src/usecases/accept-invite.js
+++ b/test/end-to-end-tests/src/usecases/accept-invite.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-module.exports = async function acceptInvite(session, name) {
+export default async function acceptInvite(session, name) {
     session.log.step(`accepts "${name}" invite`);
     //TODO: brittle selector
     const invitesHandles = await session.queryAll('.mx_RoomTile_name.mx_RoomTile_invite');
diff --git a/test/end-to-end-tests/src/usecases/create-room.js b/test/end-to-end-tests/src/usecases/create-room.js
index 75abdc78f4..7ca3826c71 100644
--- a/test/end-to-end-tests/src/usecases/create-room.js
+++ b/test/end-to-end-tests/src/usecases/create-room.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,12 +15,12 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-async function openRoomDirectory(session) {
+export async function openRoomDirectory(session) {
     const roomDirectoryButton = await session.query('.mx_LeftPanel_explore .mx_AccessibleButton');
     await roomDirectoryButton.click();
 }
 
-async function createRoom(session, roomName) {
+export async function createRoom(session, roomName) {
     session.log.step(`creates room "${roomName}"`);
 
     const roomListHeaders = await session.queryAll('.mx_RoomSubList_labelContainer');
@@ -42,5 +43,3 @@ async function createRoom(session, roomName) {
     await session.query('.mx_MessageComposer');
     session.log.done();
 }
-
-module.exports = {openRoomDirectory, createRoom};
diff --git a/test/end-to-end-tests/src/usecases/dialog.js b/test/end-to-end-tests/src/usecases/dialog.js
index 7b5d4d09fa..72d024fc79 100644
--- a/test/end-to-end-tests/src/usecases/dialog.js
+++ b/test/end-to-end-tests/src/usecases/dialog.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,20 +17,20 @@ limitations under the License.
 
 const assert = require('assert');
 
-async function assertDialog(session, expectedTitle) {
+export async function assertDialog(session, expectedTitle) {
     const titleElement = await session.query(".mx_Dialog .mx_Dialog_title");
     const dialogHeader = await session.innerText(titleElement);
     assert(dialogHeader, expectedTitle);
 }
 
-async function acceptDialog(session, expectedTitle) {
+export async function acceptDialog(session, expectedTitle) {
     const foundDialog = await acceptDialogMaybe(session, expectedTitle);
     if (!foundDialog) {
         throw new Error("could not find a dialog");
     }
 }
 
-async function acceptDialogMaybe(session, expectedTitle) {
+export async function acceptDialogMaybe(session, expectedTitle) {
     let primaryButton = null;
     try {
         primaryButton = await session.query(".mx_Dialog .mx_Dialog_primary");
@@ -42,9 +43,3 @@ async function acceptDialogMaybe(session, expectedTitle) {
     await primaryButton.click();
     return true;
 }
-
-module.exports = {
-    assertDialog,
-    acceptDialog,
-    acceptDialogMaybe,
-};
diff --git a/test/end-to-end-tests/src/usecases/invite.js b/test/end-to-end-tests/src/usecases/invite.js
index 814ecd30a6..fc91e4ce16 100644
--- a/test/end-to-end-tests/src/usecases/invite.js
+++ b/test/end-to-end-tests/src/usecases/invite.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-module.exports = async function invite(session, userId) {
+export default async function invite(session, userId) {
     session.log.step(`invites "${userId}" to room`);
     await session.delay(1000);
     const memberPanelButton = await session.query(".mx_RightPanel_membersButton");
diff --git a/test/end-to-end-tests/src/usecases/join.js b/test/end-to-end-tests/src/usecases/join.js
index bc292a0768..4fbc134598 100644
--- a/test/end-to-end-tests/src/usecases/join.js
+++ b/test/end-to-end-tests/src/usecases/join.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,7 +17,7 @@ limitations under the License.
 
 const {openRoomDirectory} = require('./create-room');
 
-module.exports = async function join(session, roomName) {
+export default async function join(session, roomName) {
     session.log.step(`joins room "${roomName}"`);
     await openRoomDirectory(session);
     const roomInput = await session.query('.mx_DirectorySearchBox input');
diff --git a/test/end-to-end-tests/src/usecases/room-settings.js b/test/end-to-end-tests/src/usecases/room-settings.js
index 7655d2d066..d853cf92e4 100644
--- a/test/end-to-end-tests/src/usecases/room-settings.js
+++ b/test/end-to-end-tests/src/usecases/room-settings.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -29,7 +30,7 @@ async function setSettingsToggle(session, toggle, enabled) {
     }
 }
 
-module.exports = async function changeRoomSettings(session, settings) {
+export default async function changeRoomSettings(session, settings) {
     session.log.startGroup(`changes the room settings`);
     /// XXX delay is needed here, possibly because the header is being rerendered
     /// click doesn't do anything otherwise
diff --git a/test/end-to-end-tests/src/usecases/signup.js b/test/end-to-end-tests/src/usecases/signup.js
index fd2b948572..381b87ec9e 100644
--- a/test/end-to-end-tests/src/usecases/signup.js
+++ b/test/end-to-end-tests/src/usecases/signup.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,7 +17,7 @@ limitations under the License.
 
 const assert = require('assert');
 
-module.exports = async function signup(session, username, password, homeserver) {
+export default async function signup(session, username, password, homeserver) {
     session.log.step("signs up");
     await session.goto(session.url('/#/register'));
     // change the homeserver by clicking the advanced section

From 4aec432b30209bfe633221c5a93c13140e69e213 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Thu, 19 Dec 2019 17:57:50 -0700
Subject: [PATCH 3/4] Convert the more complicated CommonJS exports to
 ES6-style

---
 src/CallHandler.js                            |  6 +--
 src/ObjectUtils.js                            |  9 ++--
 src/Resend.js                                 | 42 ++++++++++---------
 src/VectorConferenceHandler.js                | 28 ++++++-------
 src/components/structures/RoomView.js         |  4 +-
 src/components/views/rooms/EventTile.js       |  8 ++--
 src/index.js                                  | 13 +++---
 test/end-to-end-tests/src/rest/consent.js     |  5 ++-
 .../src/usecases/memberlist.js                | 13 +++---
 .../end-to-end-tests/src/usecases/settings.js |  9 ++--
 .../end-to-end-tests/src/usecases/timeline.js | 13 +++---
 test/end-to-end-tests/src/usecases/verify.js  |  9 ++--
 test/end-to-end-tests/src/util.js             |  9 ++--
 test/mock-clock.js                            |  9 ++--
 14 files changed, 91 insertions(+), 86 deletions(-)

diff --git a/src/CallHandler.js b/src/CallHandler.js
index eb5a5c1c8e..7dabaa3fc1 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -302,7 +302,7 @@ function _onAction(payload) {
     switch (payload.action) {
         case 'place_call':
             {
-                if (module.exports.getAnyActiveCall()) {
+                if (callHandler.getAnyActiveCall()) {
                     const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                     Modal.createTrackedDialog('Call Handler', 'Existing Call', ErrorDialog, {
                         title: _t('Existing Call'),
@@ -355,7 +355,7 @@ function _onAction(payload) {
             break;
         case 'incoming_call':
             {
-                if (module.exports.getAnyActiveCall()) {
+                if (callHandler.getAnyActiveCall()) {
                     // ignore multiple incoming calls. in future, we may want a line-1/line-2 setup.
                     // we avoid rejecting with "busy" in case the user wants to answer it on a different device.
                     // in future we could signal a "local busy" as a warning to the caller.
@@ -523,7 +523,7 @@ if (!global.mxCallHandler) {
 
 const callHandler = {
     getCallForRoom: function(roomId) {
-        let call = module.exports.getCall(roomId);
+        let call = callHandler.getCall(roomId);
         if (call) return call;
 
         if (ConferenceHandler) {
diff --git a/src/ObjectUtils.js b/src/ObjectUtils.js
index 07d8b465af..24dfe61d68 100644
--- a/src/ObjectUtils.js
+++ b/src/ObjectUtils.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2016 OpenMarket Ltd
+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.
@@ -22,7 +23,7 @@ limitations under the License.
  * @return {Object[]} An array of objects with the form:
  * { key: $KEY, val: $VALUE, place: "add|del" }
  */
-module.exports.getKeyValueArrayDiffs = function(before, after) {
+export function getKeyValueArrayDiffs(before, after) {
     const results = [];
     const delta = {};
     Object.keys(before).forEach(function(beforeKey) {
@@ -76,7 +77,7 @@ module.exports.getKeyValueArrayDiffs = function(before, after) {
     });
 
     return results;
-};
+}
 
 /**
  * Shallow-compare two objects for equality: each key and value must be identical
@@ -84,7 +85,7 @@ module.exports.getKeyValueArrayDiffs = function(before, after) {
  * @param {Object} objB Second object to compare against the first
  * @return {boolean} whether the two objects have same key=values
  */
-module.exports.shallowEqual = function(objA, objB) {
+export function shallowEqual(objA, objB) {
     if (objA === objB) {
         return true;
     }
@@ -109,4 +110,4 @@ module.exports.shallowEqual = function(objA, objB) {
     }
 
     return true;
-};
+}
diff --git a/src/Resend.js b/src/Resend.js
index 51ec804c01..563171d73e 100644
--- a/src/Resend.js
+++ b/src/Resend.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -18,40 +19,43 @@ import MatrixClientPeg from './MatrixClientPeg';
 import dis from './dispatcher';
 import { EventStatus } from 'matrix-js-sdk';
 
-module.exports = {
-    resendUnsentEvents: function(room) {
-        room.getPendingEvents().filter(function(ev) {
+export default class Resend {
+    static resendUnsentEvents(room) {
+        room.getPendingEvents().filter(function (ev) {
             return ev.status === EventStatus.NOT_SENT;
-        }).forEach(function(event) {
-            module.exports.resend(event);
+        }).forEach(function (event) {
+            Resend.resend(event);
         });
-    },
-    cancelUnsentEvents: function(room) {
-        room.getPendingEvents().filter(function(ev) {
+    }
+
+    static cancelUnsentEvents(room) {
+        room.getPendingEvents().filter(function (ev) {
             return ev.status === EventStatus.NOT_SENT;
-        }).forEach(function(event) {
-            module.exports.removeFromQueue(event);
+        }).forEach(function (event) {
+            Resend.removeFromQueue(event);
         });
-    },
-    resend: function(event) {
+    }
+
+    static resend(event) {
         const room = MatrixClientPeg.get().getRoom(event.getRoomId());
-        MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
+        MatrixClientPeg.get().resendEvent(event, room).then(function (res) {
             dis.dispatch({
                 action: 'message_sent',
                 event: event,
             });
-        }, function(err) {
+        }, function (err) {
             // XXX: temporary logging to try to diagnose
             // https://github.com/vector-im/riot-web/issues/3148
-            console.log('Resend got send failure: ' + err.name + '('+err+')');
+            console.log('Resend got send failure: ' + err.name + '(' + err + ')');
 
             dis.dispatch({
                 action: 'message_send_failed',
                 event: event,
             });
         });
-    },
-    removeFromQueue: function(event) {
+    }
+
+    static removeFromQueue(event) {
         MatrixClientPeg.get().cancelPendingEvent(event);
-    },
-};
+    }
+}
diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
index e0e333a371..debb5f94b2 100644
--- a/src/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -28,10 +29,10 @@ import MatrixClientPeg from "./MatrixClientPeg";
 const USER_PREFIX = "fs_";
 const DOMAIN = "matrix.org";
 
-function ConferenceCall(matrixClient, groupChatRoomId) {
+export function ConferenceCall(matrixClient, groupChatRoomId) {
     this.client = matrixClient;
     this.groupRoomId = groupChatRoomId;
-    this.confUserId = module.exports.getConferenceUserIdForRoom(this.groupRoomId);
+    this.confUserId = getConferenceUserIdForRoom(this.groupRoomId);
 }
 
 ConferenceCall.prototype.setup = function() {
@@ -90,7 +91,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
  * @param {string} userId The user ID to check.
  * @return {boolean} True if it is a conference bot.
  */
-module.exports.isConferenceUser = function(userId) {
+export function isConferenceUser(userId) {
     if (userId.indexOf("@" + USER_PREFIX) !== 0) {
         return false;
     }
@@ -101,26 +102,26 @@ module.exports.isConferenceUser = function(userId) {
         return /^!.+:.+/.test(decoded);
     }
     return false;
-};
+}
 
-module.exports.getConferenceUserIdForRoom = function(roomId) {
+export function getConferenceUserIdForRoom(roomId) {
     // abuse browserify's core node Buffer support (strip padding ='s)
     const base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
     return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
-};
+}
 
-module.exports.createNewMatrixCall = function(client, roomId) {
+export function createNewMatrixCall(client, roomId) {
     const confCall = new ConferenceCall(
         client, roomId,
     );
     return confCall.setup();
-};
+}
 
-module.exports.getConferenceCallForRoom = function(roomId) {
+export function getConferenceCallForRoom(roomId) {
     // search for a conference 1:1 call for this group chat room ID
     const activeCall = CallHandler.getAnyActiveCall();
     if (activeCall && activeCall.confUserId) {
-        const thisRoomConfUserId = module.exports.getConferenceUserIdForRoom(
+        const thisRoomConfUserId = getConferenceUserIdForRoom(
             roomId,
         );
         if (thisRoomConfUserId === activeCall.confUserId) {
@@ -128,8 +129,7 @@ module.exports.getConferenceCallForRoom = function(roomId) {
         }
     }
     return null;
-};
+}
 
-module.exports.ConferenceCall = ConferenceCall;
-
-module.exports.slot = 'conference';
+// TODO: Document this.
+export const slot = 'conference';
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 3f9d680503..c2311586b3 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -66,7 +66,7 @@ if (DEBUG) {
     debuglog = console.log.bind(console);
 }
 
-const RoomContext = PropTypes.shape({
+export const RoomContext = PropTypes.shape({
     canReact: PropTypes.bool.isRequired,
     canReply: PropTypes.bool.isRequired,
     room: PropTypes.instanceOf(Room),
@@ -2002,5 +2002,3 @@ export default createReactClass({
         );
     },
 });
-
-module.exports.RoomContext = RoomContext;
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 8f645522a3..8b8c60e402 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -75,7 +75,7 @@ for (const evType of ALL_RULE_TYPES) {
     stateEventTileTypes[evType] = 'messages.TextualEvent';
 }
 
-function getHandlerTile(ev) {
+export function getHandlerTile(ev) {
     const type = ev.getType();
 
     // don't show verification requests we're not involved in,
@@ -879,7 +879,7 @@ function isMessageEvent(ev) {
     return (messageTypes.includes(ev.getType()));
 }
 
-module.exports.haveTileForEvent = function(e) {
+export function haveTileForEvent(e) {
     // Only messages have a tile (black-rectangle) if redacted
     if (e.isRedacted() && !isMessageEvent(e)) return false;
 
@@ -895,7 +895,7 @@ module.exports.haveTileForEvent = function(e) {
     } else {
         return true;
     }
-};
+}
 
 function E2ePadlockUndecryptable(props) {
     return (
@@ -964,5 +964,3 @@ class E2ePadlock extends React.Component {
         );
     }
 }
-
-module.exports.getHandlerTile = getHandlerTile;
diff --git a/src/index.js b/src/index.js
index 7d0547d9c9..008e15ad90 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -16,14 +17,14 @@ limitations under the License.
 
 import Skinner from './Skinner';
 
-module.exports.loadSkin = function(skinObject) {
+export function loadSkin(skinObject) {
     Skinner.load(skinObject);
-};
+}
 
-module.exports.resetSkin = function() {
+export function resetSkin() {
     Skinner.reset();
-};
+}
 
-module.exports.getComponent = function(componentName) {
+export function getComponent(componentName) {
     return Skinner.getComponent(componentName);
-};
+}
diff --git a/test/end-to-end-tests/src/rest/consent.js b/test/end-to-end-tests/src/rest/consent.js
index 1e36f541a3..5c424cd6e8 100644
--- a/test/end-to-end-tests/src/rest/consent.js
+++ b/test/end-to-end-tests/src/rest/consent.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -18,7 +19,7 @@ const request = require('request-promise-native');
 const cheerio = require('cheerio');
 const url = require("url");
 
-module.exports.approveConsent = async function(consentUrl) {
+export async function approveConsent(consentUrl) {
     const body = await request.get(consentUrl);
     const doc = cheerio.load(body);
     const v = doc("input[name=v]").val();
@@ -27,4 +28,4 @@ module.exports.approveConsent = async function(consentUrl) {
     const formAction = doc("form").attr("action");
     const absAction = url.resolve(consentUrl, formAction);
     await request.post(absAction).form({v, u, h});
-};
+}
diff --git a/test/end-to-end-tests/src/usecases/memberlist.js b/test/end-to-end-tests/src/usecases/memberlist.js
index 42601b6610..8d2aacf35c 100644
--- a/test/end-to-end-tests/src/usecases/memberlist.js
+++ b/test/end-to-end-tests/src/usecases/memberlist.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,7 +17,7 @@ limitations under the License.
 
 const assert = require('assert');
 
-async function openMemberInfo(session, name) {
+export async function openMemberInfo(session, name) {
     const membersAndNames = await getMembersInMemberlist(session);
     const matchingLabel = membersAndNames.filter((m) => {
         return m.displayName === name;
@@ -24,9 +25,7 @@ async function openMemberInfo(session, name) {
     await matchingLabel.click();
 }
 
-module.exports.openMemberInfo = openMemberInfo;
-
-module.exports.verifyDeviceForUser = async function(session, name, expectedDevice) {
+export async function verifyDeviceForUser(session, name, expectedDevice) {
     session.log.step(`verifies e2e device for ${name}`);
     const membersAndNames = await getMembersInMemberlist(session);
     const matchingLabel = membersAndNames.filter((m) => {
@@ -59,9 +58,9 @@ module.exports.verifyDeviceForUser = async function(session, name, expectedDevic
     const closeMemberInfo = await session.query(".mx_MemberInfo_cancel");
     await closeMemberInfo.click();
     session.log.done();
-};
+}
 
-async function getMembersInMemberlist(session) {
+export async function getMembersInMemberlist(session) {
     const memberPanelButton = await session.query(".mx_RightPanel_membersButton");
     try {
         await session.query(".mx_RightPanel_headerButton_highlight", 500);
@@ -78,5 +77,3 @@ async function getMembersInMemberlist(session) {
         return {label: el, displayName: await session.innerText(el)};
     }));
 }
-
-module.exports.getMembersInMemberlist = getMembersInMemberlist;
diff --git a/test/end-to-end-tests/src/usecases/settings.js b/test/end-to-end-tests/src/usecases/settings.js
index ec675157f2..411ec3b497 100644
--- a/test/end-to-end-tests/src/usecases/settings.js
+++ b/test/end-to-end-tests/src/usecases/settings.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -28,7 +29,7 @@ async function openSettings(session, section) {
     }
 }
 
-module.exports.enableLazyLoading = async function(session) {
+export async function enableLazyLoading(session) {
     session.log.step(`enables lazy loading of members in the lab settings`);
     const settingsButton = await session.query('.mx_BottomLeftMenu_settings');
     await settingsButton.click();
@@ -38,9 +39,9 @@ module.exports.enableLazyLoading = async function(session) {
     const closeButton = await session.query(".mx_RoomHeader_cancelButton");
     await closeButton.click();
     session.log.done();
-};
+}
 
-module.exports.getE2EDeviceFromSettings = async function(session) {
+export async function getE2EDeviceFromSettings(session) {
     session.log.step(`gets e2e device/key from settings`);
     await openSettings(session, "security");
     const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_SecurityUserSettingsTab_deviceInfo code");
@@ -51,4 +52,4 @@ module.exports.getE2EDeviceFromSettings = async function(session) {
     await closeButton.click();
     session.log.done();
     return {id, key};
-};
+}
diff --git a/test/end-to-end-tests/src/usecases/timeline.js b/test/end-to-end-tests/src/usecases/timeline.js
index 3ff9e0f5b4..8a18e2653f 100644
--- a/test/end-to-end-tests/src/usecases/timeline.js
+++ b/test/end-to-end-tests/src/usecases/timeline.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -16,7 +17,7 @@ limitations under the License.
 
 const assert = require('assert');
 
-module.exports.scrollToTimelineTop = async function(session) {
+export async function scrollToTimelineTop(session) {
     session.log.step(`scrolls to the top of the timeline`);
     await session.page.evaluate(() => {
         return Promise.resolve().then(async () => {
@@ -40,9 +41,9 @@ module.exports.scrollToTimelineTop = async function(session) {
         });
     });
     session.log.done();
-};
+}
 
-module.exports.receiveMessage = async function(session, expectedMessage) {
+export async function receiveMessage(session, expectedMessage) {
     session.log.step(`receives message "${expectedMessage.body}" from ${expectedMessage.sender}`);
     // wait for a response to come in that contains the message
     // crude, but effective
@@ -66,10 +67,10 @@ module.exports.receiveMessage = async function(session, expectedMessage) {
     });
     assertMessage(lastMessage, expectedMessage);
     session.log.done();
-};
+}
 
 
-module.exports.checkTimelineContains = async function(session, expectedMessages, sendersDescription) {
+export async function checkTimelineContains(session, expectedMessages, sendersDescription) {
     session.log.step(`checks timeline contains ${expectedMessages.length} ` +
         `given messages${sendersDescription ? ` from ${sendersDescription}`:""}`);
     const eventTiles = await getAllEventTiles(session);
@@ -101,7 +102,7 @@ module.exports.checkTimelineContains = async function(session, expectedMessages,
     });
 
     session.log.done();
-};
+}
 
 function assertMessage(foundMessage, expectedMessage) {
     assert(foundMessage, `message ${JSON.stringify(expectedMessage)} not found in timeline`);
diff --git a/test/end-to-end-tests/src/usecases/verify.js b/test/end-to-end-tests/src/usecases/verify.js
index 11ff98d097..a8b71cfe5c 100644
--- a/test/end-to-end-tests/src/usecases/verify.js
+++ b/test/end-to-end-tests/src/usecases/verify.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2019 New Vector Ltd
+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.
@@ -37,7 +38,7 @@ async function getSasCodes(session) {
     return sasLabels;
 }
 
-module.exports.startSasVerifcation = async function(session, name) {
+export async function startSasVerifcation(session, name) {
     await startVerification(session, name);
     // expect "Verify device" dialog and click "Begin Verification"
     await assertDialog(session, "Verify device");
@@ -50,9 +51,9 @@ module.exports.startSasVerifcation = async function(session, name) {
     // click "Got it" when verification is done
     await acceptDialog(session);
     return sasCodes;
-};
+}
 
-module.exports.acceptSasVerification = async function(session, name) {
+export async function acceptSasVerification(session, name) {
     await assertDialog(session, "Incoming Verification Request");
     const opponentLabelElement = await session.query(".mx_IncomingSasDialog_opponentProfile h2");
     const opponentLabel = await session.innerText(opponentLabelElement);
@@ -66,4 +67,4 @@ module.exports.acceptSasVerification = async function(session, name) {
     // click "Got it" when verification is done
     await acceptDialog(session);
     return sasCodes;
-};
+}
diff --git a/test/end-to-end-tests/src/util.js b/test/end-to-end-tests/src/util.js
index 699b11b5ce..cafe929eca 100644
--- a/test/end-to-end-tests/src/util.js
+++ b/test/end-to-end-tests/src/util.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 New Vector Ltd
+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.
@@ -14,14 +15,14 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-module.exports.range = function(start, amount, step = 1) {
+export function range(start, amount, step = 1) {
     const r = [];
     for (let i = 0; i < amount; ++i) {
         r.push(start + (i * step));
     }
     return r;
-};
+}
 
-module.exports.delay = function(ms) {
+export function delay(ms) {
     return new Promise((resolve) => setTimeout(resolve, ms));
-};
+}
diff --git a/test/mock-clock.js b/test/mock-clock.js
index 103e186c1f..7cad0e9483 100644
--- a/test/mock-clock.js
+++ b/test/mock-clock.js
@@ -1,5 +1,6 @@
 /*
 Copyright (c) 2008-2015 Pivotal Labs
+Copyright 2019 The Matrix.org Foundation C.I.C.
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
@@ -411,10 +412,10 @@ j$.MockDate = function() {
   return MockDate;
 }();
 
-const clock = new j$.Clock(global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
+const _clock = new j$.Clock(global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
 
-module.exports.clock = function() {
-    return clock;
-};
+export function clock() {
+    return _clock;
+}
 
 

From 206d4c78d2a3c943fd3d1ab6980061e4fb38a1ce Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 3 Jan 2020 12:32:29 -0700
Subject: [PATCH 4/4] Fix references to 'this' in Avatar and Unread

---
 src/Avatar.js | 2 +-
 src/Unread.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Avatar.js b/src/Avatar.js
index 3377849eca..70be0a4684 100644
--- a/src/Avatar.js
+++ b/src/Avatar.js
@@ -32,7 +32,7 @@ export function avatarUrlForMember(member, width, height, resizeMethod) {
         // member can be null here currently since on invites, the JS SDK
         // does not have enough info to build a RoomMember object for
         // the inviter.
-        url = this.defaultAvatarUrlForString(member ? member.userId : '');
+        url = defaultAvatarUrlForString(member ? member.userId : '');
     }
     return url;
 }
diff --git a/src/Unread.js b/src/Unread.js
index c2eaa468f5..dba8be492d 100644
--- a/src/Unread.js
+++ b/src/Unread.js
@@ -77,7 +77,7 @@ export function doesRoomHaveUnreadMessages(room) {
             // that counts and we can stop looking because the user's read
             // this and everything before.
             return false;
-        } else if (!shouldHideEvent(ev) && this.eventTriggersUnreadCount(ev)) {
+        } else if (!shouldHideEvent(ev) && eventTriggersUnreadCount(ev)) {
             // We've found a message that counts before we hit
             // the user's read receipt, so this room is definitely unread.
             return true;