diff options
Diffstat (limited to 'web/react/components')
25 files changed, 517 insertions, 484 deletions
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index ab5686720..65b80dfb7 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -14,8 +14,8 @@ export default class AccessHistoryModal extends React.Component { this.onAuditChange = this.onAuditChange.bind(this); this.handleMoreInfo = this.handleMoreInfo.bind(this); - this.onHide = this.onHide.bind(this); this.onShow = this.onShow.bind(this); + this.onHide = this.onHide.bind(this); this.formatAuditInfo = this.formatAuditInfo.bind(this); this.handleRevokedSession = this.handleRevokedSession.bind(this); @@ -39,10 +39,14 @@ export default class AccessHistoryModal extends React.Component { } onHide() { this.setState({moreInfo: []}); - this.props.onModalDismissed(); + this.props.onHide(); } componentDidMount() { UserStore.addAuditsChangeListener(this.onAuditChange); + + if (this.props.show) { + this.onShow(); + } } componentDidUpdate(prevProps) { if (this.props.show && !prevProps.show) { @@ -406,5 +410,5 @@ export default class AccessHistoryModal extends React.Component { AccessHistoryModal.propTypes = { show: React.PropTypes.bool.isRequired, - onModalDismissed: React.PropTypes.func.isRequired + onHide: React.PropTypes.func.isRequired }; diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx index af423a601..5824ce7e2 100644 --- a/web/react/components/activity_log_modal.jsx +++ b/web/react/components/activity_log_modal.jsx @@ -58,10 +58,14 @@ export default class ActivityLogModal extends React.Component { } onHide() { this.setState({moreInfo: []}); - this.props.onModalDismissed(); + this.props.onHide(); } componentDidMount() { UserStore.addSessionsChangeListener(this.onListenerChange); + + if (this.props.show) { + this.onShow(); + } } componentDidUpdate(prevProps) { if (this.props.show && !prevProps.show) { @@ -178,5 +182,5 @@ export default class ActivityLogModal extends React.Component { ActivityLogModal.propTypes = { show: React.PropTypes.bool.isRequired, - onModalDismissed: React.PropTypes.func.isRequired + onHide: React.PropTypes.func.isRequired }; diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index a8d4ec100..ffe7cbb5d 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -5,8 +5,12 @@ const NavbarSearchBox = require('./search_bar.jsx'); const MessageWrapper = require('./message_wrapper.jsx'); const PopoverListMembers = require('./popover_list_members.jsx'); const EditChannelPurposeModal = require('./edit_channel_purpose_modal.jsx'); +const ChannelInfoModal = require('./channel_info_modal.jsx'); const ChannelInviteModal = require('./channel_invite_modal.jsx'); const ChannelMembersModal = require('./channel_members_modal.jsx'); +const ChannelNotificationsModal = require('./channel_notifications_modal.jsx'); +const DeleteChannelModal = require('./delete_channel_modal.jsx'); +const ToggleModalButton = require('./toggle_modal_button.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); const UserStore = require('../stores/user_store.jsx'); @@ -180,15 +184,13 @@ export default class ChannelHeader extends React.Component { key='view_info' role='presentation' > - <a + <ToggleModalButton role='menuitem' - data-toggle='modal' - data-target='#channel_info' - data-channelid={channel.id} - href='#' + dialogType={ChannelInfoModal} + dialogProps={{channel}} > {'View Info'} - </a> + </ToggleModalButton> </li> ); @@ -263,58 +265,55 @@ export default class ChannelHeader extends React.Component { key='notification_preferences' role='presentation' > - <a + <ToggleModalButton role='menuitem' - href='#' - data-toggle='modal' - data-target='#channel_notifications' - data-title={channel.display_name} - data-channelid={channel.id} + dialogType={ChannelNotificationsModal} + dialogProps={{channel}} > {'Notification Preferences'} - </a> + </ToggleModalButton> </li> ); - if (!ChannelStore.isDefault(channel)) { - if (isAdmin) { - dropdownContents.push( - <li - key='rename_channel' - role='presentation' + if (isAdmin) { + dropdownContents.push( + <li + key='rename_channel' + role='presentation' + > + <a + role='menuitem' + href='#' + data-toggle='modal' + data-target='#rename_channel' + data-display={channel.display_name} + data-name={channel.name} + data-channelid={channel.id} > - <a - role='menuitem' - href='#' - data-toggle='modal' - data-target='#rename_channel' - data-display={channel.display_name} - data-name={channel.name} - data-channelid={channel.id} - > - {'Rename '}{channelTerm}{'...'} - </a> - </li> - ); + {'Rename '}{channelTerm}{'...'} + </a> + </li> + ); + + if (!ChannelStore.isDefault(channel)) { dropdownContents.push( <li key='delete_channel' role='presentation' > - <a + <ToggleModalButton role='menuitem' - href='#' - data-toggle='modal' - data-target='#delete_channel' - data-title={channel.display_name} - data-channelid={channel.id} + dialogType={DeleteChannelModal} + dialogProps={{channel}} > {'Delete '}{channelTerm}{'...'} - </a> + </ToggleModalButton> </li> ); } + } + if (!ChannelStore.isDefault(channel)) { dropdownContents.push( <li key='leave_channel' diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx index bccd8d0b9..18e125de3 100644 --- a/web/react/components/channel_info_modal.jsx +++ b/web/react/components/channel_info_modal.jsx @@ -1,88 +1,57 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var ChannelStore = require('../stores/channel_store.jsx'); - -export default class CommandList extends React.Component { - constructor(props) { - super(props); - - this.state = { - channel_id: ChannelStore.getCurrentId() - }; - } - - componentDidMount() { - var self = this; - if (this.refs.modal) { - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) { - var button = e.relatedTarget; - self.setState({channel_id: $(button).attr('data-channelid')}); - }); - } - } +const Modal = ReactBootstrap.Modal; +export default class ChannelInfoModal extends React.Component { render() { - var channel = ChannelStore.get(this.state.channel_id); - + let channel = this.props.channel; if (!channel) { - channel = {}; - channel.display_name = 'No Channel Found'; - channel.name = 'No Channel Found'; - channel.id = 'No Channel Found'; + channel = { + display_name: 'No Channel Found', + name: 'No Channel Found', + id: 'No Channel Found' + }; } return ( - <div - className='modal fade' - ref='modal' - id='channel_info' - tabIndex='-1' - role='dialog' - aria-hidden='true' + <Modal + show={this.props.show} + onHide={this.props.onHide} > - <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' - > - <span className='name'>{channel.display_name}</span> - </h4> - </div> - <div className='modal-body'> - <div className='row form-group'> - <div className='col-sm-3 info__label'>Channel Name: </div> + <Modal.Header closeButtton={true}> + {channel.display_name} + </Modal.Header> + <Modal.Body ref='modalBody'> + <div className='row form-group'> + <div className='col-sm-3 info__label'>{'Channel Name:'}</div> <div className='col-sm-9'>{channel.display_name}</div> - </div> - <div className='row form-group'> - <div className='col-sm-3 info__label'>Channel Handle:</div> + </div> + <div className='row form-group'> + <div className='col-sm-3 info__label'>{'Channel Handle:'}</div> <div className='col-sm-9'>{channel.name}</div> - </div> - <div className='row'> - <div className='col-sm-3 info__label'>Channel ID:</div> - <div className='col-sm-9'>{channel.id}</div> - </div> </div> - <div className='modal-footer'> - <button - type='button' - className='btn btn-default' - data-dismiss='modal' - >Close</button> + <div className='row'> + <div className='col-sm-3 info__label'>{'Channel ID:'}</div> + <div className='col-sm-9'>{channel.id}</div> </div> - </div> - </div> - </div> + </Modal.Body> + <Modal.Footer> + <button + type='button' + className='btn btn-default' + onClick={this.props.onHide} + > + {'Close'} + </button> + </Modal.Footer> + </Modal> ); } } + +ChannelInfoModal.propTypes = { + show: React.PropTypes.bool.isRequired, + onHide: React.PropTypes.func.isRequired, + channel: React.PropTypes.object.isRequired +}; diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications_modal.jsx index f57fc12c5..c8bd1c2dc 100644 --- a/web/react/components/channel_notifications.jsx +++ b/web/react/components/channel_notifications_modal.jsx @@ -1,15 +1,15 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +var Modal = ReactBootstrap.Modal; var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); -var Utils = require('../utils/utils.jsx'); var Client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); -export default class ChannelNotifications extends React.Component { +export default class ChannelNotificationsModal extends React.Component { constructor(props) { super(props); @@ -23,35 +23,17 @@ export default class ChannelNotifications extends React.Component { this.handleSubmitMarkUnreadLevel = this.handleSubmitMarkUnreadLevel.bind(this); this.handleUpdateMarkUnreadLevel = this.handleUpdateMarkUnreadLevel.bind(this); this.createMarkUnreadLevelSection = this.createMarkUnreadLevelSection.bind(this); - this.onShow = this.onShow.bind(this); + const member = ChannelStore.getMember(props.channel.id); this.state = { - notifyLevel: '', - markUnreadLevel: '', - title: '', + notifyLevel: member.notify_props.desktop, + markUnreadLevel: member.notify_props.mark_unread, channelId: '', activeSection: '' }; } - onShow(e) { - var button = e.relatedTarget; - var channelId = button.getAttribute('data-channelid'); - - const member = ChannelStore.getMember(channelId); - var notifyLevel = member.notify_props.desktop; - var markUnreadLevel = member.notify_props.mark_unread; - - this.setState({ - notifyLevel, - markUnreadLevel, - title: button.getAttribute('data-title'), - channelId - }); - } componentDidMount() { ChannelStore.addChangeListener(this.onListenerChange); - - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow); } componentWillUnmount() { ChannelStore.removeChangeListener(this.onListenerChange); @@ -62,15 +44,12 @@ export default class ChannelNotifications extends React.Component { } const member = ChannelStore.getMember(this.state.channelId); - var notifyLevel = member.notify_props.desktop; - var markUnreadLevel = member.notify_props.mark_unread; - var newState = this.state; - newState.notifyLevel = notifyLevel; - newState.markUnreadLevel = markUnreadLevel; - - if (!Utils.areObjectsEqual(this.state, newState)) { - this.setState(newState); + if (member.notify_props.desktop !== this.state.notifyLevel || member.notify_props.mark_unread !== this.state.mark_unread) { + this.setState({ + notifyLevel: member.notify_props.desktop, + markUnreadLevel: member.notify_props.mark_unread + }); } } updateSection(section) { @@ -104,7 +83,6 @@ export default class ChannelNotifications extends React.Component { } handleUpdateNotifyLevel(notifyLevel) { this.setState({notifyLevel}); - ReactDOM.findDOMNode(this.refs.modal).focus(); } createNotifyLevelSection(serverError) { var handleUpdateSection; @@ -262,7 +240,6 @@ export default class ChannelNotifications extends React.Component { handleUpdateMarkUnreadLevel(markUnreadLevel) { this.setState({markUnreadLevel}); - ReactDOM.findDOMNode(this.refs.modal).focus(); } createMarkUnreadLevelSection(serverError) { @@ -347,48 +324,39 @@ export default class ChannelNotifications extends React.Component { } return ( - <div - className='modal fade' - id='channel_notifications' - ref='modal' - tabIndex='-1' - role='dialog' - aria-hidden='true' + <Modal + show={this.props.show} + dialogClassName='settings-modal' + onHide={this.props.onHide} > - <div className='modal-dialog settings-modal'> - <div className='modal-content'> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' + <Modal.Header closeButton={true}> + {'Notification Preferences for '}<span className='name'>{this.props.channel.display_name}</span> + </Modal.Header> + <Modal.Body> + <div className='settings-table'> + <div className='settings-content'> + <div + ref='wrapper' + className='user-settings' > - <span aria-hidden='true'>×</span> - <span className='sr-only'>{'Close'}</span> - </button> - <h4 className='modal-title'>Notification Preferences for <span className='name'>{this.state.title}</span></h4> - </div> - <div className='modal-body'> - <div className='settings-table'> - <div className='settings-content'> - <div - ref='wrapper' - className='user-settings' - > - <br/> - <div className='divider-dark first'/> - {this.createNotifyLevelSection(serverError)} - <div className='divider-light'/> - {this.createMarkUnreadLevelSection(serverError)} - <div className='divider-dark'/> - </div> + <br/> + <div className='divider-dark first'/> + {this.createNotifyLevelSection(serverError)} + <div className='divider-light'/> + {this.createMarkUnreadLevelSection(serverError)} + <div className='divider-dark'/> </div> - </div> - {serverError} </div> </div> - </div> - </div> + {serverError} + </Modal.Body> + </Modal> ); } } + +ChannelNotificationsModal.propTypes = { + show: React.PropTypes.bool.isRequired, + onHide: React.PropTypes.func.isRequired, + channel: React.PropTypes.object.isRequired +}; diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 058594165..22a659ed5 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -194,7 +194,8 @@ export default class CreateComment extends React.Component { title: 'Comment', message: lastPost.message, postId: lastPost.id, - channelId: lastPost.channel_id + channelId: lastPost.channel_id, + comments: PostStore.getCommentCount(lastPost) }); } } diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 5a69c9bfb..6f25ef608 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -372,7 +372,8 @@ export default class CreatePost extends React.Component { title: type, message: lastPost.message, postId: lastPost.id, - channelId: lastPost.channel_id + channelId: lastPost.channel_id, + comments: PostStore.getCommentCount(lastPost) }); } } diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx index b7d633b38..271f21c3a 100644 --- a/web/react/components/delete_channel_modal.jsx +++ b/web/react/components/delete_channel_modal.jsx @@ -1,102 +1,72 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -const Client = require('../utils/client.jsx'); const AsyncClient = require('../utils/async_client.jsx'); -const ChannelStore = require('../stores/channel_store.jsx'); -var TeamStore = require('../stores/team_store.jsx'); +const Client = require('../utils/client.jsx'); +const Modal = ReactBootstrap.Modal; +const TeamStore = require('../stores/team_store.jsx'); +const Utils = require('../utils/utils.jsx'); export default class DeleteChannelModal extends React.Component { constructor(props) { super(props); this.handleDelete = this.handleDelete.bind(this); - this.onShow = this.onShow.bind(this); - - this.state = { - title: '', - channelId: '' - }; } + handleDelete() { - if (this.state.channelId.length !== 26) { + if (this.props.channel.id.length !== 26) { return; } - Client.deleteChannel(this.state.channelId, - function handleDeleteSuccess() { + Client.deleteChannel( + this.props.channel.id, + () => { AsyncClient.getChannels(true); window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square'; }, - function handleDeleteError(err) { + (err) => { AsyncClient.dispatchError(err, 'handleDelete'); } ); } - onShow(e) { - var button = $(e.relatedTarget); - this.setState({ - title: button.attr('data-title'), - channelId: button.attr('data-channelid') - }); - } - componentDidMount() { - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow); - } + render() { - const channel = ChannelStore.getCurrent(); - let channelType = 'channel'; - if (channel && channel.type === 'P') { - channelType = 'private group'; - } + const channelTerm = Utils.getChannelTerm(this.props.channel.type).toLowerCase(); return ( - <div - className='modal fade' - ref='modal' - id='delete_channel' - role='dialog' - tabIndex='-1' - aria-hidden='true' + <Modal + show={this.props.show} + onHide={this.props.onHide} > - <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'>Confirm DELETE Channel</h4> - </div> - <div className='modal-body'> - <p> - Are you sure you wish to delete the {this.state.title} {channelType}? - </p> - </div> - <div className='modal-footer'> - <button - type='button' - className='btn btn-default' - data-dismiss='modal' - > - Cancel - </button> - <button - type='button' - className='btn btn-danger' - data-dismiss='modal' - onClick={this.handleDelete} - > - Delete - </button> - </div> - </div> - </div> - </div> + <Modal.Header closeButton={true}>{'Confirm DELETE Channel'}</Modal.Header> + <Modal.Body> + {`Are you sure you wish to delete the ${this.props.channel.display_name} ${channelTerm}?`} + </Modal.Body> + <Modal.Footer> + <button + type='button' + className='btn btn-default' + onClick={this.props.onHide} + > + {'Cancel'} + </button> + <button + type='button' + className='btn btn-danger' + data-dismiss='modal' + onClick={this.handleDelete} + > + {'Delete'} + </button> + </Modal.Footer> + </Modal> ); } } + +DeleteChannelModal.propTypes = { + show: React.PropTypes.bool.isRequired, + onHide: React.PropTypes.func.isRequired, + channel: React.PropTypes.object.isRequired +}; diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx index f3bead1c2..e0489856f 100644 --- a/web/react/components/delete_post_modal.jsx +++ b/web/react/components/delete_post_modal.jsx @@ -3,7 +3,8 @@ var Client = require('../utils/client.jsx'); var PostStore = require('../stores/post_store.jsx'); -var BrowserStore = require('../stores/browser_store.jsx'); +var ModalStore = require('../stores/modal_store.jsx'); +var Modal = ReactBootstrap.Modal; var Utils = require('../utils/utils.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); @@ -15,18 +16,40 @@ export default class DeletePostModal extends React.Component { super(props); this.handleDelete = this.handleDelete.bind(this); + this.handleToggle = this.handleToggle.bind(this); + this.handleHide = this.handleHide.bind(this); this.onListenerChange = this.onListenerChange.bind(this); - this.onShow = this.onShow.bind(this); - this.state = {title: '', postId: '', channelId: '', selectedList: PostStore.getSelectedPost(), comments: 0}; + this.selectedList = null; + + this.state = { + show: true, + post: null, + commentCount: 0, + error: '' + }; + } + + componentDidMount() { + ModalStore.addModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle); + PostStore.addSelectedPostChangeListener(this.onListenerChange); + } + + componentWillUnmount() { + PostStore.removeSelectedPostChangeListener(this.onListenerChange); + ModalStore.removeModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle); } + handleDelete() { - Client.deletePost(this.state.channelId, this.state.postId, - function deleteSuccess() { - var selectedList = this.state.selectedList; + Client.deletePost( + this.state.post.channel_id, + this.state.post.id, + () => { + var selectedList = this.selectedList; + if (selectedList && selectedList.order && selectedList.order.length > 0) { var selectedPost = selectedList.posts[selectedList.order[0]]; - if ((selectedPost.id === this.state.postId && this.state.title === 'Post') || selectedPost.root_id === this.state.postId) { + if ((selectedPost.id === this.state.post.id && !this.state.root_id) || selectedPost.root_id === this.state.post.id) { AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_SEARCH, results: null @@ -36,7 +59,7 @@ export default class DeletePostModal extends React.Component { type: ActionTypes.RECIEVED_POST_SELECTED, results: null }); - } else if (selectedPost.id === this.state.postId && this.state.title === 'Comment') { + } else if (selectedPost.id === this.state.post.id && this.state.root_id) { if (selectedPost.root_id && selectedPost.root_id.length > 0 && selectedList.posts[selectedPost.root_id]) { selectedList.order = [selectedPost.root_id]; delete selectedList.posts[selectedPost.id]; @@ -53,98 +76,96 @@ export default class DeletePostModal extends React.Component { } } } - PostStore.removePost(this.state.postId, this.state.channelId); - AsyncClient.getPosts(this.state.channelId); - }.bind(this), - function deleteFailed(err) { + + PostStore.removePost(this.state.post.id, this.state.post.channel_id); + AsyncClient.getPosts(this.state.post.channel_id); + }, + (err) => { AsyncClient.dispatchError(err, 'deletePost'); } ); + + this.handleHide(); } - onShow(e) { - var newState = {}; - if (BrowserStore.getItem('edit_state_transfer')) { - newState = BrowserStore.getItem('edit_state_transfer'); - BrowserStore.removeItem('edit_state_transfer'); - } else { - var button = e.relatedTarget; - newState = {title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), postId: $(button).attr('data-postid'), comments: $(button).attr('data-comments')}; - } - this.setState(newState); - } - componentDidMount() { - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow); - PostStore.addSelectedPostChangeListener(this.onListenerChange); + + handleToggle(value, args) { + this.setState({ + show: value, + post: args.post, + commentCount: args.commentCount, + error: '' + }); } - componentWillUnmount() { - PostStore.removeSelectedPostChangeListener(this.onListenerChange); + + handleHide() { + this.setState({show: false}); } + onListenerChange() { var newList = PostStore.getSelectedPost(); - if (!Utils.areObjectsEqual(this.state.selectedList, newList)) { - this.setState({selectedList: newList}); + if (!Utils.areObjectsEqual(this.selectedList, newList)) { + this.selectedList = newList; } } + render() { + if (!this.state.post) { + return null; + } + var error = null; if (this.state.error) { error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>; } var commentWarning = ''; - if (this.state.comments > 0) { - commentWarning = 'This post has ' + this.state.comments + ' comment(s) on it.'; + if (this.state.commentCount > 0) { + commentWarning = 'This post has ' + this.state.commentCount + ' comment(s) on it.'; } + const postTerm = Utils.getPostTerm(this.state.post); + return ( - <div - className='modal fade' - id='delete_post' - ref='modal' - role='dialog' - tabIndex='-1' - aria-hidden='true' + <Modal + show={this.state.show} + onHide={this.handleHide} > - <div className='modal-dialog modal-push-down'> - <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'>Confirm {this.state.title} Delete</h4> - </div> - <div className='modal-body'> - Are you sure you want to delete the {this.state.title.toLowerCase()}? - <br/> - <br/> + <Modal.Header closeButton={true}> + {`Confirm ${postTerm} Delete`} + </Modal.Header> + <Modal.Body> + {`Are you sure you want to delete this ${postTerm.toLowerCase()}?`} + <br /> + <br /> {commentWarning} - </div> - {error} - <div className='modal-footer'> + {error} + </Modal.Body> + <Modal.Footer> <button type='button' className='btn btn-default' - data-dismiss='modal' + onClick={this.handleHide} > - Cancel + {'Cancel'} </button> <button type='button' className='btn btn-danger' - data-dismiss='modal' onClick={this.handleDelete} > - Delete + {'Delete'} </button> - </div> - </div> - </div> - </div> + </Modal.Footer> + </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/docs.jsx b/web/react/components/docs.jsx new file mode 100644 index 000000000..68baa6dad --- /dev/null +++ b/web/react/components/docs.jsx @@ -0,0 +1,41 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +const TextFormatting = require('../utils/text_formatting.jsx'); +const UserStore = require('../stores/user_store.jsx'); + +export default class Docs extends React.Component { + constructor(props) { + super(props); + UserStore.setCurrentUser(global.window.mm_user || {}); + + this.state = {text: ''}; + const errorState = {text: '## 404'}; + + if (props.site) { + $.get('/static/help/' + props.site + '.md').then((response) => { + this.setState({text: response}); + }, () => { + this.setState(errorState); + }); + } else { + this.setState(errorState); + } + } + + render() { + return ( + <div + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.text)}} + > + </div> + ); + } +} + +Docs.defaultProps = { + site: '' +}; +Docs.propTypes = { + site: React.PropTypes.string +}; diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index ef32baa7d..c75da75c9 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -3,6 +3,7 @@ var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var Textbox = require('./textbox.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); var PostStore = require('../stores/post_store.jsx'); @@ -34,7 +35,7 @@ export default class EditPostModal extends React.Component { delete tempState.editText; BrowserStore.setItem('edit_state_transfer', tempState); $('#edit_post').modal('hide'); - $('#delete_post').modal('show'); + DeletePostModal.show(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments); return; } diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 423ba9067..7f8820d9f 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -201,14 +201,12 @@ export default class Login extends React.Component { if (global.window.mm_config.EnableTeamCreation === 'true') { teamSignUp = ( <div className='margin--extra'> - <span>{'Want to create your own team? '} - <a - href='/' - className='signup-team-login' - > - {'Create one now'} - </a> - </span> + <a + href='/' + className='signup-team-login' + > + {'Create a new team'} + </a> </div> ); } @@ -227,7 +225,7 @@ export default class Login extends React.Component { {emailSignup} {userSignUp} <div className='form-group margin--extra form-group--small'> - <span><a href='/find_team'>{'Find other teams'}</a></span> + <span><a href='/find_team'>{'Find your other teams'}</a></span> </div> {forgotPassword} {teamSignUp} diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index af29f219e..1fcfabccd 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -5,7 +5,11 @@ const EditChannelPurposeModal = require('./edit_channel_purpose_modal.jsx'); const MessageWrapper = require('./message_wrapper.jsx'); const NotifyCounts = require('./notify_counts.jsx'); const ChannelMembersModal = require('./channel_members_modal.jsx'); +const ChannelInfoModal = require('./channel_info_modal.jsx'); const ChannelInviteModal = require('./channel_invite_modal.jsx'); +const ChannelNotificationsModal = require('./channel_notifications_modal.jsx'); +const DeleteChannelModal = require('./delete_channel_modal.jsx'); +const ToggleModalButton = require('./toggle_modal_button.jsx'); const UserStore = require('../stores/user_store.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); @@ -104,15 +108,13 @@ export default class Navbar extends React.Component { if (channel) { var viewInfoOption = ( <li role='presentation'> - <a + <ToggleModalButton role='menuitem' - data-toggle='modal' - data-target='#channel_info' - data-channelid={channel.id} - href='#' + dialogType={ChannelInfoModal} + dialogProps={{channel}} > {'View Info'} - </a> + </ToggleModalButton> </li> ); @@ -178,18 +180,32 @@ export default class Navbar extends React.Component { var manageMembersOption; var renameChannelOption; var deleteChannelOption; - if (!isDirect && isAdmin && !ChannelStore.isDefault(channel)) { - manageMembersOption = ( - <li role='presentation'> - <a - role='menuitem' - href='#' - onClick={() => this.setState({showMembersModal: true})} - > - {'Manage Members'} - </a> - </li> - ); + if (!isDirect && isAdmin) { + if (!ChannelStore.isDefault(channel)) { + manageMembersOption = ( + <li role='presentation'> + <a + role='menuitem' + href='#' + onClick={() => this.setState({showMembersModal: true})} + > + {'Manage Members'} + </a> + </li> + ); + + deleteChannelOption = ( + <li role='presentation'> + <ToggleModalButton + role='menuitem' + dialogType={DeleteChannelModal} + dialogProps={{channel}} + > + {'Delete Channel...'} + </ToggleModalButton> + </li> + ); + } renameChannelOption = ( <li role='presentation'> @@ -206,37 +222,19 @@ export default class Navbar extends React.Component { </a> </li> ); - - deleteChannelOption = ( - <li role='presentation'> - <a - role='menuitem' - href='#' - data-toggle='modal' - data-target='#delete_channel' - data-title={channel.display_name} - data-channelid={channel.id} - > - {'Delete Channel...'} - </a> - </li> - ); } var notificationPreferenceOption; if (!isDirect) { notificationPreferenceOption = ( <li role='presentation'> - <a + <ToggleModalButton role='menuitem' - href='#' - data-toggle='modal' - data-target='#channel_notifications' - data-title={channel.display_name} - data-channelid={channel.id} + dialogType={ChannelNotificationsModal} + dialogProps={{channel}} > - {'Notification Preferences'} - </a> + {'Notification Preferences'} + </ToggleModalButton> </li> ); } diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index a01d842e5..fffa5b19a 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +var DeletePostModal = require('./delete_post_modal.jsx'); var UserStore = require('../stores/user_store.jsx'); var utils = require('../utils/utils.jsx'); var TimeSince = require('./time_since.jsx'); @@ -50,7 +51,7 @@ export default class PostInfo extends React.Component { data-channelid={post.channel_id} data-comments={dataComments} > - Edit + {'Edit'} </a> </li> ); @@ -65,14 +66,9 @@ export default class PostInfo extends React.Component { <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title={type} - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={dataComments} + onClick={() => DeletePostModal.show(post, dataComments)} > - Delete + {'Delete'} </a> </li> ); @@ -89,7 +85,7 @@ export default class PostInfo extends React.Component { href='#' onClick={this.props.handleCommentClick} > - Reply + {'Reply'} </a> </li> ); diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 087ca1df2..ec8223203 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -83,9 +83,14 @@ export default class PostsView extends React.Component { let hideProfilePic = false; if (prevPost) { - sameUser = prevPost.user_id === post.user_id && post.create_at - prevPost.create_at <= 1000 * 60 * 5; + const postIsComment = Utils.isComment(post); + const prevPostIsComment = Utils.isComment(prevPost); + const postFromWebhook = Boolean(post.props && post.props.from_webhook); + const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook); - sameRoot = Utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id); + sameUser = prevPost.user_id === post.user_id && postFromWebhook === prevPostFromWebhook && + post.create_at - prevPost.create_at <= 1000 * 60 * 5; + sameRoot = (postIsComment && (prevPost.id === post.root_id || prevPost.root_id === post.root_id)) || (!postIsComment && !prevPostIsComment && sameUser); // hide the profile pic if: // the previous post was made by the same user as the current post, @@ -94,10 +99,10 @@ export default class PostsView extends React.Component { // the current post is not from a webhook // and the previous post is not from a webhook if ((prevPost.user_id === post.user_id) && - !Utils.isComment(prevPost) && - !Utils.isComment(post) && - (!post.props || !post.props.from_webhook) && - (!prevPost.props || !prevPost.props.from_webhook)) { + !prevPostIsComment && + !postIsComment && + !postFromWebhook && + !prevPostFromWebhook) { hideProfilePic = true; } } diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx index 9fb3af035..f47009cce 100644 --- a/web/react/components/rename_channel_modal.jsx +++ b/web/react/components/rename_channel_modal.jsx @@ -5,6 +5,7 @@ const Utils = require('../utils/utils.jsx'); const Client = require('../utils/client.jsx'); const AsyncClient = require('../utils/async_client.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); +const Constants = require('../utils/constants.jsx'); export default class RenameChannelModal extends React.Component { constructor(props) { @@ -36,10 +37,10 @@ export default class RenameChannelModal extends React.Component { return; } - let channel = ChannelStore.get(this.state.channelId); + const channel = ChannelStore.get(this.state.channelId); const oldName = channel.name; const oldDisplayName = channel.displayName; - let state = {serverError: ''}; + const state = {serverError: ''}; channel.display_name = this.state.displayName.trim(); if (!channel.display_name) { @@ -60,7 +61,7 @@ export default class RenameChannelModal extends React.Component { state.nameError = 'This field must be less than 22 characters'; state.invalid = true; } else { - let cleanedName = Utils.cleanUpUrlable(channel.name); + const cleanedName = Utils.cleanUpUrlable(channel.name); if (cleanedName === channel.name) { state.nameError = ''; } else { @@ -76,7 +77,7 @@ export default class RenameChannelModal extends React.Component { } Client.updateChannel(channel, - function handleUpdateSuccess() { + () => { $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide'); AsyncClient.getChannel(channel.id); @@ -84,12 +85,12 @@ export default class RenameChannelModal extends React.Component { ReactDOM.findDOMNode(this.refs.displayName).value = ''; ReactDOM.findDOMNode(this.refs.channelName).value = ''; - }.bind(this), - function handleUpdateError(err) { + }, + (err) => { state.serverError = err.message; state.invalid = true; this.setState(state); - }.bind(this) + } ); } onNameChange() { @@ -99,10 +100,12 @@ export default class RenameChannelModal extends React.Component { this.setState({displayName: ReactDOM.findDOMNode(this.refs.displayName).value}); } displayNameKeyUp() { - const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim(); - const channelName = Utils.cleanUpUrlable(displayName); - ReactDOM.findDOMNode(this.refs.channelName).value = channelName; - this.setState({channelName: channelName}); + if (this.state.channelName !== Constants.DEFAULT_CHANNEL) { + const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim(); + const channelName = Utils.cleanUpUrlable(displayName); + ReactDOM.findDOMNode(this.refs.channelName).value = channelName; + this.setState({channelName: channelName}); + } } handleClose() { this.setState({ @@ -150,6 +153,15 @@ export default class RenameChannelModal extends React.Component { serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>; } + let handleInputLabel = 'Handle'; + let handleInputClass = 'form-control'; + let readOnlyHandleInput = false; + if (this.state.channelName === Constants.DEFAULT_CHANNEL) { + handleInputLabel += ' - Cannot be changed for the default channel'; + handleInputClass += ' disabled-input'; + readOnlyHandleInput = true; + } + return ( <div className='modal fade' @@ -167,15 +179,15 @@ export default class RenameChannelModal extends React.Component { className='close' data-dismiss='modal' > - <span aria-hidden='true'>×</span> - <span className='sr-only'>Close</span> + <span aria-hidden='true'>{'×'}</span> + <span className='sr-only'>{'Close'}</span> </button> - <h4 className='modal-title'>Rename Channel</h4> + <h4 className='modal-title'>{'Rename Channel'}</h4> </div> <form role='form'> <div className='modal-body'> <div className={displayNameClass}> - <label className='control-label'>Display Name</label> + <label className='control-label'>{'Display Name'}</label> <input onKeyUp={this.displayNameKeyUp} onChange={this.onDisplayNameChange} @@ -190,15 +202,16 @@ export default class RenameChannelModal extends React.Component { {displayNameError} </div> <div className={nameClass}> - <label className='control-label'>Handle</label> + <label className='control-label'>{handleInputLabel}</label> <input onChange={this.onNameChange} type='text' - className='form-control' + className={handleInputClass} ref='channelName' placeholder='lowercase alphanumeric's only' value={this.state.channelName} maxLength='64' + readOnly={readOnlyHandleInput} /> {nameError} </div> @@ -210,14 +223,14 @@ export default class RenameChannelModal extends React.Component { className='btn btn-default' data-dismiss='modal' > - Cancel + {'Cancel'} </button> <button onClick={this.handleSubmit} type='submit' className='btn btn-primary' > - Save + {'Save'} </button> </div> </form> diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 58cc1cac7..c16f9ff0e 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -8,6 +8,7 @@ var UserStore = require('../stores/user_store.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Utils = require('../utils/utils.jsx'); var Constants = require('../utils/constants.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx'); var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); @@ -114,12 +115,7 @@ export default class RhsComment extends React.Component { <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title='Comment' - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={0} + onClick={() => DeletePostModal.show(post, 0)} > {'Delete'} </a> diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index 69de5d523..84fdc014a 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -6,6 +6,7 @@ var UserProfile = require('./user_profile.jsx'); var UserStore = require('../stores/user_store.jsx'); var TextFormatting = require('../utils/text_formatting.jsx'); var utils = require('../utils/utils.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx'); var twemoji = require('twemoji'); var Constants = require('../utils/constants.jsx'); @@ -86,21 +87,16 @@ export default class RhsRootPost extends React.Component { data-postid={post.id} data-channelid={post.channel_id} > - Edit + {'Edit'} </a> </li> <li role='presentation'> <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title={type} - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={this.props.commentCount} + onClick={() => DeletePostModal.show(post, this.props.commentCount)} > - Delete + {'Delete'} </a> </li> </ul> diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 7c11de7cf..cc062c538 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -117,8 +117,6 @@ export default class RhsThread extends React.Component { } } resize() { - var height = this.state.windowHeight - $('#error_bar').outerHeight() - 100; - $('.post-right__scroll').css('height', height + 'px'); $('.post-right__scroll').scrollTop(100000); if (this.state.windowWidth > 768) { $('.post-right__scroll').perfectScrollbar(); diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 2f0068908..f4d8647db 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -62,8 +62,6 @@ export default class SearchResults extends React.Component { } resize() { - var height = this.state.windowHeight - $('#error_bar').outerHeight() - 100; - $('#search-items-container').css('height', height + 'px'); $('#search-items-container').scrollTop(0); if (this.state.windowWidth > 768) { $('#search-items-container').perfectScrollbar(); diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 2135e3ef3..6a428e884 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -48,7 +48,7 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={InviteMemberModal.show} > - <i className='glyphicon glyphicon-user'></i>Invite New Member + <i className='fa fa-user'></i>Invite New Member </a> </li> ); @@ -61,7 +61,7 @@ export default class SidebarRightMenu extends React.Component { data-target='#get_link' data-title='Team Invite' data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id} - ><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a> + ><i className='fa fa-link'></i>Get Team Invite Link</a> </li> ); } @@ -74,7 +74,7 @@ export default class SidebarRightMenu extends React.Component { href='#' data-toggle='modal' data-target='#team_settings' - ><i className='glyphicon glyphicon-globe'></i>Team Settings</a> + ><i className='fa fa-globe'></i>Team Settings</a> </li> ); manageLink = ( @@ -84,7 +84,7 @@ export default class SidebarRightMenu extends React.Component { data-toggle='modal' data-target='#team_members' > - <i className='glyphicon glyphicon-wrench'></i>Manage Members</a> + <i className='fa fa-users'></i>Manage Members</a> </li> ); } @@ -95,7 +95,7 @@ export default class SidebarRightMenu extends React.Component { <a href={'/admin_console?' + utils.getSessionIndex()} > - <i className='glyphicon glyphicon-wrench'></i>System Console</a> + <i className='fa fa-wrench'></i>System Console</a> </li> ); } @@ -125,7 +125,7 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={() => this.setState({showUserSettingsModal: true})} > - <i className='glyphicon glyphicon-cog'></i>Account Settings + <i className='fa fa-cog'></i>Account Settings </a> </li> {teamSettingsLink} @@ -137,18 +137,18 @@ export default class SidebarRightMenu extends React.Component { <a href='#' onClick={this.handleLogoutClick} - ><i className='glyphicon glyphicon-log-out'></i>Logout</a></li> + ><i className='fa fa-sign-out'></i>Logout</a></li> <li className='divider'></li> <li> <a target='_blank' href='/static/help/configure_links.html' - ><i className='glyphicon glyphicon-question-sign'></i>Help</a></li> + ><i className='fa fa-question'></i>Help</a></li> <li> <a target='_blank' href='/static/help/configure_links.html' - ><i className='glyphicon glyphicon-earphone'></i>Report a Problem</a></li> + ><i className='fa fa-phone'></i>Report a Problem</a></li> </ul> </div> <UserSettingsModal diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index e6530b941..1a5269baa 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -295,6 +295,13 @@ export default class Textbox extends React.Component { this.resize(); } + showHelp(e) { + e.preventDefault(); + e.target.blur(); + + global.window.open('/docs/Messaging'); + } + render() { const previewLinkVisible = this.props.messageText.length > 0; @@ -336,11 +343,17 @@ export default class Textbox extends React.Component { > </div> <a + onClick={this.showHelp} + className='textbox-help-link' + > + {'Help'} + </a> + <a style={{visibility: previewLinkVisible ? 'visible' : 'hidden'}} onClick={this.showPreview} className='textbox-preview-link' > - {this.state.preview ? 'Edit message' : 'Preview'} + {this.state.preview ? 'Edit' : 'Preview'} </a> </div> ); diff --git a/web/react/components/toggle_modal_button.jsx b/web/react/components/toggle_modal_button.jsx new file mode 100644 index 000000000..51c8d1f20 --- /dev/null +++ b/web/react/components/toggle_modal_button.jsx @@ -0,0 +1,62 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +export default class ModalToggleButton extends React.Component { + constructor(props) { + super(props); + + this.show = this.show.bind(this); + this.hide = this.hide.bind(this); + + this.state = { + show: false + }; + } + + show() { + this.setState({show: true}); + } + + hide() { + this.setState({show: false}); + } + + render() { + const {children, dialogType, dialogProps, ...props} = this.props; + + // this assumes that all modals will have a show property and an onHide event + const dialog = React.createElement(this.props.dialogType, Object.assign({}, dialogProps, { + show: this.state.show, + onHide: () => { + this.hide(); + + if (dialogProps.onHide) { + dialogProps.onHide(); + } + } + })); + + // nesting the dialog in the anchor tag looks like it shouldn't work, but it does due to how react-bootstrap + // renders modals at the top level of the DOM instead of where you specify in the virtual DOM + return ( + <a + {...props} + href='#' + onClick={this.show} + > + {children} + {dialog} + </a> + ); + } +} + +ModalToggleButton.propTypes = { + children: React.PropTypes.node.isRequired, + dialogType: React.PropTypes.func.isRequired, + dialogProps: React.PropTypes.object +}; + +ModalToggleButton.defaultProps = { + dialogProps: {} +}; diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 4dcf32cb9..776201295 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -10,6 +10,7 @@ export default class UserSettingsModal extends React.Component { constructor(props) { super(props); + this.handleShow = this.handleShow.bind(this); this.handleHide = this.handleHide.bind(this); this.handleHidden = this.handleHidden.bind(this); this.handleCollapse = this.handleCollapse.bind(this); @@ -33,12 +34,22 @@ export default class UserSettingsModal extends React.Component { this.requireConfirm = false; } + componentDidMount() { + if (this.props.show) { + this.handleShow(); + } + } + componentDidUpdate(prevProps) { - if (!prevProps.show && this.props.show) { - $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); - if ($(window).width() > 768) { - $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); - } + if (this.props.show && !prevProps.show) { + this.handleShow(); + } + } + + handleShow() { + $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); + if ($(window).width() > 768) { + $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); } } diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx index 61d13ed8b..16ace0abc 100644 --- a/web/react/components/user_settings/user_settings_security.jsx +++ b/web/react/components/user_settings/user_settings_security.jsx @@ -5,6 +5,7 @@ var SettingItemMin = require('../setting_item_min.jsx'); var SettingItemMax = require('../setting_item_max.jsx'); var AccessHistoryModal = require('../access_history_modal.jsx'); var ActivityLogModal = require('../activity_log_modal.jsx'); +var ToggleModalButton = require('../toggle_modal_button.jsx'); var Client = require('../../utils/client.jsx'); var AsyncClient = require('../../utils/async_client.jsx'); var Constants = require('../../utils/constants.jsx'); @@ -13,34 +14,13 @@ export default class SecurityTab extends React.Component { constructor(props) { super(props); - this.showAccessHistoryModal = this.showAccessHistoryModal.bind(this); - this.showActivityLogModal = this.showActivityLogModal.bind(this); - this.hideModals = this.hideModals.bind(this); this.submitPassword = this.submitPassword.bind(this); this.updateCurrentPassword = this.updateCurrentPassword.bind(this); this.updateNewPassword = this.updateNewPassword.bind(this); this.updateConfirmPassword = this.updateConfirmPassword.bind(this); this.setupInitialState = this.setupInitialState.bind(this); - const state = this.setupInitialState(); - state.showAccessHistoryModal = false; - state.showActivityLogModal = false; - this.state = state; - } - showAccessHistoryModal() { - this.props.setEnforceFocus(false); - this.setState({showAccessHistoryModal: true}); - } - showActivityLogModal() { - this.props.setEnforceFocus(false); - this.setState({showActivityLogModal: true}); - } - hideModals() { - this.props.setEnforceFocus(true); - this.setState({ - showAccessHistoryModal: false, - showActivityLogModal: false - }); + this.state = this.setupInitialState(); } submitPassword(e) { e.preventDefault(); @@ -258,30 +238,20 @@ export default class SecurityTab extends React.Component { {passwordSection} <div className='divider-dark'/> <br></br> - <a + <ToggleModalButton className='security-links theme' - href='#' - onClick={this.showAccessHistoryModal} + dialogType={AccessHistoryModal} > <i className='fa fa-clock-o'></i>View Access History - </a> + </ToggleModalButton> <b> </b> - <a + <ToggleModalButton className='security-links theme' - href='#' - onClick={this.showActivityLogModal} + dialogType={ActivityLogModal} > - <i className='fa fa-globe'></i>View and Logout of Active Sessions - </a> + <i className='fa fa-clock-o'></i>{'View and Logout of Active Sessions'} + </ToggleModalButton> </div> - <AccessHistoryModal - show={this.state.showAccessHistoryModal} - onModalDismissed={this.hideModals} - /> - <ActivityLogModal - show={this.state.showActivityLogModal} - onModalDismissed={this.hideModals} - /> </div> ); } |