diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx
index 0504e3a76a..19edf505e0 100644
--- a/src/components/structures/LoggedInView.tsx
+++ b/src/components/structures/LoggedInView.tsx
@@ -452,9 +452,7 @@ class LoggedInView extends React.PureComponent
{
// composer, so CTRL+` it is
if (ctrlCmdOnly) {
- dis.dispatch({
- action: 'toggle_top_left_menu',
- });
+ dis.fire(Action.ToggleUserMenu);
handled = true;
}
break;
diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js
index 234dc661f9..71e7e61406 100644
--- a/src/components/structures/TopLeftMenuButton.js
+++ b/src/components/structures/TopLeftMenuButton.js
@@ -24,6 +24,7 @@ import * as Avatar from '../../Avatar';
import { _t } from '../../languageHandler';
import dis from "../../dispatcher/dispatcher";
import {ContextMenu, ContextMenuButton} from "./ContextMenu";
+import {Action} from "../../dispatcher/actions";
const AVATAR_SIZE = 28;
@@ -75,7 +76,7 @@ export default class TopLeftMenuButton extends React.Component {
onAction = (payload) => {
// For accessibility
- if (payload.action === "toggle_top_left_menu") {
+ if (payload.action === Action.ToggleUserMenu) {
if (this._buttonRef) this._buttonRef.click();
}
};
diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx
new file mode 100644
index 0000000000..0968e0bdb6
--- /dev/null
+++ b/src/components/structures/UserMenuButton.tsx
@@ -0,0 +1,211 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import * as React from "react";
+import {User} from "matrix-js-sdk/src/models/user";
+import { MatrixClientPeg } from "../../MatrixClientPeg";
+import defaultDispatcher from "../../dispatcher/dispatcher";
+import { ActionPayload } from "../../dispatcher/payloads";
+import { Action } from "../../dispatcher/actions";
+import { createRef } from "react";
+import { _t } from "../../languageHandler";
+import {ContextMenu, ContextMenuButton} from "./ContextMenu";
+
+interface IProps {
+}
+
+interface IState {
+ user: User;
+ menuDisplayed: boolean;
+}
+
+export default class UserMenuButton extends React.Component {
+ private dispatcherRef: string;
+ private buttonRef: React.RefObject = createRef();
+
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = {
+ menuDisplayed: false,
+ user: MatrixClientPeg.get().getUser(MatrixClientPeg.get().getUserId()),
+ };
+ }
+
+ private get displayName(): string {
+ if (MatrixClientPeg.get().isGuest()) {
+ return _t("Guest");
+ } else if (this.state.user) {
+ return this.state.user.displayName;
+ } else {
+ return MatrixClientPeg.get().getUserId();
+ }
+ }
+
+ public componentDidMount() {
+ this.dispatcherRef = defaultDispatcher.register(this.onAction);
+ }
+
+ private onAction = (ev: ActionPayload) => {
+ if (ev.action !== Action.ToggleUserMenu) return; // not interested
+
+ // For accessibility
+ if (this.buttonRef.current) this.buttonRef.current.click();
+ };
+
+ private onOpenMenuClick = (ev: InputEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+ this.setState({menuDisplayed: true});
+ };
+
+ private onCloseMenu = () => {
+ this.setState({menuDisplayed: false});
+ };
+
+ private onSwitchThemeClick = () => {
+ console.log("TODO: Switch theme");
+ };
+
+ private onSettingsOpen = (ev: React.MouseEvent, tabRef: string) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ console.log("TODO: Open settings", tabRef);
+ };
+
+ private onShowArchived = (ev: React.MouseEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ console.log("TODO: Show archived rooms");
+ };
+
+ private onProvideFeedback = (ev: React.MouseEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ console.log("TODO: Show feedback");
+ };
+
+ private onSignOutClick = (ev: React.MouseEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ console.log("TODO: Sign out");
+ };
+
+ public render() {
+ let contextMenu;
+ if (this.state.menuDisplayed) {
+ const elementRect = this.buttonRef.current.getBoundingClientRect();
+ contextMenu = (
+
+
+
+
+
+ {this.displayName}
+
+
+ {MatrixClientPeg.get().getUserId()}
+
+
+
+
+
+
+
+ TODO: Upgrade prompt
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ {contextMenu}
+
+ )
+ }
+}
diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts
index 71493d6e44..60ef61a6e9 100644
--- a/src/dispatcher/actions.ts
+++ b/src/dispatcher/actions.ts
@@ -58,4 +58,9 @@ export enum Action {
* Focuses the user's cursor to the composer. No additional payload information required.
*/
FocusComposer = "focus_composer",
+
+ /**
+ * Opens the user menu (previously known as the top left menu). No additional payload information required.
+ */
+ ToggleUserMenu = "toggle_user_menu",
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index cf6dc2431a..8575b3a258 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -2042,6 +2042,13 @@
"Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others",
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
+ "Switch to dark mode": "Switch to dark mode",
+ "Switch theme": "Switch theme",
+ "Security & privacy": "Security & privacy",
+ "All settings": "All settings",
+ "Archived rooms": "Archived rooms",
+ "Feedback": "Feedback",
+ "Account settings": "Account settings",
"Could not load user profile": "Could not load user profile",
"Verify this login": "Verify this login",
"Session verified": "Session verified",