summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorhmhealey <harrisonmhealey@gmail.com>2015-11-16 12:08:05 -0500
committerhmhealey <harrisonmhealey@gmail.com>2015-11-23 10:53:06 -0500
commit6237631a85b79311a60b87df423abbdce56c7876 (patch)
tree2fe385ee6258be1d9f83cc737fd51651359ff48c /web
parent60e2314baaa17fe972f13e6f763e08e03e356c8a (diff)
downloadchat-6237631a85b79311a60b87df423abbdce56c7876.tar.gz
chat-6237631a85b79311a60b87df423abbdce56c7876.tar.bz2
chat-6237631a85b79311a60b87df423abbdce56c7876.zip
Ported EditChannelModal to React-Bootstrap
Diffstat (limited to 'web')
-rw-r--r--web/react/components/channel_header.jsx27
-rw-r--r--web/react/components/edit_channel_modal.jsx174
-rw-r--r--web/react/components/navbar.jsx56
-rw-r--r--web/react/components/toggle_modal_button.jsx17
-rw-r--r--web/react/pages/channel.jsx6
-rw-r--r--web/react/utils/channel_intro_mssages.jsx51
-rw-r--r--web/react/utils/utils.jsx17
7 files changed, 161 insertions, 187 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 8c721348f..06d5db8b0 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 EditChannelModal from './edit_channel_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={EditChannelModal}
+ 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={EditChannelModal}
+ dialogProps={{channel}}
>
- {'Set '}{channelTerm}{' Header...'}
- </a>
+ {`Set ${channelTerm} Header...`}
+ </ToggleModalButton>
</li>
);
dropdownContents.push(
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 80dab4a57..487ae6964 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -3,39 +3,51 @@
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 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.onHide = this.onHide.bind(this);
this.state = {
- header: '',
- title: '',
- channelId: '',
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.state.channelId;
+ data.channel_id = this.props.channel.id;
if (data.channel_id.length !== 26) {
return;
}
- data.channel_header = this.state.header.trim();
+ data.channel_header = ReactDOM.findDOMNode(this.refs.textarea).value;
Client.updateChannelHeader(data,
() => {
this.setState({serverError: ''});
- AsyncClient.getChannel(this.state.channelId);
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
+ AsyncClient.getChannel(this.props.channel.id);
+ this.onHide();
},
(err) => {
if (err.message === 'Invalid channel_header parameter') {
@@ -46,105 +58,69 @@ export default class EditChannelModal extends React.Component {
}
);
}
- 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);
+
+ onShow() {
+ const textarea = ReactDOM.findDOMNode(this.refs.textarea);
+ Utils.placeCaretAtEnd(textarea);
}
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
+
+ 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>;
}
- 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'
+ <Modal
+ show={this.props.show}
+ onHide={this.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>
- {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>
+ <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>
);
}
}
+
+EditChannelModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired,
+ channel: React.PropTypes.object.isRequired
+};
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 6848ee5da..8d3d779f3 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 EditChannelModal from './edit_channel_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 = (
+ <EditChannelModal
+ 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/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/pages/channel.jsx b/web/react/pages/channel.jsx
index 5cc1be741..1c0d873d5 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -8,7 +8,6 @@ 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 RenameChannelModal from '../components/rename_channel_modal.jsx';
import EditPostModal from '../components/edit_post_modal.jsx';
import DeletePostModal from '../components/delete_post_modal.jsx';
@@ -93,11 +92,6 @@ function setupChannelPage(props, team, channel) {
);
ReactDOM.render(
- <EditChannelModal />,
- document.getElementById('edit_channel_modal')
- );
-
- ReactDOM.render(
<RenameChannelModal />,
document.getElementById('rename_channel_modal')
);
diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx
index 0bbc7366e..4047ec028 100644
--- a/web/react/utils/channel_intro_mssages.jsx
+++ b/web/react/utils/channel_intro_mssages.jsx
@@ -1,9 +1,10 @@
-
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from './utils.jsx';
+import EditChannelModal from '../components/edit_channel_modal.jsx';
import InviteMemberModal from '../components/invite_member_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';
@@ -49,17 +50,13 @@ 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
+ <ToggleModalButton
className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelModal}
+ dialogProps={{channel}}
>
<i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ </ToggleModalButton>
</div>
);
}
@@ -79,17 +76,13 @@ export function createOffTopicIntroMessage(channel, showInviteModal) {
{'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
<br/>
</p>
- <a
+ <ToggleModalButton
className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelModal}
+ dialogProps={{channel}}
>
<i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ </ToggleModalButton>
<a
href='#'
className='intro-links'
@@ -138,17 +131,13 @@ 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
+ <ToggleModalButton
className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelModal}
+ dialogProps={{channel}}
>
<i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ </ToggleModalButton>
<br/>
</div>
);
@@ -193,17 +182,13 @@ export function createStandardIntroMessage(channel, showInviteModal) {
{memberMessage}
<br/>
</p>
- <a
+ <ToggleModalButton
className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
+ dialogType={EditChannelModal}
+ dialogProps={{channel}}
>
<i className='fa fa-pencil'></i>{'Set a header'}
- </a>
+ </ToggleModalButton>
<a
className='intro-links'
href='#'
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) {