diff --git a/api/command_settings_test.go b/api/command_settings_test.go
new file mode 100644
index 0000000000..f34154f39c
--- /dev/null
+++ b/api/command_settings_test.go
@@ -0,0 +1,13 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "testing"
+)
+
+func TestSettingsCommand(t *testing.T) {
+ th := Setup().InitBasic()
+ th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/settings"))
+}
diff --git a/app/command_settings.go b/app/command_settings.go
new file mode 100644
index 0000000000..e84492cc29
--- /dev/null
+++ b/app/command_settings.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "github.com/mattermost/platform/model"
+ goi18n "github.com/nicksnyder/go-i18n/i18n"
+)
+
+type SettingsProvider struct {
+}
+
+const (
+ CMD_SETTINGS = "settings"
+)
+
+func init() {
+ RegisterCommandProvider(&SettingsProvider{})
+}
+
+func (settings *SettingsProvider) GetTrigger() string {
+ return CMD_SETTINGS
+}
+
+func (settings *SettingsProvider) GetCommand(T goi18n.TranslateFunc) *model.Command {
+ return &model.Command{
+ Trigger: CMD_SETTINGS,
+ AutoComplete: true,
+ AutoCompleteDesc: T("api.command_settings.desc"),
+ AutoCompleteHint: "",
+ DisplayName: T("api.command_settings.name"),
+ }
+}
+
+func (settings *SettingsProvider) DoCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ // This command is handled client-side and shouldn't hit the server.
+ return &model.CommandResponse{
+ Text: args.T("api.command_settings.unsupported.app_error"),
+ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL,
+ }
+}
diff --git a/i18n/en.json b/i18n/en.json
index 584c8dec61..b2e0b7b5c9 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -659,6 +659,18 @@
"id": "api.command_online.success",
"translation": "You are now online"
},
+ {
+ "id": "api.command_settings.desc",
+ "translation": "Open the Account Settings dialog"
+ },
+ {
+ "id": "api.command_settings.name",
+ "translation": "settings"
+ },
+ {
+ "id": "api.command_settings.unsupported.app_error",
+ "translation": "The settings command is not supported on your device"
+ },
{
"id": "api.command_shortcuts.browser.channel_next",
"translation": "{{.ChannelNextCmd}}: Next channel in your history\n"
diff --git a/webapp/actions/channel_actions.jsx b/webapp/actions/channel_actions.jsx
index e756275bc5..3a01c40899 100644
--- a/webapp/actions/channel_actions.jsx
+++ b/webapp/actions/channel_actions.jsx
@@ -9,6 +9,8 @@ import ChannelStore from 'stores/channel_store.jsx';
import * as ChannelUtils from 'utils/channel_utils.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
+import * as GlobalActions from 'actions/global_actions.jsx';
+
import {loadProfilesForSidebar, loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx';
import {trackEvent} from 'actions/diagnostics_actions.jsx';
@@ -66,9 +68,15 @@ export function goToChannel(channel) {
export function executeCommand(message, args, success, error) {
let msg = message;
- msg = msg.substring(0, msg.indexOf(' ')).toLowerCase() + msg.substring(msg.indexOf(' '), msg.length);
+ let cmdLength = msg.indexOf(' ');
+ if (cmdLength < 0) {
+ cmdLength = msg.length;
+ }
+ const cmd = msg.substring(0, cmdLength).toLowerCase();
+ msg = cmd + msg.substring(cmdLength, msg.length);
- if (message.indexOf('/shortcuts') !== -1) {
+ switch (cmd) {
+ case '/shortcuts':
if (UserAgent.isMobile()) {
const err = {message: Utils.localizeMessage('create_post.shortcutsNotSupported', 'Keyboard shortcuts are not supported on your device')};
error(err);
@@ -78,7 +86,12 @@ export function executeCommand(message, args, success, error) {
} else if (message.indexOf('mac') !== -1) {
msg = '/shortcuts';
}
+ break;
+ case '/settings':
+ GlobalActions.showAccountSettingsModal();
+ return;
}
+
Client.executeCommand(msg, args, success,
(err) => {
AsyncClient.dispatchError(err, 'executeCommand');
@@ -86,7 +99,8 @@ export function executeCommand(message, args, success, error) {
if (error) {
error(err);
}
- });
+ }
+ );
}
export function setChannelAsRead(channelIdParam) {
diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx
index ca38ec9f1c..d3fa80d312 100644
--- a/webapp/actions/global_actions.jsx
+++ b/webapp/actions/global_actions.jsx
@@ -197,6 +197,13 @@ export function emitUserCommentedEvent(post) {
});
}
+export function showAccountSettingsModal() {
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.TOGGLE_ACCOUNT_SETTINGS_MODAL,
+ value: true
+ });
+}
+
export function showDeletePostModal(post, commentCount = 0) {
AppDispatcher.handleViewAction({
type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
diff --git a/webapp/components/needs_team/needs_team.jsx b/webapp/components/needs_team/needs_team.jsx
index 6fd2d32088..e86e4fb113 100644
--- a/webapp/components/needs_team/needs_team.jsx
+++ b/webapp/components/needs_team/needs_team.jsx
@@ -33,6 +33,7 @@ import store from 'stores/redux_store.jsx';
import {getPost} from 'mattermost-redux/selectors/entities/posts';
// Modals
+import UserSettingsModal from 'components/user_settings/user_settings_modal.jsx';
import GetPostLinkModal from 'components/get_post_link_modal.jsx';
import GetPublicLinkModal from 'components/get_public_link_modal.jsx';
import GetTeamInviteLinkModal from 'components/get_team_invite_link_modal.jsx';
@@ -218,6 +219,7 @@ export default class NeedsTeam extends React.Component {
{content}
+
diff --git a/webapp/components/sidebar_header_dropdown.jsx b/webapp/components/sidebar_header_dropdown.jsx
index 5a7784733d..54f8c3a2d1 100644
--- a/webapp/components/sidebar_header_dropdown.jsx
+++ b/webapp/components/sidebar_header_dropdown.jsx
@@ -13,7 +13,6 @@ import WebrtcStore from 'stores/webrtc_store.jsx';
import AboutBuildModal from './about_build_modal.jsx';
import SidebarHeaderDropdownButton from './sidebar_header_dropdown_button.jsx';
import TeamMembersModal from './team_members_modal.jsx';
-import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import AddUsersToTeam from 'components/add_users_to_team';
import {Constants, WebrtcActionTypes} from 'utils/constants.jsx';
@@ -45,7 +44,7 @@ export default class SidebarHeaderDropdown extends React.Component {
this.handleAboutModal = this.handleAboutModal.bind(this);
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
- this.toggleAccountSettingsModal = this.toggleAccountSettingsModal.bind(this);
+ this.showAccountSettingsModal = this.showAccountSettingsModal.bind(this);
this.showAddUsersToTeamModal = this.showAddUsersToTeamModal.bind(this);
this.hideAddUsersToTeamModal = this.hideAddUsersToTeamModal.bind(this);
this.showInviteMemberModal = this.showInviteMemberModal.bind(this);
@@ -54,7 +53,6 @@ export default class SidebarHeaderDropdown extends React.Component {
this.hideTeamMembersModal = this.hideTeamMembersModal.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
- this.openAccountSettings = this.openAccountSettings.bind(this);
this.renderCustomEmojiLink = this.renderCustomEmojiLink.bind(this);
@@ -66,7 +64,6 @@ export default class SidebarHeaderDropdown extends React.Component {
showAboutModal: false,
showDropdown: false,
showTeamMembersModal: false,
- showUserSettingsModal: false,
showAddUsersToTeamModal: false
};
}
@@ -104,13 +101,12 @@ export default class SidebarHeaderDropdown extends React.Component {
this.setState({showAboutModal: false});
}
- toggleAccountSettingsModal(e) {
+ showAccountSettingsModal(e) {
e.preventDefault();
- this.setState({
- showUserSettingsModal: !this.state.showUserSettingsModal,
- showDropdown: false
- });
+ this.setState({showDropdown: false});
+
+ GlobalActions.showAccountSettingsModal();
}
showAddUsersToTeamModal(e) {
@@ -160,7 +156,6 @@ export default class SidebarHeaderDropdown extends React.Component {
componentDidMount() {
TeamStore.addChangeListener(this.onTeamChange);
- document.addEventListener('keydown', this.openAccountSettings);
}
onTeamChange() {
@@ -174,13 +169,6 @@ export default class SidebarHeaderDropdown extends React.Component {
componentWillUnmount() {
$(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
TeamStore.removeChangeListener(this.onTeamChange);
- document.removeEventListener('keydown', this.openAccountSettings);
- }
-
- openAccountSettings(e) {
- if (Utils.cmdOrCtrlPressed(e) && e.shiftKey && e.keyCode === Constants.KeyCodes.A) {
- this.toggleAccountSettingsModal(e);
- }
}
renderCustomEmojiLink() {
@@ -527,7 +515,7 @@ export default class SidebarHeaderDropdown extends React.Component {
this.setState({showUserSettingsModal: false})}
- />
{teamMembersModal}
this.setState({showUserSettingsModal: true})}
+ onClick={() => GlobalActions.showAccountSettingsModal()}
>
- this.setState({showUserSettingsModal: false})}
- />