Support minimum to open user settings to a particular tab
Tabs now have IDs, and we use those IDs to open things. This doesn't do any conversion to typescript, and doesn't add the same feature to the room settings out of concern for the size of diff.
This commit is contained in:
parent
f05a1e532b
commit
de18af35ff
7 changed files with 92 additions and 25 deletions
|
@ -72,6 +72,7 @@ import {
|
||||||
hideToast as hideAnalyticsToast
|
hideToast as hideAnalyticsToast
|
||||||
} from "../../toasts/AnalyticsToast";
|
} from "../../toasts/AnalyticsToast";
|
||||||
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
|
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
|
||||||
|
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
||||||
|
|
||||||
/** constants for MatrixChat.state.view */
|
/** constants for MatrixChat.state.view */
|
||||||
export enum Views {
|
export enum Views {
|
||||||
|
@ -604,9 +605,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.viewIndexedRoom(payload.roomIndex);
|
this.viewIndexedRoom(payload.roomIndex);
|
||||||
break;
|
break;
|
||||||
case Action.ViewUserSettings: {
|
case Action.ViewUserSettings: {
|
||||||
|
const tabPayload = payload as OpenToTabPayload;
|
||||||
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
||||||
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {},
|
Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
|
||||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
{initialTabId: tabPayload.initialTabId},
|
||||||
|
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true
|
||||||
|
);
|
||||||
|
|
||||||
// View the welcome or home page if we need something to look at
|
// View the welcome or home page if we need something to look at
|
||||||
this.viewSomethingBehindModal();
|
this.viewSomethingBehindModal();
|
||||||
|
|
|
@ -27,25 +27,20 @@ import { ReactNode } from "react";
|
||||||
* Represents a tab for the TabbedView.
|
* Represents a tab for the TabbedView.
|
||||||
*/
|
*/
|
||||||
export class Tab {
|
export class Tab {
|
||||||
public label: string;
|
|
||||||
public icon: string;
|
|
||||||
public body: React.ReactNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new tab.
|
* Creates a new tab.
|
||||||
* @param {string} tabLabel The untranslated tab label.
|
* @param {string} id The tab's ID.
|
||||||
* @param {string} tabIconClass The class for the tab icon. This should be a simple mask.
|
* @param {string} label The untranslated tab label.
|
||||||
* @param {React.ReactNode} tabJsx The JSX for the tab container.
|
* @param {string} icon The class for the tab icon. This should be a simple mask.
|
||||||
|
* @param {React.ReactNode} body The JSX for the tab container.
|
||||||
*/
|
*/
|
||||||
constructor(tabLabel: string, tabIconClass: string, tabJsx: React.ReactNode) {
|
constructor(public id: string, public label: string, public icon: string, public body: React.ReactNode) {
|
||||||
this.label = tabLabel;
|
|
||||||
this.icon = tabIconClass;
|
|
||||||
this.body = tabJsx;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tabs: Tab[];
|
tabs: Tab[];
|
||||||
|
initialTabId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -53,16 +48,17 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TabbedView extends React.Component<IProps, IState> {
|
export default class TabbedView extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
|
||||||
// The tabs to show
|
|
||||||
tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
let activeTabIndex = 0;
|
||||||
|
if (props.initialTabId) {
|
||||||
|
const tabIndex = props.tabs.findIndex(t => t.id === props.initialTabId);
|
||||||
|
if (tabIndex >= 0) activeTabIndex = tabIndex;
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
activeTabIndex: 0,
|
activeTabIndex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { Action } from "../../dispatcher/actions";
|
||||||
import { createRef } from "react";
|
import { createRef } from "react";
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import {ContextMenu, ContextMenuButton} from "./ContextMenu";
|
import {ContextMenu, ContextMenuButton} from "./ContextMenu";
|
||||||
|
import {USER_NOTIFICATIONS_TAB, USER_SECURITY_TAB} from "../views/dialogs/UserSettingsDialog";
|
||||||
|
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
}
|
}
|
||||||
|
@ -80,11 +82,13 @@ export default class UserMenuButton extends React.Component<IProps, IState> {
|
||||||
console.log("TODO: Switch theme");
|
console.log("TODO: Switch theme");
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSettingsOpen = (ev: React.MouseEvent, tabRef: string) => {
|
private onSettingsOpen = (ev: React.MouseEvent, tabId: string) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
console.log("TODO: Open settings", tabRef);
|
const payload: OpenToTabPayload = {action: Action.ViewUserSettings, initialTabId: tabId};
|
||||||
|
defaultDispatcher.dispatch(payload);
|
||||||
|
this.setState({menuDisplayed: false}); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onShowArchived = (ev: React.MouseEvent) => {
|
private onShowArchived = (ev: React.MouseEvent) => {
|
||||||
|
@ -147,19 +151,19 @@ export default class UserMenuButton extends React.Component<IProps, IState> {
|
||||||
<div className="mx_UserMenuButton_contextMenu_optionList">
|
<div className="mx_UserMenuButton_contextMenu_optionList">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, 'notifications')}>
|
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}>
|
||||||
<img src={require("../../../res/img/feather-customised/notifications.svg")} width={16} />
|
<img src={require("../../../res/img/feather-customised/notifications.svg")} width={16} />
|
||||||
<span>{_t("Notification settings")}</span>
|
<span>{_t("Notification settings")}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, 'security')}>
|
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}>
|
||||||
<img src={require("../../../res/img/feather-customised/lock.svg")} width={16} />
|
<img src={require("../../../res/img/feather-customised/lock.svg")} width={16} />
|
||||||
<span>{_t("Security & privacy")}</span>
|
<span>{_t("Security & privacy")}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, 'all')}>
|
<a href={"#"} onClick={(e) => this.onSettingsOpen(e, null)}>
|
||||||
<img src={require("../../../res/img/feather-customised/settings.svg")} width={16} />
|
<img src={require("../../../res/img/feather-customised/settings.svg")} width={16} />
|
||||||
<span>{_t("All settings")}</span>
|
<span>{_t("All settings")}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -30,6 +30,13 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
|
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
|
||||||
|
export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB";
|
||||||
|
export const ROOM_ROLES_TAB = "ROOM_ROLES_TAB";
|
||||||
|
export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB";
|
||||||
|
export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB";
|
||||||
|
export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB";
|
||||||
|
|
||||||
export default class RoomSettingsDialog extends React.Component {
|
export default class RoomSettingsDialog extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
roomId: PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
|
@ -56,21 +63,25 @@ export default class RoomSettingsDialog extends React.Component {
|
||||||
const tabs = [];
|
const tabs = [];
|
||||||
|
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_GENERAL_TAB,
|
||||||
_td("General"),
|
_td("General"),
|
||||||
"mx_RoomSettingsDialog_settingsIcon",
|
"mx_RoomSettingsDialog_settingsIcon",
|
||||||
<GeneralRoomSettingsTab roomId={this.props.roomId} />,
|
<GeneralRoomSettingsTab roomId={this.props.roomId} />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_SECURITY_TAB,
|
||||||
_td("Security & Privacy"),
|
_td("Security & Privacy"),
|
||||||
"mx_RoomSettingsDialog_securityIcon",
|
"mx_RoomSettingsDialog_securityIcon",
|
||||||
<SecurityRoomSettingsTab roomId={this.props.roomId} />,
|
<SecurityRoomSettingsTab roomId={this.props.roomId} />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_ROLES_TAB,
|
||||||
_td("Roles & Permissions"),
|
_td("Roles & Permissions"),
|
||||||
"mx_RoomSettingsDialog_rolesIcon",
|
"mx_RoomSettingsDialog_rolesIcon",
|
||||||
<RolesRoomSettingsTab roomId={this.props.roomId} />,
|
<RolesRoomSettingsTab roomId={this.props.roomId} />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_NOTIFICATIONS_TAB,
|
||||||
_td("Notifications"),
|
_td("Notifications"),
|
||||||
"mx_RoomSettingsDialog_notificationsIcon",
|
"mx_RoomSettingsDialog_notificationsIcon",
|
||||||
<NotificationSettingsTab roomId={this.props.roomId} />,
|
<NotificationSettingsTab roomId={this.props.roomId} />,
|
||||||
|
@ -78,6 +89,7 @@ export default class RoomSettingsDialog extends React.Component {
|
||||||
|
|
||||||
if (SettingsStore.isFeatureEnabled("feature_bridge_state")) {
|
if (SettingsStore.isFeatureEnabled("feature_bridge_state")) {
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_BRIDGES_TAB,
|
||||||
_td("Bridges"),
|
_td("Bridges"),
|
||||||
"mx_RoomSettingsDialog_bridgesIcon",
|
"mx_RoomSettingsDialog_bridgesIcon",
|
||||||
<BridgeSettingsTab roomId={this.props.roomId} />,
|
<BridgeSettingsTab roomId={this.props.roomId} />,
|
||||||
|
@ -85,6 +97,7 @@ export default class RoomSettingsDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
ROOM_ADVANCED_TAB,
|
||||||
_td("Advanced"),
|
_td("Advanced"),
|
||||||
"mx_RoomSettingsDialog_warningIcon",
|
"mx_RoomSettingsDialog_warningIcon",
|
||||||
<AdvancedRoomSettingsTab roomId={this.props.roomId} closeSettingsFn={this.props.onFinished} />,
|
<AdvancedRoomSettingsTab roomId={this.props.roomId} closeSettingsFn={this.props.onFinished} />,
|
||||||
|
|
|
@ -33,9 +33,21 @@ import * as sdk from "../../../index";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab";
|
import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab";
|
||||||
|
|
||||||
|
export const USER_GENERAL_TAB = "USER_GENERAL_TAB";
|
||||||
|
export const USER_APPEARANCE_TAB = "USER_APPEARANCE_TAB";
|
||||||
|
export const USER_FLAIR_TAB = "USER_FLAIR_TAB";
|
||||||
|
export const USER_NOTIFICATIONS_TAB = "USER_NOTIFICATIONS_TAB";
|
||||||
|
export const USER_PREFERENCES_TAB = "USER_PREFERENCES_TAB";
|
||||||
|
export const USER_VOICE_TAB = "USER_VOICE_TAB";
|
||||||
|
export const USER_SECURITY_TAB = "USER_SECURITY_TAB";
|
||||||
|
export const USER_LABS_TAB = "USER_LABS_TAB";
|
||||||
|
export const USER_MJOLNIR_TAB = "USER_MJOLNIR_TAB";
|
||||||
|
export const USER_HELP_TAB = "USER_HELP_TAB";
|
||||||
|
|
||||||
export default class UserSettingsDialog extends React.Component {
|
export default class UserSettingsDialog extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
initialTabId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -63,42 +75,50 @@ export default class UserSettingsDialog extends React.Component {
|
||||||
const tabs = [];
|
const tabs = [];
|
||||||
|
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_GENERAL_TAB,
|
||||||
_td("General"),
|
_td("General"),
|
||||||
"mx_UserSettingsDialog_settingsIcon",
|
"mx_UserSettingsDialog_settingsIcon",
|
||||||
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_APPEARANCE_TAB,
|
||||||
_td("Appearance"),
|
_td("Appearance"),
|
||||||
"mx_UserSettingsDialog_appearanceIcon",
|
"mx_UserSettingsDialog_appearanceIcon",
|
||||||
<AppearanceUserSettingsTab />,
|
<AppearanceUserSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_FLAIR_TAB,
|
||||||
_td("Flair"),
|
_td("Flair"),
|
||||||
"mx_UserSettingsDialog_flairIcon",
|
"mx_UserSettingsDialog_flairIcon",
|
||||||
<FlairUserSettingsTab />,
|
<FlairUserSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_NOTIFICATIONS_TAB,
|
||||||
_td("Notifications"),
|
_td("Notifications"),
|
||||||
"mx_UserSettingsDialog_bellIcon",
|
"mx_UserSettingsDialog_bellIcon",
|
||||||
<NotificationUserSettingsTab />,
|
<NotificationUserSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_PREFERENCES_TAB,
|
||||||
_td("Preferences"),
|
_td("Preferences"),
|
||||||
"mx_UserSettingsDialog_preferencesIcon",
|
"mx_UserSettingsDialog_preferencesIcon",
|
||||||
<PreferencesUserSettingsTab />,
|
<PreferencesUserSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_VOICE_TAB,
|
||||||
_td("Voice & Video"),
|
_td("Voice & Video"),
|
||||||
"mx_UserSettingsDialog_voiceIcon",
|
"mx_UserSettingsDialog_voiceIcon",
|
||||||
<VoiceUserSettingsTab />,
|
<VoiceUserSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_SECURITY_TAB,
|
||||||
_td("Security & Privacy"),
|
_td("Security & Privacy"),
|
||||||
"mx_UserSettingsDialog_securityIcon",
|
"mx_UserSettingsDialog_securityIcon",
|
||||||
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
||||||
));
|
));
|
||||||
if (SdkConfig.get()['showLabsSettings'] || SettingsStore.getLabsFeatures().length > 0) {
|
if (SdkConfig.get()['showLabsSettings'] || SettingsStore.getLabsFeatures().length > 0) {
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_LABS_TAB,
|
||||||
_td("Labs"),
|
_td("Labs"),
|
||||||
"mx_UserSettingsDialog_labsIcon",
|
"mx_UserSettingsDialog_labsIcon",
|
||||||
<LabsUserSettingsTab />,
|
<LabsUserSettingsTab />,
|
||||||
|
@ -106,12 +126,14 @@ export default class UserSettingsDialog extends React.Component {
|
||||||
}
|
}
|
||||||
if (this.state.mjolnirEnabled) {
|
if (this.state.mjolnirEnabled) {
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_MJOLNIR_TAB,
|
||||||
_td("Ignored users"),
|
_td("Ignored users"),
|
||||||
"mx_UserSettingsDialog_mjolnirIcon",
|
"mx_UserSettingsDialog_mjolnirIcon",
|
||||||
<MjolnirUserSettingsTab />,
|
<MjolnirUserSettingsTab />,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
|
USER_HELP_TAB,
|
||||||
_td("Help & About"),
|
_td("Help & About"),
|
||||||
"mx_UserSettingsDialog_helpIcon",
|
"mx_UserSettingsDialog_helpIcon",
|
||||||
<HelpUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
<HelpUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
||||||
|
@ -127,7 +149,7 @@ export default class UserSettingsDialog extends React.Component {
|
||||||
<BaseDialog className='mx_UserSettingsDialog' hasCancel={true}
|
<BaseDialog className='mx_UserSettingsDialog' hasCancel={true}
|
||||||
onFinished={this.props.onFinished} title={_t("Settings")}>
|
onFinished={this.props.onFinished} title={_t("Settings")}>
|
||||||
<div className='ms_SettingsDialog_content'>
|
<div className='ms_SettingsDialog_content'>
|
||||||
<TabbedView tabs={this._getTabs()} />
|
<TabbedView tabs={this._getTabs()} initialTabId={this.props.initialTabId} />
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,6 +36,7 @@ export enum Action {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the user settings. No additional payload information required.
|
* Open the user settings. No additional payload information required.
|
||||||
|
* Optionally can include an OpenToTabPayload.
|
||||||
*/
|
*/
|
||||||
ViewUserSettings = "view_user_settings",
|
ViewUserSettings = "view_user_settings",
|
||||||
|
|
||||||
|
|
27
src/dispatcher/payloads/OpenToTabPayload.ts
Normal file
27
src/dispatcher/payloads/OpenToTabPayload.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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 { ActionPayload } from "../payloads";
|
||||||
|
import { Action } from "../actions";
|
||||||
|
|
||||||
|
export interface OpenToTabPayload extends ActionPayload {
|
||||||
|
action: Action.ViewUserSettings | string, // TODO: Add room settings action
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tab ID to open in the settings view to start, if possible.
|
||||||
|
*/
|
||||||
|
initialTabId?: string;
|
||||||
|
}
|
Loading…
Reference in a new issue