summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/channel_header.jsx27
-rw-r--r--web/react/components/command_list.jsx2
-rw-r--r--web/react/components/delete_post_modal.jsx9
-rw-r--r--web/react/components/edit_channel_header_modal.jsx126
-rw-r--r--web/react/components/edit_channel_modal.jsx150
-rw-r--r--web/react/components/edit_post_modal.jsx4
-rw-r--r--web/react/components/get_link_modal.jsx144
-rw-r--r--web/react/components/get_team_invite_link_modal.jsx45
-rw-r--r--web/react/components/invite_member_modal.jsx36
-rw-r--r--web/react/components/member_list_team.jsx47
-rw-r--r--web/react/components/navbar.jsx56
-rw-r--r--web/react/components/navbar_dropdown.jsx19
-rw-r--r--web/react/components/post_info.jsx4
-rw-r--r--web/react/components/rhs_comment.jsx4
-rw-r--r--web/react/components/rhs_root_post.jsx84
-rw-r--r--web/react/components/sidebar_right_menu.jsx28
-rw-r--r--web/react/components/team_members.jsx130
-rw-r--r--web/react/components/team_members_modal.jsx69
-rw-r--r--web/react/components/toggle_modal_button.jsx17
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx4
-rw-r--r--web/react/dispatcher/event_helpers.jsx23
-rw-r--r--web/react/pages/channel.jsx18
-rw-r--r--web/react/stores/modal_store.jsx1
-rw-r--r--web/react/stores/team_store.jsx11
-rw-r--r--web/react/utils/channel_intro_mssages.jsx72
-rw-r--r--web/react/utils/constants.jsx3
-rw-r--r--web/react/utils/utils.jsx17
27 files changed, 575 insertions, 575 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 8c721348f..6e12c7c14 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -4,6 +4,7 @@
import NavbarSearchBox from './search_bar.jsx';
import MessageWrapper from './message_wrapper.jsx';
import PopoverListMembers from './popover_list_members.jsx';
+import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
import ChannelInfoModal from './channel_info_modal.jsx';
import ChannelInviteModal from './channel_invite_modal.jsx';
@@ -167,17 +168,13 @@ export default class ChannelHeader extends React.Component {
key='edit_header_direct'
role='presentation'
>
- <a
+ <ToggleModalButton
role='menuitem'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
>
{'Set Channel Header...'}
- </a>
+ </ToggleModalButton>
</li>
);
} else {
@@ -235,17 +232,13 @@ export default class ChannelHeader extends React.Component {
key='set_channel_header'
role='presentation'
>
- <a
+ <ToggleModalButton
role='menuitem'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
>
- {'Set '}{channelTerm}{' Header...'}
- </a>
+ {`Set ${channelTerm} Header...`}
+ </ToggleModalButton>
</li>
);
dropdownContents.push(
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index ff83d0420..7fc0f79cf 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -81,7 +81,7 @@ export default class CommandList extends React.Component {
<div
ref='mentionlist'
className='command-box'
- style={{height: (this.state.suggestions.length * 56) + 2}}
+ style={{height: (suggestions.length * 56) + 2}}
>
{suggestions}
</div>
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index fab5b60ea..3c4b17905 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -159,13 +159,4 @@ export default class DeletePostModal extends React.Component {
</Modal>
);
}
-
- static show(post, commentCount) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
- value: true,
- post,
- commentCount: commentCount || 0
- });
- }
}
diff --git a/web/react/components/edit_channel_header_modal.jsx b/web/react/components/edit_channel_header_modal.jsx
new file mode 100644
index 000000000..5529a419d
--- /dev/null
+++ b/web/react/components/edit_channel_header_modal.jsx
@@ -0,0 +1,126 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Client from '../utils/client.jsx';
+import * as AsyncClient from '../utils/async_client.jsx';
+import * as Utils from '../utils/utils.jsx';
+
+const Modal = ReactBootstrap.Modal;
+
+export default class EditChannelHeaderModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleEdit = this.handleEdit.bind(this);
+
+ this.onShow = this.onShow.bind(this);
+ this.onHide = this.onHide.bind(this);
+
+ this.state = {
+ serverError: ''
+ };
+ }
+
+ componentDidMount() {
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+
+ handleEdit() {
+ var data = {};
+ data.channel_id = this.props.channel.id;
+
+ if (data.channel_id.length !== 26) {
+ return;
+ }
+
+ data.channel_header = ReactDOM.findDOMNode(this.refs.textarea).value;
+
+ Client.updateChannelHeader(data,
+ () => {
+ this.setState({serverError: ''});
+ AsyncClient.getChannel(this.props.channel.id);
+ this.onHide();
+ },
+ (err) => {
+ if (err.message === 'Invalid channel_header parameter') {
+ this.setState({serverError: 'This channel header is too long, please enter a shorter one'});
+ } else {
+ this.setState({serverError: err.message});
+ }
+ }
+ );
+ }
+
+ onShow() {
+ const textarea = ReactDOM.findDOMNode(this.refs.textarea);
+ Utils.placeCaretAtEnd(textarea);
+ }
+
+ onHide() {
+ this.setState({
+ serverError: ''
+ });
+
+ this.props.onHide();
+ }
+
+ render() {
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ return (
+ <Modal
+ show={this.props.show}
+ onHide={this.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {'Edit Header for ' + this.props.channel.display_name}
+ </Modal.Header>
+ <Modal.Body>
+ <p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
+ <textarea
+ ref='textarea'
+ className='form-control no-resize'
+ rows='6'
+ id='edit_header'
+ maxLength='1024'
+ defaultValue={this.props.channel.header}
+ />
+ {serverError}
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.props.onHide}
+ >
+ {'Cancel'}
+ </button>
+ <button
+ type='button'
+ className='btn btn-primary'
+ onClick={this.handleEdit}
+ >
+ {'Save'}
+ </button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+EditChannelHeaderModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired,
+ channel: React.PropTypes.object.isRequired
+};
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
deleted file mode 100644
index 80dab4a57..000000000
--- a/web/react/components/edit_channel_modal.jsx
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-export default class EditChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleEdit = this.handleEdit.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleClose = this.handleClose.bind(this);
- this.onShow = this.onShow.bind(this);
- this.handleShown = this.handleShown.bind(this);
-
- this.state = {
- header: '',
- title: '',
- channelId: '',
- serverError: ''
- };
- }
- handleEdit() {
- var data = {};
- data.channel_id = this.state.channelId;
-
- if (data.channel_id.length !== 26) {
- return;
- }
-
- data.channel_header = this.state.header.trim();
-
- Client.updateChannelHeader(data,
- () => {
- this.setState({serverError: ''});
- AsyncClient.getChannel(this.state.channelId);
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- },
- (err) => {
- if (err.message === 'Invalid channel_header parameter') {
- this.setState({serverError: 'This channel header is too long, please enter a shorter one'});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
- handleUserInput(e) {
- this.setState({header: e.target.value});
- }
- handleClose() {
- this.setState({header: '', serverError: ''});
- }
- onShow(e) {
- const button = e.relatedTarget;
- this.setState({header: $(button).attr('data-header'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
- }
- handleShown() {
- $('#edit_channel #edit_header').focus();
- }
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.handleShown);
- }
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
- }
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var editTitle = (
- <h4
- className='modal-title'
- ref='title'
- >
- {'Edit Header'}
- </h4>
- );
- if (this.state.title) {
- editTitle = (
- <h4
- className='modal-title'
- ref='title'
- >
- {'Edit Header for '}<span className='name'>{this.state.title}</span>
- </h4>
- );
- }
-
- return (
- <div
- className='modal fade'
- ref='modal'
- id='edit_channel'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>{'×'}</span>
- </button>
- {editTitle}
- </div>
- <div className='modal-body'>
- <p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
- <textarea
- className='form-control no-resize'
- rows='6'
- id='edit_header'
- maxLength='1024'
- value={this.state.header}
- onChange={this.handleUserInput}
- />
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- {'Cancel'}
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleEdit}
- >
- {'Save'}
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index ddbdee8a4..eb58fe721 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -3,7 +3,7 @@
import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Textbox from './textbox.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import PostStore from '../stores/post_store.jsx';
@@ -35,7 +35,7 @@ export default class EditPostModal extends React.Component {
delete tempState.editText;
BrowserStore.setItem('edit_state_transfer', tempState);
$('#edit_post').modal('hide');
- DeletePostModal.show(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
+ EventHelpers.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
return;
}
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index 2bd2c42d6..df5d6b8e1 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -1,32 +1,28 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import UserStore from '../stores/user_store.jsx';
+const Modal = ReactBootstrap.Modal;
export default class GetLinkModal extends React.Component {
constructor(props) {
super(props);
- this.handleClick = this.handleClick.bind(this);
- this.onShow = this.onShow.bind(this);
this.onHide = this.onHide.bind(this);
- this.state = {copiedLink: false};
- }
- onShow(e) {
- var button = e.relatedTarget;
- this.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value')});
+ this.copyLink = this.copyLink.bind(this);
+
+ this.state = {
+ copiedLink: false
+ };
}
+
onHide() {
this.setState({copiedLink: false});
+
+ this.props.onHide();
}
- componentDidMount() {
- if (this.refs.modal) {
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', this.onHide);
- }
- }
- handleClick() {
+
+ copyLink() {
var copyTextarea = $(ReactDOM.findDOMNode(this.refs.textarea));
copyTextarea.select();
@@ -41,8 +37,18 @@ export default class GetLinkModal extends React.Component {
this.setState({copiedLink: false});
}
}
+
render() {
- var currentUser = UserStore.getCurrentUser();
+ let helpText = null;
+ if (this.props.helpText) {
+ helpText = (
+ <p>
+ {this.props.helpText}
+ <br />
+ <br />
+ </p>
+ );
+ }
let copyLink = null;
if (document.queryCommandSupported('copy')) {
@@ -51,75 +57,59 @@ export default class GetLinkModal extends React.Component {
data-copy-btn='true'
type='button'
className='btn btn-primary pull-left'
- onClick={this.handleClick}
- data-clipboard-text={this.state.value}
+ onClick={this.copyLink}
>
- Copy Link
+ {'Copy Link'}
</button>
);
}
var copyLinkConfirm = null;
if (this.state.copiedLink) {
- copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i> Link copied to clipboard.</p>;
+ copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i>{' Link copied to clipboard.'}</p>;
}
- if (currentUser != null) {
- return (
- <div
- className='modal fade'
- ref='modal'
- id='get_link'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- id='myModalLabel'
- >
- {this.state.title} Link
- </h4>
- </div>
- <div className='modal-body'>
- <p>
- Send teammates the link below for them to sign-up to this team site.
- <br /><br />
- </p>
- <textarea
- className='form-control no-resize min-height'
- readOnly='true'
- ref='textarea'
- value={this.state.value}
- />
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- Close
- </button>
- {copyLink}
- {copyLinkConfirm}
- </div>
- </div>
- </div>
- </div>
- );
- }
- return <div/>;
+ return (
+ <Modal
+ show={this.props.show}
+ onHide={this.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {this.props.title}
+ </Modal.Header>
+ <Modal.Body>
+ {helpText}
+ <textarea
+ className='form-control no-resize min-height'
+ readOnly='true'
+ ref='textarea'
+ value={this.props.link}
+ />
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.onHide}
+ >
+ {'Close'}
+ </button>
+ {copyLink}
+ {copyLinkConfirm}
+ </Modal.Footer>
+ </Modal>
+ );
}
}
+
+GetLinkModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired,
+ title: React.PropTypes.string.isRequired,
+ helpText: React.PropTypes.string,
+ link: React.PropTypes.string.isRequired
+};
+
+GetLinkModal.defaultProps = {
+ helpText: null
+};
diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx
new file mode 100644
index 000000000..a926c4451
--- /dev/null
+++ b/web/react/components/get_team_invite_link_modal.jsx
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Constants from '../utils/constants.jsx';
+import GetLinkModal from './get_link_modal.jsx';
+import ModalStore from '../stores/modal_store.jsx';
+import TeamStore from '../stores/team_store.jsx';
+
+export default class GetTeamInviteLinkModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleToggle = this.handleToggle.bind(this);
+
+ this.state = {
+ show: false
+ };
+ }
+
+ componentDidMount() {
+ ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
+ }
+
+ componentWillUnmount() {
+ ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
+ }
+
+ handleToggle(value) {
+ this.setState({
+ show: value
+ });
+ }
+
+ render() {
+ return (
+ <GetLinkModal
+ show={this.state.show}
+ onHide={() => this.setState({show: false})}
+ title='Team Invite Link'
+ helpText='Send teammates the link below for them to sign-up to this team site.'
+ link={TeamStore.getCurrentInviteLink()}
+ />
+ );
+ }
+}
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 7df75252e..76f52faa9 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,8 +4,8 @@
import * as utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Client from '../utils/client.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import ModalStore from '../stores/modal_store.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
@@ -23,6 +23,7 @@ export default class InviteMemberModal extends React.Component {
this.addInviteFields = this.addInviteFields.bind(this);
this.clearFields = this.clearFields.bind(this);
this.removeInviteFields = this.removeInviteFields.bind(this);
+ this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
this.state = {
show: false,
@@ -188,6 +189,12 @@ export default class InviteMemberModal extends React.Component {
this.setState({inviteIds: inviteIds, idCount: count});
}
+ showGetTeamInviteLinkModal() {
+ this.handleHide(false);
+
+ EventHelpers.showGetTeamInviteLinkModal();
+ }
+
render() {
var currentUser = UserStore.getCurrentUser();
@@ -333,22 +340,18 @@ export default class InviteMemberModal extends React.Component {
} else {
var teamInviteLink = null;
if (currentUser && TeamStore.getCurrent().type === 'O') {
- var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id;
- var link =
- (
- <a
- href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={linkUrl}
- onClick={() => this.handleHide(this, false)}
- >Team Invite Link</a>
+ var link = (
+ <a
+ href='#'
+ onClick={this.showGetTeamInviteLinkModal}
+ >
+ {'Team Invite Link'}
+ </a>
);
teamInviteLink = (
<p>
- You can also invite people using the {link}.
+ {'You can also invite people using the '}{link}{'.'}
</p>
);
}
@@ -405,13 +408,6 @@ export default class InviteMemberModal extends React.Component {
return null;
}
-
- static show() {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL,
- value: true
- });
- }
}
InviteMemberModal.propTypes = {
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index 72fdb7be9..f1c31131f 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -2,17 +2,56 @@
// See License.txt for license information.
import MemberListTeamItem from './member_list_team_item.jsx';
+import UserStore from '../stores/user_store.jsx';
export default class MemberListTeam extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getUsers = this.getUsers.bind(this);
+ this.onChange = this.onChange.bind(this);
+
+ this.state = {
+ users: this.getUsers()
+ };
+ }
+
+ componentDidMount() {
+ UserStore.addChangeListener(this.onChange);
+ }
+
+ componentWillUnmount() {
+ UserStore.removeChangeListener(this.onChange);
+ }
+
+ getUsers() {
+ const profiles = UserStore.getProfiles();
+ const users = [];
+
+ for (const id of Object.keys(profiles)) {
+ users.push(profiles[id]);
+ }
+
+ users.sort((a, b) => a.username.localeCompare(b.username));
+
+ return users;
+ }
+
+ onChange() {
+ this.setState({
+ users: this.getUsers()
+ });
+ }
+
render() {
- const memberList = this.props.users.map(function makeListItem(user) {
+ const memberList = this.state.users.map((user) => {
return (
<MemberListTeamItem
key={user.id}
user={user}
/>
);
- }, this);
+ });
return (
<table className='table more-table member-list-holder'>
@@ -23,7 +62,3 @@ export default class MemberListTeam extends React.Component {
);
}
}
-
-MemberListTeam.propTypes = {
- users: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
-};
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 6848ee5da..03cc75a08 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
import MessageWrapper from './message_wrapper.jsx';
import NotifyCounts from './notify_counts.jsx';
@@ -33,11 +34,15 @@ export default class Navbar extends React.Component {
this.onChange = this.onChange.bind(this);
this.handleLeave = this.handleLeave.bind(this);
this.showSearch = this.showSearch.bind(this);
+
+ this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
+
this.createCollapseButtons = this.createCollapseButtons.bind(this);
this.createDropdown = this.createDropdown.bind(this);
const state = this.getStateFromStores();
state.showEditChannelPurposeModal = false;
+ state.showEditChannelHeaderModal = false;
state.showMembersModal = false;
state.showInviteModal = false;
this.state = state;
@@ -110,6 +115,16 @@ export default class Navbar extends React.Component {
this.setState(this.getStateFromStores());
$('#navbar .navbar-brand .description').popover({placement: 'bottom', trigger: 'click', html: true});
}
+ showEditChannelHeaderModal() {
+ // this can't be done using a ToggleModalButton because we can't use one inside an OverlayTrigger
+ if (this.refs.headerOverlay) {
+ this.refs.headerOverlay.hide();
+ }
+
+ this.setState({
+ showEditChannelHeaderModal: true
+ });
+ }
createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent) {
if (channel) {
var viewInfoOption = (
@@ -129,11 +144,7 @@ export default class Navbar extends React.Component {
<a
role='menuitem'
href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ onClick={this.showEditChannelHeaderModal}
>
{'Set Channel Header...'}
</a>
@@ -239,7 +250,7 @@ export default class Navbar extends React.Component {
dialogType={ChannelNotificationsModal}
dialogProps={{channel}}
>
- {'Notification Preferences'}
+ {'Notification Preferences'}
</ToggleModalButton>
</li>
);
@@ -249,6 +260,7 @@ export default class Navbar extends React.Component {
<div className='navbar-brand'>
<div className='dropdown'>
<OverlayTrigger
+ ref='headerOverlay'
trigger='click'
placement='bottom'
overlay={popoverContent}
@@ -358,6 +370,9 @@ export default class Navbar extends React.Component {
var isAdmin = false;
var isDirect = false;
+ var editChannelHeaderModal = null;
+ var editChannelPurposeModal = null;
+
if (channel) {
popoverContent = (
<Popover
@@ -400,11 +415,7 @@ export default class Navbar extends React.Component {
<br/>
<a
href='#'
- data-toggle='modal'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- data-target='#edit_channel'
+ onClick={this.showEditChannelHeaderModal}
>
{'Click here'}
</a>
@@ -413,6 +424,22 @@ export default class Navbar extends React.Component {
</Popover>
);
}
+
+ editChannelHeaderModal = (
+ <EditChannelHeaderModal
+ show={this.state.showEditChannelHeaderModal}
+ onHide={() => this.setState({showEditChannelHeaderModal: false})}
+ channel={channel}
+ />
+ );
+
+ editChannelPurposeModal = (
+ <EditChannelPurposeModal
+ show={this.state.showEditChannelPurposeModal}
+ onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
+ channel={channel}
+ />
+ );
}
var collapseButtons = this.createCollapseButtons(currentId);
@@ -443,11 +470,8 @@ export default class Navbar extends React.Component {
</div>
</div>
</nav>
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
+ {editChannelHeaderModal}
+ {editChannelPurposeModal}
<ChannelMembersModal
show={this.state.showMembersModal}
onModalDismissed={() => this.setState({showMembersModal: false})}
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index c0230fe5f..c286ee6f9 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -5,9 +5,11 @@ import * as Utils from '../utils/utils.jsx';
import * as client from '../utils/client.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import AboutBuildModal from './about_build_modal.jsx';
-import InviteMemberModal from './invite_member_modal.jsx';
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
@@ -93,7 +95,7 @@ export default class NavbarDropdown extends React.Component {
<li>
<a
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
{'Invite New Member'}
</a>
@@ -105,10 +107,7 @@ export default class NavbarDropdown extends React.Component {
<li>
<a
href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
>
{'Get Team Invite Link'}
</a>
@@ -120,13 +119,9 @@ export default class NavbarDropdown extends React.Component {
if (isAdmin) {
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
+ <ToggleModalButton dialogType={TeamMembersModal}>
{'Manage Members'}
- </a>
+ </ToggleModalButton>
</li>
);
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index bc6e8470d..cedb2b59b 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -1,11 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import DeletePostModal from './delete_post_modal.jsx';
import UserStore from '../stores/user_store.jsx';
import TeamStore from '../stores/team_store.jsx';
import * as Utils from '../utils/utils.jsx';
import TimeSince from './time_since.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx';
@@ -74,7 +74,7 @@ export default class PostInfo extends React.Component {
<a
href='#'
role='menuitem'
- onClick={() => DeletePostModal.show(post, dataComments)}
+ onClick={() => EventHelpers.showDeletePostModal(post, dataComments)}
>
{'Delete'}
</a>
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 3e555c85a..7aae5177e 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -8,13 +8,13 @@ import UserStore from '../stores/user_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
var ActionTypes = Constants.ActionTypes;
import * as TextFormatting from '../utils/text_formatting.jsx';
import twemoji from 'twemoji';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export default class RhsComment extends React.Component {
constructor(props) {
@@ -115,7 +115,7 @@ export default class RhsComment extends React.Component {
<a
href='#'
role='menuitem'
- onClick={() => DeletePostModal.show(post, 0)}
+ onClick={() => EventHelpers.showDeletePostModal(post, 0)}
>
{'Delete'}
</a>
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 96f43bdb5..3d3d9e13f 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -6,11 +6,11 @@ import UserProfile from './user_profile.jsx';
import UserStore from '../stores/user_store.jsx';
import * as TextFormatting from '../utils/text_formatting.jsx';
import * as utils from '../utils/utils.jsx';
-import DeletePostModal from './delete_post_modal.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
import twemoji from 'twemoji';
import Constants from '../utils/constants.jsx';
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export default class RhsRootPost extends React.Component {
constructor(props) {
@@ -38,7 +38,9 @@ export default class RhsRootPost extends React.Component {
}
render() {
var post = this.props.post;
- var isOwner = UserStore.getCurrentId() === post.user_id;
+ var currentUser = UserStore.getCurrentUser();
+ var isOwner = currentUser.id === post.user_id;
+ var isAdmin = utils.isAdmin(currentUser.roles);
var timestamp = UserStore.getProfile(post.user_id).update_at;
var channel = ChannelStore.get(post.channel_id);
@@ -61,11 +63,54 @@ export default class RhsRootPost extends React.Component {
}
}
- var ownerOptions;
+ var dropdownContents = [];
+
if (isOwner) {
- ownerOptions = (
- <div>
- <a href='#'
+ dropdownContents.push(
+ <li
+ key='rhs-root-edit'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#edit_post'
+ data-refocusid='#reply_textbox'
+ data-title={type}
+ data-message={post.message}
+ data-postid={post.id}
+ data-channelid={post.channel_id}
+ >
+ {'Edit'}
+ </a>
+ </li>
+ );
+ }
+
+ if (isOwner || isAdmin) {
+ dropdownContents.push(
+ <li
+ key='rhs-root-delete'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)}
+ >
+ {'Delete'}
+ </a>
+ </li>
+ );
+ }
+
+ var rootOptions = '';
+ if (dropdownContents.length > 0) {
+ rootOptions = (
+ <div className='dropdown'>
+ <a
+ href='#'
className='post__dropdown dropdown-toggle'
type='button'
data-toggle='dropdown'
@@ -75,30 +120,7 @@ export default class RhsRootPost extends React.Component {
className='dropdown-menu'
role='menu'
>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- {'Edit'}
- </a>
- </li>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- onClick={() => DeletePostModal.show(post, this.props.commentCount)}
- >
- {'Delete'}
- </a>
- </li>
+ {dropdownContents}
</ul>
</div>
);
@@ -166,7 +188,7 @@ export default class RhsRootPost extends React.Component {
</li>
<li className='col col__reply'>
<div className='dropdown'>
- {ownerOptions}
+ {rootOptions}
</div>
</li>
</ul>
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index f6c0c8adb..1f268a2f7 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -1,11 +1,12 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import InviteMemberModal from './invite_member_modal.jsx';
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import UserStore from '../stores/user_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
import * as client from '../utils/client.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import * as utils from '../utils/utils.jsx';
export default class SidebarRightMenu extends React.Component {
@@ -46,7 +47,7 @@ export default class SidebarRightMenu extends React.Component {
<li>
<a
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user'></i>Invite New Member
</a>
@@ -56,12 +57,12 @@ export default class SidebarRightMenu extends React.Component {
if (this.props.teamType === 'O') {
teamLink = (
<li>
- <a href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
- ><i className='fa fa-link'></i>Get Team Invite Link</a>
+ <a
+ href='#'
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
+ >
+ <i className='glyphicon glyphicon-link'></i>{'Get Team Invite Link'}
+ </a>
</li>
);
}
@@ -79,12 +80,9 @@ export default class SidebarRightMenu extends React.Component {
);
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
- <i className='fa fa-users'></i>Manage Members</a>
+ <ToggleModalButton dialogType={TeamMembersModal}>
+ <i className='fa fa-users'></i>{'Manage Members'}
+ </ToggleModalButton>
</li>
);
}
diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx
deleted file mode 100644
index cd0766012..000000000
--- a/web/react/components/team_members.jsx
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import MemberListTeam from './member_list_team.jsx';
-import * as utils from '../utils/utils.jsx';
-
-function getStateFromStores() {
- var users = UserStore.getProfiles();
- var memberList = [];
- for (var id in users) {
- if (users.hasOwnProperty(id)) {
- memberList.push(users[id]);
- }
- }
-
- memberList.sort(function sort(a, b) {
- if (a.username < b.username) {
- return -1;
- }
-
- if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- });
-
- return {
- member_list: memberList
- };
-}
-
-export default class TeamMembers extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = getStateFromStores();
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
-
- var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function show() {
- self.setState({render_members: false});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function hide() {
- self.setState({render_members: true});
- });
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
-
- onChange() {
- var newState = getStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
-
- render() {
- var serverError = null;
-
- if (this.state.server_error) {
- serverError = <label className='has-error control-label'>{this.state.server_error}</label>;
- }
-
- var renderMembers = '';
-
- if (this.state.render_members) {
- renderMembers = <MemberListTeam users={this.state.member_list} />;
- }
-
- return (
- <div
- className='modal fade more-modal'
- ref='modal'
- id='team_members'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>×</span>
- </button>
- <h4
- className='modal-title'
- id='myModalLabel'
- >{this.props.teamDisplayName + ' Members'}</h4>
- </div>
- <div
- ref='modalBody'
- className='modal-body'
- >
- <div className='team-member-list'>
- {renderMembers}
- </div>
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >Close</button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamMembers.propTypes = {
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
new file mode 100644
index 000000000..0a30a2202
--- /dev/null
+++ b/web/react/components/team_members_modal.jsx
@@ -0,0 +1,69 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import MemberListTeam from './member_list_team.jsx';
+import TeamStore from '../stores/team_store.jsx';
+
+const Modal = ReactBootstrap.Modal;
+
+export default class TeamMembersModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onShow = this.onShow.bind(this);
+ }
+
+ componentDidMount() {
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+
+ onShow() {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300);
+ if ($(window).width() > 768) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ }
+ }
+
+ render() {
+ const team = TeamStore.getCurrent();
+
+ return (
+ <Modal
+ dialogClassName='team-members-modal'
+ show={this.props.show}
+ onHide={this.props.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {team.display_name + ' Members'}
+ </Modal.Header>
+ <Modal.Body ref='modalBody'>
+ <div className='team-member-list'>
+ <MemberListTeam />
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.props.onHide}
+ >
+ {'Close'}
+ </button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+TeamMembersModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/toggle_modal_button.jsx b/web/react/components/toggle_modal_button.jsx
index eae4a024d..ce8ff3f60 100644
--- a/web/react/components/toggle_modal_button.jsx
+++ b/web/react/components/toggle_modal_button.jsx
@@ -22,7 +22,17 @@ export default class ModalToggleButton extends React.Component {
}
render() {
- const {children, dialogType, dialogProps, ...props} = this.props; //eslint-disable-line no-redeclare
+ const {children, dialogType, dialogProps, onClick, ...props} = this.props; // eslint-disable-line no-redeclare
+
+ // allow callers to provide an onClick which will be called before the modal is shown
+ let clickHandler = this.show;
+ if (onClick) {
+ clickHandler = () => {
+ onClick();
+
+ this.show();
+ };
+ }
// this assumes that all modals will have a show property and an onHide event
const dialog = React.createElement(this.props.dialogType, Object.assign({}, dialogProps, {
@@ -42,7 +52,7 @@ export default class ModalToggleButton extends React.Component {
<a
{...props}
href='#'
- onClick={this.show}
+ onClick={clickHandler}
>
{children}
{dialog}
@@ -54,7 +64,8 @@ export default class ModalToggleButton extends React.Component {
ModalToggleButton.propTypes = {
children: React.PropTypes.node.isRequired,
dialogType: React.PropTypes.func.isRequired,
- dialogProps: React.PropTypes.object
+ dialogProps: React.PropTypes.object,
+ onClick: React.PropTypes.func
};
ModalToggleButton.defaultProps = {
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index b3ec7ddd7..962efd7a2 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -438,12 +438,12 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'email') {
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
- let helpText = 'Email is used for notifications, and requires verification if changed.';
+ let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
if (!emailEnabled) {
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
} else if (!emailVerificationEnabled) {
- helpText = 'Email is used for notifications.';
+ helpText = 'Email is used for sign-in, notifications, and password reset.';
} else if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
diff --git a/web/react/dispatcher/event_helpers.jsx b/web/react/dispatcher/event_helpers.jsx
index 85329b38f..d7f255aaa 100644
--- a/web/react/dispatcher/event_helpers.jsx
+++ b/web/react/dispatcher/event_helpers.jsx
@@ -81,3 +81,26 @@ export function emitPostDeletedEvent(post) {
post
});
}
+
+export function showDeletePostModal(post, commentCount = 0) {
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
+ value: true,
+ post,
+ commentCount
+ });
+}
+
+export function showGetTeamInviteLinkModal() {
+ AppDispatcher.handleViewAction({
+ type: Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL,
+ value: true
+ });
+}
+
+export function showInviteMemberModal() {
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL,
+ value: true
+ });
+}
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 5cc1be741..b73dfdafe 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -7,15 +7,13 @@ import ErrorBar from '../components/error_bar.jsx';
import ErrorStore from '../stores/error_store.jsx';
import MentionList from '../components/mention_list.jsx';
-import GetLinkModal from '../components/get_link_modal.jsx';
-import EditChannelModal from '../components/edit_channel_modal.jsx';
+import GetTeamInviteLinkModal from '../components/get_team_invite_link_modal.jsx';
import RenameChannelModal from '../components/rename_channel_modal.jsx';
import EditPostModal from '../components/edit_post_modal.jsx';
import DeletePostModal from '../components/delete_post_modal.jsx';
import MoreChannelsModal from '../components/more_channels.jsx';
import PostDeletedModal from '../components/post_deleted_modal.jsx';
import TeamSettingsModal from '../components/team_settings_modal.jsx';
-import TeamMembersModal from '../components/team_members.jsx';
import RemovedFromChannelModal from '../components/removed_from_channel_modal.jsx';
import RegisterAppModal from '../components/register_app_modal.jsx';
import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
@@ -68,8 +66,8 @@ function setupChannelPage(props, team, channel) {
// Modals
//
ReactDOM.render(
- <GetLinkModal />,
- document.getElementById('get_link_modal')
+ <GetTeamInviteLinkModal />,
+ document.getElementById('get_team_invite_link_modal')
);
ReactDOM.render(
@@ -88,16 +86,6 @@ function setupChannelPage(props, team, channel) {
);
ReactDOM.render(
- <TeamMembersModal teamDisplayName={props.TeamDisplayName} />,
- document.getElementById('team_members_modal')
- );
-
- ReactDOM.render(
- <EditChannelModal />,
- document.getElementById('edit_channel_modal')
- );
-
- ReactDOM.render(
<RenameChannelModal />,
document.getElementById('rename_channel_modal')
);
diff --git a/web/react/stores/modal_store.jsx b/web/react/stores/modal_store.jsx
index 69f43a5cf..a26a97f53 100644
--- a/web/react/stores/modal_store.jsx
+++ b/web/react/stores/modal_store.jsx
@@ -34,6 +34,7 @@ class ModalStoreClass extends EventEmitter {
case ActionTypes.TOGGLE_IMPORT_THEME_MODAL:
case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL:
case ActionTypes.TOGGLE_DELETE_POST_MODAL:
+ case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL:
this.emit(type, value, args);
break;
}
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
index 26c83cc8c..2d518d9e7 100644
--- a/web/react/stores/team_store.jsx
+++ b/web/react/stores/team_store.jsx
@@ -31,6 +31,7 @@ class TeamStoreClass extends EventEmitter {
this.getCurrentId = this.getCurrentId.bind(this);
this.getCurrent = this.getCurrent.bind(this);
this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this);
+ this.getCurrentInviteLink = this.getCurrentInviteLink.bind(this);
this.saveTeam = this.saveTeam.bind(this);
}
@@ -92,6 +93,16 @@ class TeamStoreClass extends EventEmitter {
return null;
}
+ getCurrentInviteLink() {
+ const current = this.getCurrent();
+
+ if (current) {
+ return getWindowLocationOrigin() + '/signup_user_complete/?id=' + current.invite_id;
+ }
+
+ return '';
+ }
+
saveTeam(team) {
var teams = this.getAll();
teams[team.id] = team;
diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx
index 0bbc7366e..6f83778c9 100644
--- a/web/react/utils/channel_intro_mssages.jsx
+++ b/web/react/utils/channel_intro_mssages.jsx
@@ -1,13 +1,14 @@
-
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from './utils.jsx';
-import InviteMemberModal from '../components/invite_member_modal.jsx';
+import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx';
+import ToggleModalButton from '../components/toggle_modal_button.jsx';
import UserProfile from '../components/user_profile.jsx';
import ChannelStore from '../stores/channel_store.jsx';
import Constants from '../utils/constants.jsx';
import TeamStore from '../stores/team_store.jsx';
+import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export function createChannelIntroMessage(channel, showInviteModal) {
if (channel.type === 'D') {
@@ -49,17 +50,7 @@ export function createDMIntroMessage(channel) {
{'This is the start of your direct message history with ' + teammateName + '.'}<br/>
{'Direct messages and files shared here are not shown to people outside this area.'}
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
</div>
);
}
@@ -79,17 +70,7 @@ export function createOffTopicIntroMessage(channel, showInviteModal) {
{'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
<br/>
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<a
href='#'
className='intro-links'
@@ -109,7 +90,7 @@ export function createDefaultIntroMessage(channel) {
<a
className='intro-links'
href='#'
- onClick={InviteMemberModal.show}
+ onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
</a>
@@ -119,10 +100,7 @@ export function createDefaultIntroMessage(channel) {
<a
className='intro-links'
href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
+ onClick={EventHelpers.showGetTeamInviteLinkModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
</a>
@@ -138,17 +116,7 @@ export function createDefaultIntroMessage(channel) {
{'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
</p>
{inviteModalLink}
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<br/>
</div>
);
@@ -193,17 +161,7 @@ export function createStandardIntroMessage(channel, showInviteModal) {
{memberMessage}
<br/>
</p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ {createSetHeaderButton(channel)}
<a
className='intro-links'
href='#'
@@ -214,3 +172,15 @@ export function createStandardIntroMessage(channel, showInviteModal) {
</div>
);
}
+
+function createSetHeaderButton(channel) {
+ return (
+ <ToggleModalButton
+ className='intro-links'
+ dialogType={EditChannelHeaderModal}
+ dialogProps={{channel}}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </ToggleModalButton>
+ );
+}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 1ac9a1b98..6281813e9 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -48,7 +48,8 @@ export default {
TOGGLE_IMPORT_THEME_MODAL: null,
TOGGLE_INVITE_MEMBER_MODAL: null,
- TOGGLE_DELETE_POST_MODAL: null
+ TOGGLE_DELETE_POST_MODAL: null,
+ TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null
}),
PayloadSources: keyMirror({
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 764bdf763..9b2f7e057 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -749,19 +749,10 @@ export function updateCodeTheme(theme) {
export function placeCaretAtEnd(el) {
el.focus();
- if (typeof window.getSelection != 'undefined' && typeof document.createRange != 'undefined') {
- var range = document.createRange();
- range.selectNodeContents(el);
- range.collapse(false);
- var sel = window.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
- } else if (typeof document.body.createTextRange != 'undefined') {
- var textRange = document.body.createTextRange();
- textRange.moveToElementText(el);
- textRange.collapse(false);
- textRange.select();
- }
+ el.selectionStart = el.value.length;
+ el.selectionEnd = el.value.length;
+
+ return;
}
export function getCaretPosition(el) {