summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/admin_console/password_settings.jsx16
-rw-r--r--webapp/components/admin_console/reset_password_modal.jsx2
-rw-r--r--webapp/components/audio_video_preview.jsx4
-rw-r--r--webapp/components/audit_table.jsx33
-rw-r--r--webapp/components/channel_header.jsx78
-rw-r--r--webapp/components/channel_invite_button.jsx1
-rw-r--r--webapp/components/channel_members_dropdown.jsx5
-rw-r--r--webapp/components/channel_members_modal.jsx3
-rw-r--r--webapp/components/channel_notifications_modal.jsx10
-rw-r--r--webapp/components/create_team/components/display_name.jsx9
-rw-r--r--webapp/components/delete_modal_trigger.jsx62
-rw-r--r--webapp/components/emoji/components/delete_emoji_modal.jsx49
-rw-r--r--webapp/components/emoji/components/emoji_list_item.jsx11
-rw-r--r--webapp/components/integrations/components/confirm_integration.jsx11
-rw-r--r--webapp/components/integrations/components/delete_integration.jsx73
-rw-r--r--webapp/components/login/login_controller.jsx3
-rw-r--r--webapp/components/navbar.jsx38
-rw-r--r--webapp/components/needs_team.jsx16
-rw-r--r--webapp/components/new_channel_modal.jsx6
-rw-r--r--webapp/components/popover_list_members.jsx14
-rw-r--r--webapp/components/post_view/components/date_separator.jsx27
-rw-r--r--webapp/components/post_view/components/post_attachment_opengraph.jsx22
-rw-r--r--webapp/components/post_view/components/post_info.jsx61
-rw-r--r--webapp/components/post_view/components/post_time.jsx7
-rw-r--r--webapp/components/rhs_comment.jsx66
-rw-r--r--webapp/components/rhs_root_post.jsx66
-rw-r--r--webapp/components/rhs_thread.jsx90
-rw-r--r--webapp/components/search_bar.jsx5
-rw-r--r--webapp/components/search_results.jsx37
-rw-r--r--webapp/components/search_results_header.jsx14
-rw-r--r--webapp/components/search_results_item.jsx13
-rw-r--r--webapp/components/searchable_user_list.jsx12
-rw-r--r--webapp/components/setting_item_max.jsx24
-rw-r--r--webapp/components/setting_item_min.jsx8
-rw-r--r--webapp/components/setting_picture.jsx52
-rw-r--r--webapp/components/sidebar.jsx14
-rw-r--r--webapp/components/sidebar_header_dropdown.jsx2
-rw-r--r--webapp/components/sidebar_right.jsx23
-rw-r--r--webapp/components/team_general_tab.jsx16
-rw-r--r--webapp/components/user_settings/desktop_notification_settings.jsx9
-rw-r--r--webapp/components/user_settings/email_notification_setting.jsx4
-rw-r--r--webapp/components/user_settings/import_theme_modal.jsx3
-rw-r--r--webapp/components/user_settings/manage_languages.jsx1
-rw-r--r--webapp/components/user_settings/user_settings_advanced.jsx8
-rw-r--r--webapp/components/user_settings/user_settings_display.jsx13
-rw-r--r--webapp/components/user_settings/user_settings_general.jsx21
-rw-r--r--webapp/components/user_settings/user_settings_notifications.jsx18
-rw-r--r--webapp/components/user_settings/user_settings_security.jsx3
-rw-r--r--webapp/components/user_settings/user_settings_theme.jsx5
-rw-r--r--webapp/components/view_image.jsx12
50 files changed, 854 insertions, 246 deletions
diff --git a/webapp/components/admin_console/password_settings.jsx b/webapp/components/admin_console/password_settings.jsx
index 3707977b8..43ec40904 100644
--- a/webapp/components/admin_console/password_settings.jsx
+++ b/webapp/components/admin_console/password_settings.jsx
@@ -39,16 +39,16 @@ export default class PasswordSettings extends AdminSettings {
if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.PasswordRequirements === 'true') {
let sampleErrorMsgId = 'user.settings.security.passwordError';
if (props.config.PasswordSettings.Lowercase) {
- sampleErrorMsgId = sampleErrorMsgId + 'Lowercase';
+ sampleErrorMsgId += 'Lowercase';
}
if (props.config.PasswordSettings.Uppercase) {
- sampleErrorMsgId = sampleErrorMsgId + 'Uppercase';
+ sampleErrorMsgId += 'Uppercase';
}
if (props.config.PasswordSettings.Number) {
- sampleErrorMsgId = sampleErrorMsgId + 'Number';
+ sampleErrorMsgId += 'Number';
}
if (props.config.PasswordSettings.Symbol) {
- sampleErrorMsgId = sampleErrorMsgId + 'Symbol';
+ sampleErrorMsgId += 'Symbol';
}
this.sampleErrorMsg = (
<FormattedMessage
@@ -101,16 +101,16 @@ export default class PasswordSettings extends AdminSettings {
}
let sampleErrorMsgId = 'user.settings.security.passwordError';
if (this.refs.lowercase.checked) {
- sampleErrorMsgId = sampleErrorMsgId + 'Lowercase';
+ sampleErrorMsgId += 'Lowercase';
}
if (this.refs.uppercase.checked) {
- sampleErrorMsgId = sampleErrorMsgId + 'Uppercase';
+ sampleErrorMsgId += 'Uppercase';
}
if (this.refs.number.checked) {
- sampleErrorMsgId = sampleErrorMsgId + 'Number';
+ sampleErrorMsgId += 'Number';
}
if (this.refs.symbol.checked) {
- sampleErrorMsgId = sampleErrorMsgId + 'Symbol';
+ sampleErrorMsgId += 'Symbol';
}
return (
<FormattedMessage
diff --git a/webapp/components/admin_console/reset_password_modal.jsx b/webapp/components/admin_console/reset_password_modal.jsx
index 757f85517..1b9e5b37a 100644
--- a/webapp/components/admin_console/reset_password_modal.jsx
+++ b/webapp/components/admin_console/reset_password_modal.jsx
@@ -61,7 +61,7 @@ class ResetPasswordModal extends React.Component {
if (this.state.serverError) {
urlClass += ' has-error';
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.state.serverError}</p></div>;
+ serverError = <div className='has-error'><p className='input__help error'>{this.state.serverError}</p></div>;
}
let title;
diff --git a/webapp/components/audio_video_preview.jsx b/webapp/components/audio_video_preview.jsx
index 4956900a9..9a55e4835 100644
--- a/webapp/components/audio_video_preview.jsx
+++ b/webapp/components/audio_video_preview.jsx
@@ -94,7 +94,6 @@ export default class AudioVideoPreview extends React.Component {
<video
key={this.props.fileInfo.id}
ref='video'
- style={{maxHeight: this.props.maxHeight}}
data-setup='{}'
controls='controls'
width={width}
@@ -111,6 +110,5 @@ export default class AudioVideoPreview extends React.Component {
AudioVideoPreview.propTypes = {
fileInfo: React.PropTypes.object.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired
+ fileUrl: React.PropTypes.string.isRequired
};
diff --git a/webapp/components/audit_table.jsx b/webapp/components/audit_table.jsx
index c70c659d4..34e61dbac 100644
--- a/webapp/components/audit_table.jsx
+++ b/webapp/components/audit_table.jsx
@@ -229,12 +229,12 @@ class AuditTable extends React.Component {
let iContent;
if (this.props.showIp) {
- iContent = <td className='word-break--all'>{auditInfo.ip}</td>;
+ iContent = <td className='whitespace--nowrap word-break--all'>{auditInfo.ip}</td>;
}
let sContent;
if (this.props.showSession) {
- sContent = <td className='word-break--all'>{auditInfo.sessionId}</td>;
+ sContent = <td className='whitespace--nowrap word-break--all'>{auditInfo.sessionId}</td>;
}
const descStyle = {};
@@ -244,7 +244,7 @@ class AuditTable extends React.Component {
accessList[i] = (
<tr key={audit.id}>
- <td className='word-break--all'>{auditInfo.timestamp}</td>
+ <td className='whitespace--nowrap word-break--all'>{auditInfo.timestamp}</td>
{uContent}
<td
className='word-break--all'
@@ -615,18 +615,21 @@ export function formatAuditInfo(audit, formatMessage) {
const auditInfo = {};
auditInfo.timestamp = (
<div>
- <FormattedDate
- value={date}
- day='2-digit'
- month='short'
- year='numeric'
- />
- {' - '}
- <FormattedTime
- value={date}
- hour='2-digit'
- minute='2-digit'
- />
+ <div>
+ <FormattedDate
+ value={date}
+ day='2-digit'
+ month='short'
+ year='numeric'
+ />
+ </div>
+ <div>
+ <FormattedTime
+ value={date}
+ hour='2-digit'
+ minute='2-digit'
+ />
+ </div>
</div>
);
auditInfo.userId = audit.user_id;
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 01e1e98cf..c05033b51 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -30,7 +30,7 @@ import * as Utils from 'utils/utils.jsx';
import * as ChannelUtils from 'utils/channel_utils.jsx';
import {getSiteURL} from 'utils/url.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-import {getFlaggedPosts} from 'actions/post_actions.jsx';
+import {getFlaggedPosts, getPinnedPosts} from 'actions/post_actions.jsx';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
@@ -53,6 +53,7 @@ export default class ChannelHeader extends React.Component {
this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
this.handleShortcut = this.handleShortcut.bind(this);
this.getFlagged = this.getFlagged.bind(this);
+ this.getPinnedPosts = this.getPinnedPosts.bind(this);
this.initWebrtc = this.initWebrtc.bind(this);
this.onBusy = this.onBusy.bind(this);
this.openDirectMessageModal = this.openDirectMessageModal.bind(this);
@@ -158,6 +159,15 @@ export default class ChannelHeader extends React.Component {
}
}
+ getPinnedPosts(e) {
+ e.preventDefault();
+ if (SearchStore.isPinnedPosts) {
+ GlobalActions.toggleSideBarAction(false);
+ } else {
+ getPinnedPosts(this.props.channelId);
+ }
+ }
+
getFlagged(e) {
e.preventDefault();
if (SearchStore.isFlaggedPosts) {
@@ -211,6 +221,7 @@ export default class ChannelHeader extends React.Component {
render() {
const flagIcon = Constants.FLAG_ICON_SVG;
+ const pinIcon = Constants.PIN_ICON;
if (!this.validState()) {
// Use an empty div to make sure the header's height stays constant
@@ -230,7 +241,10 @@ export default class ChannelHeader extends React.Component {
);
const flaggedTooltip = (
- <Tooltip id='flaggedTooltip'>
+ <Tooltip
+ id='flaggedTooltip'
+ className='text-nowrap'
+ >
<FormattedMessage
id='channel_header.flagged'
defaultMessage='Flagged Posts'
@@ -365,6 +379,7 @@ export default class ChannelHeader extends React.Component {
if (isDirect) {
dropdownContents.push(
<li
+ id='channelEditHeaderDirect'
key='edit_header_direct'
role='presentation'
>
@@ -383,6 +398,7 @@ export default class ChannelHeader extends React.Component {
} else if (isGroup) {
dropdownContents.push(
<li
+ id='channelEditHeaderGroup'
key='edit_header_direct'
role='presentation'
>
@@ -401,6 +417,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelnotificationPreferencesGroup'
key='notification_preferences'
role='presentation'
>
@@ -423,6 +440,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelAddMembersGroup'
key='add_members'
role='presentation'
>
@@ -441,6 +459,7 @@ export default class ChannelHeader extends React.Component {
} else {
dropdownContents.push(
<li
+ id='channelViewInfo'
key='view_info'
role='presentation'
>
@@ -459,6 +478,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelNotificationPreferences'
key='notification_preferences'
role='presentation'
>
@@ -489,6 +509,7 @@ export default class ChannelHeader extends React.Component {
if (!ChannelStore.isDefault(channel)) {
dropdownContents.push(
<li
+ id='channelAddMembers'
key='add_members'
role='presentation'
>
@@ -508,6 +529,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelManageMembers'
key='manage_members'
role='presentation'
>
@@ -534,6 +556,7 @@ export default class ChannelHeader extends React.Component {
const deleteOption = (
<li
+ id='channelDelete'
key='delete_channel'
role='presentation'
>
@@ -556,6 +579,7 @@ export default class ChannelHeader extends React.Component {
if (ChannelUtils.showManagementOptions(channel, isAdmin, isSystemAdmin, isChannelAdmin)) {
dropdownContents.push(
<li
+ id='channelEditHeader'
key='set_channel_header'
role='presentation'
>
@@ -577,6 +601,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelEditPurpose'
key='set_channel_purpose'
role='presentation'
>
@@ -598,6 +623,7 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
<li
+ id='channelRename'
key='rename_channel'
role='presentation'
>
@@ -637,6 +663,7 @@ export default class ChannelHeader extends React.Component {
if (!ChannelStore.isDefault(channel) && canLeave) {
dropdownContents.push(
<li
+ id='channelLeave'
key='leave_channel'
role='presentation'
>
@@ -665,19 +692,27 @@ export default class ChannelHeader extends React.Component {
headerText = channel.header;
}
- const toggleFavoriteTooltip = (
- <Tooltip id='favoriteTooltip'>
- {this.state.isFavorite ?
+ let toggleFavoriteTooltip;
+ if (this.state.isFavorite) {
+ toggleFavoriteTooltip = (
+ <Tooltip id='favoriteTooltip'>
<FormattedMessage
id='channelHeader.removeFromFavorites'
defaultMessage='Remove from Favorites'
- /> :
- <FormattedMessage
- id='channelHeader.addToFavorites'
- defaultMessage='Add to Favorites'
- />}
- </Tooltip>
- );
+ />
+ </Tooltip>
+ );
+ } else {
+ toggleFavoriteTooltip = (
+ <Tooltip id='favoriteTooltip'>
+ <FormattedMessage
+ id='channelHeader.addToFavorites'
+ defaultMessage='Add to Favorites'
+ />
+ </Tooltip>
+ );
+ }
+
const toggleFavorite = (
<OverlayTrigger
delayShow={Constants.OVERLAY_TIME_DELAY}
@@ -685,6 +720,7 @@ export default class ChannelHeader extends React.Component {
overlay={toggleFavoriteTooltip}
>
<a
+ id='toggleFavorite'
href='#'
onClick={this.toggleFavorite}
className='channel-header__favorites'
@@ -729,10 +765,10 @@ export default class ChannelHeader extends React.Component {
{toggleFavorite}
<div className='dropdown'>
<a
+ id='channelHeaderDropdown'
href='#'
className='dropdown-toggle theme'
type='button'
- id='channel_header_dropdown'
data-toggle='dropdown'
aria-expanded='true'
>
@@ -762,8 +798,20 @@ export default class ChannelHeader extends React.Component {
</OverlayTrigger>
</div>
</th>
- <th className='header-list__members'>
+ <th className='header-list__right'>
{popoverListMembers}
+ <a
+ href='#'
+ type='button'
+ id='pinnedPostsButton'
+ className='pinned-posts-button'
+ onClick={this.getPinnedPosts}
+ >
+ <span
+ dangerouslySetInnerHTML={{__html: pinIcon}}
+ aria-hidden='true'
+ />
+ </a>
</th>
<th className='search-bar__container'>
<NavbarSearchBox
@@ -779,6 +827,7 @@ export default class ChannelHeader extends React.Component {
overlay={recentMentionsTooltip}
>
<a
+ id='searchMentions'
href='#'
type='button'
onClick={this.searchMentions}
@@ -796,6 +845,7 @@ export default class ChannelHeader extends React.Component {
overlay={flaggedTooltip}
>
<a
+ id='flaggedPostsButton'
href='#'
type='button'
onClick={this.getFlagged}
diff --git a/webapp/components/channel_invite_button.jsx b/webapp/components/channel_invite_button.jsx
index 7c2718cb9..2ef8fb4af 100644
--- a/webapp/components/channel_invite_button.jsx
+++ b/webapp/components/channel_invite_button.jsx
@@ -55,6 +55,7 @@ export default class ChannelInviteButton extends React.Component {
render() {
return (
<SpinnerButton
+ id='addMembers'
className='btn btn-sm btn-primary'
onClick={this.handleClick}
spinning={this.state.addingUser}
diff --git a/webapp/components/channel_members_dropdown.jsx b/webapp/components/channel_members_dropdown.jsx
index a7b3259af..5ccdcd4c1 100644
--- a/webapp/components/channel_members_dropdown.jsx
+++ b/webapp/components/channel_members_dropdown.jsx
@@ -131,6 +131,7 @@ export default class ChannelMembersDropdown extends React.Component {
removeFromChannel = (
<li role='presentation'>
<a
+ id='removeFromChannel'
role='menuitem'
href='#'
onClick={this.handleRemoveFromChannel}
@@ -149,6 +150,7 @@ export default class ChannelMembersDropdown extends React.Component {
makeChannelMember = (
<li role='presentation'>
<a
+ id='makeChannelMember'
role='menuitem'
href='#'
onClick={this.handleMakeChannelMember}
@@ -167,6 +169,7 @@ export default class ChannelMembersDropdown extends React.Component {
makeChannelAdmin = (
<li role='presentation'>
<a
+ id='makeChannelAdmin'
role='menuitem'
href='#'
onClick={this.handleMakeChannelAdmin}
@@ -183,6 +186,7 @@ export default class ChannelMembersDropdown extends React.Component {
return (
<div className='dropdown member-drop'>
<a
+ id='channelMemberDropdown'
href='#'
className='dropdown-toggle theme'
type='button'
@@ -206,6 +210,7 @@ export default class ChannelMembersDropdown extends React.Component {
} else if (this.canRemoveMember()) {
return (
<button
+ id='removeMember'
type='button'
className='btn btn-danger btn-message'
onClick={this.handleRemoveFromChannel}
diff --git a/webapp/components/channel_members_modal.jsx b/webapp/components/channel_members_modal.jsx
index 96d90e5cc..ec5423fe2 100644
--- a/webapp/components/channel_members_modal.jsx
+++ b/webapp/components/channel_members_modal.jsx
@@ -41,6 +41,7 @@ export default class ChannelMembersModal extends React.Component {
/>
</Modal.Title>
<a
+ id='showInviteModal'
className='btn btn-md btn-primary'
href='#'
onClick={() => {
@@ -71,4 +72,4 @@ ChannelMembersModal.propTypes = {
onModalDismissed: React.PropTypes.func.isRequired,
showInviteModal: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired
-}; \ No newline at end of file
+};
diff --git a/webapp/components/channel_notifications_modal.jsx b/webapp/components/channel_notifications_modal.jsx
index e78755fe3..2f76f19e1 100644
--- a/webapp/components/channel_notifications_modal.jsx
+++ b/webapp/components/channel_notifications_modal.jsx
@@ -135,6 +135,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelNotificationGlobalDefault'
type='radio'
name='desktopNotificationLevel'
checked={notifyActive[0]}
@@ -153,6 +154,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelNotificationAllActivity'
type='radio'
name='desktopNotificationLevel'
checked={notifyActive[1]}
@@ -165,6 +167,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelNotificationMentions'
type='radio'
name='desktopNotificationLevel'
checked={notifyActive[2]}
@@ -177,6 +180,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelNotificationNever'
type='radio'
name='desktopNotificationLevel'
checked={notifyActive[3]}
@@ -287,6 +291,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelUnreadAll'
type='radio'
name='markUnreadLevel'
checked={this.state.unreadLevel === 'all'}
@@ -302,6 +307,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelUnreadMentions'
type='radio'
name='markUnreadLevel'
checked={this.state.unreadLevel === 'mention'}
@@ -459,6 +465,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelPushNotificationGlobalDefault'
type='radio'
name='pushNotificationLevel'
checked={notifyActive[0]}
@@ -477,6 +484,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelPushNotificationAllActivity'
type='radio'
name='pushNotificationLevel'
checked={notifyActive[1]}
@@ -489,6 +497,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelPushNotificationMentions'
type='radio'
name='pushNotificationLevel'
checked={notifyActive[2]}
@@ -501,6 +510,7 @@ export default class ChannelNotificationsModal extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelPushNotificationNever'
type='radio'
name='pushNotificationLevel'
checked={notifyActive[3]}
diff --git a/webapp/components/create_team/components/display_name.jsx b/webapp/components/create_team/components/display_name.jsx
index aeb8afbb9..865c0e6db 100644
--- a/webapp/components/create_team/components/display_name.jsx
+++ b/webapp/components/create_team/components/display_name.jsx
@@ -10,7 +10,6 @@ import logoImage from 'images/logo.png';
import React from 'react';
import ReactDOM from 'react-dom';
-import {Link} from 'react-router/es6';
import {FormattedMessage} from 'react-intl';
export default class TeamSignupDisplayNamePage extends React.Component {
@@ -118,14 +117,6 @@ export default class TeamSignupDisplayNamePage extends React.Component {
defaultMessage='Next'
/><i className='fa fa-chevron-right'/>
</button>
- <div className='margin--extra'>
- <Link to='/select_team'>
- <FormattedMessage
- id='create_team.display_name.back'
- defaultMessage='Back to previous step'
- />
- </Link>
- </div>
</form>
</div>
);
diff --git a/webapp/components/delete_modal_trigger.jsx b/webapp/components/delete_modal_trigger.jsx
new file mode 100644
index 000000000..9ccbf33a2
--- /dev/null
+++ b/webapp/components/delete_modal_trigger.jsx
@@ -0,0 +1,62 @@
+import React from 'react';
+
+import ConfirmModal from './confirm_modal.jsx';
+
+export default class DeleteModalTrigger extends React.Component {
+ constructor(props) {
+ super(props);
+ if (this.constructor === DeleteModalTrigger) {
+ throw new TypeError('Can not construct abstract class.');
+ }
+ this.handleConfirm = this.handleConfirm.bind(this);
+ this.handleCancel = this.handleCancel.bind(this);
+ this.handleOpenModal = this.handleOpenModal.bind(this);
+
+ this.state = {
+ showDeleteModal: false
+ };
+ }
+
+ handleOpenModal(e) {
+ e.preventDefault();
+
+ this.setState({
+ showDeleteModal: true
+ });
+ }
+
+ handleConfirm(e) {
+ this.props.onDelete(e);
+ }
+
+ handleCancel() {
+ this.setState({
+ showDeleteModal: false
+ });
+ }
+
+ render() {
+ return (
+ <span>
+ <a
+ href='#'
+ onClick={this.handleOpenModal}
+ >
+ { this.triggerTitle }
+ </a>
+ <ConfirmModal
+ show={this.state.showDeleteModal}
+ title={this.modalTitle}
+ message={this.modalMessage}
+ confirmButton={this.modalConfirmButton}
+ onConfirm={this.handleConfirm}
+ onCancel={this.handleCancel}
+ />
+ </span>
+ );
+ }
+}
+
+DeleteModalTrigger.propTypes = {
+ onDelete: React.PropTypes.func.isRequired
+};
diff --git a/webapp/components/emoji/components/delete_emoji_modal.jsx b/webapp/components/emoji/components/delete_emoji_modal.jsx
new file mode 100644
index 000000000..604e3a27b
--- /dev/null
+++ b/webapp/components/emoji/components/delete_emoji_modal.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+
+import DeleteModalTrigger from '../../delete_modal_trigger.jsx';
+
+export default class DeleteEmoji extends DeleteModalTrigger {
+ get triggerTitle() {
+ return (
+ <FormattedMessage
+ id='emoji_list.delete'
+ defaultMessage='Delete'
+ />
+ );
+ }
+
+ get modalTitle() {
+ return (
+ <FormattedMessage
+ id='emoji_list.delete.confirm.title'
+ defaultMessage='Delete Custom Emoji'
+ />
+ );
+ }
+
+ get modalMessage() {
+ return (
+ <div className='alert alert-warning'>
+ <i className='fa fa-warning'/>
+ <FormattedMessage
+ id='emoji_list.delete.confirm.msg'
+ defaultMessage='This action permanently deletes the custom emoji. Are you sure you want to delete it?'
+ />
+ </div>
+ );
+ }
+
+ get modalConfirmButton() {
+ return (
+ <FormattedMessage
+ id='emoji_list.delete.confirm.button'
+ defaultMessage='Delete'
+ />
+ );
+ }
+}
+
+DeleteEmoji.propTypes = {
+ onDelete: React.PropTypes.func.isRequired
+};
diff --git a/webapp/components/emoji/components/emoji_list_item.jsx b/webapp/components/emoji/components/emoji_list_item.jsx
index f2ed82ba7..019b0ca93 100644
--- a/webapp/components/emoji/components/emoji_list_item.jsx
+++ b/webapp/components/emoji/components/emoji_list_item.jsx
@@ -4,6 +4,7 @@
import React from 'react';
import EmojiStore from 'stores/emoji_store.jsx';
+import DeleteEmoji from './delete_emoji_modal.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -80,15 +81,7 @@ export default class EmojiListItem extends React.Component {
let deleteButton = null;
if (this.props.onDelete) {
deleteButton = (
- <a
- href='#'
- onClick={this.handleDelete}
- >
- <FormattedMessage
- id='emoji_list.delete'
- defaultMessage='Delete'
- />
- </a>
+ <DeleteEmoji onDelete={this.handleDelete}/>
);
}
diff --git a/webapp/components/integrations/components/confirm_integration.jsx b/webapp/components/integrations/components/confirm_integration.jsx
index 6d778f241..b4f299d1c 100644
--- a/webapp/components/integrations/components/confirm_integration.jsx
+++ b/webapp/components/integrations/components/confirm_integration.jsx
@@ -5,7 +5,7 @@ import React from 'react';
import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-import {Link} from 'react-router/es6';
+import {Link, browserHistory} from 'react-router/es6';
import UserStore from 'stores/user_store.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
@@ -25,6 +25,7 @@ export default class ConfirmIntegration extends React.Component {
super(props);
this.handleIntegrationChange = this.handleIntegrationChange.bind(this);
+ this.handleKeyPress = this.handleKeyPress.bind(this);
const userId = UserStore.getCurrentId();
@@ -38,10 +39,12 @@ export default class ConfirmIntegration extends React.Component {
componentDidMount() {
IntegrationStore.addChangeListener(this.handleIntegrationChange);
+ window.addEventListener('keypress', this.handleKeyPress);
}
componentWillUnmount() {
IntegrationStore.removeChangeListener(this.handleIntegrationChange);
+ window.removeEventListener('keypress', this.handleKeyPress);
}
handleIntegrationChange() {
@@ -53,6 +56,12 @@ export default class ConfirmIntegration extends React.Component {
});
}
+ handleKeyPress(e) {
+ if (e.key === 'Enter') {
+ browserHistory.push('/' + this.props.team.name + '/integrations/' + this.state.type);
+ }
+ }
+
render() {
let headerText = null;
let helpText = null;
diff --git a/webapp/components/integrations/components/delete_integration.jsx b/webapp/components/integrations/components/delete_integration.jsx
index 442ac57f7..8e2e27596 100644
--- a/webapp/components/integrations/components/delete_integration.jsx
+++ b/webapp/components/integrations/components/delete_integration.jsx
@@ -1,48 +1,29 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
-import ConfirmModal from '../../confirm_modal.jsx';
+import DeleteModalTrigger from '../../delete_modal_trigger.jsx';
-export default class DeleteIntegration extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleConfirm = this.handleConfirm.bind(this);
- this.handleCancel = this.handleCancel.bind(this);
- this.handleOpenModal = this.handleOpenModal.bind(this);
-
- this.state = {
- showDeleteModal: false
- };
- }
-
- handleOpenModal(e) {
- e.preventDefault();
-
- this.setState({
- showDeleteModal: true
- });
- }
-
- handleConfirm() {
- this.props.onDelete();
- }
-
- handleCancel() {
- this.setState({
- showDeleteModal: false
- });
+export default class DeleteIntegration extends DeleteModalTrigger {
+ get triggerTitle() {
+ return (
+ <FormattedMessage
+ id='installed_integrations.delete'
+ defaultMessage='Delete'
+ />
+ );
}
- render() {
- const title = (
+ get modalTitle() {
+ return (
<FormattedMessage
id='integrations.delete.confirm.title'
defaultMessage='Delete Integration'
/>
);
+ }
- const message = (
+ get modalMessage() {
+ return (
<div className='alert alert-warning'>
<i className='fa fa-warning'/>
<FormattedMessage
@@ -51,35 +32,15 @@ export default class DeleteIntegration extends React.Component {
/>
</div>
);
+ }
- const confirmButton = (
+ get modalConfirmButton() {
+ return (
<FormattedMessage
id='integrations.delete.confirm.button'
defaultMessage='Delete'
/>
);
-
- return (
- <span>
- <a
- href='#'
- onClick={this.handleOpenModal}
- >
- <FormattedMessage
- id='installed_integrations.delete'
- defaultMessage='Delete'
- />
- </a>
- <ConfirmModal
- show={this.state.showDeleteModal}
- title={title}
- message={message}
- confirmButton={confirmButton}
- onConfirm={this.handleConfirm}
- onCancel={this.handleCancel}
- />
- </span>
- );
}
}
diff --git a/webapp/components/login/login_controller.jsx b/webapp/components/login/login_controller.jsx
index 38c594cf7..1d812147a 100644
--- a/webapp/components/login/login_controller.jsx
+++ b/webapp/components/login/login_controller.jsx
@@ -89,7 +89,7 @@ export default class LoginController extends React.Component {
}
// don't trim the password since we support spaces in passwords
- loginId = loginId.trim();
+ loginId = loginId.trim().toLowerCase();
if (!loginId) {
// it's slightly weird to be constructing the message ID, but it's a bit nicer than triply nested if statements
@@ -413,6 +413,7 @@ export default class LoginController extends React.Component {
</div>
<div className='form-group'>
<button
+ id='loginButton'
type='submit'
className='btn btn-primary'
>
diff --git a/webapp/components/navbar.jsx b/webapp/components/navbar.jsx
index c945a0b9c..28d8fae05 100644
--- a/webapp/components/navbar.jsx
+++ b/webapp/components/navbar.jsx
@@ -19,12 +19,15 @@ import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
+import SearchStore from 'stores/search_store.jsx';
import ChannelSwitchModal from './channel_switch_modal.jsx';
import * as Utils from 'utils/utils.jsx';
import * as ChannelUtils from 'utils/channel_utils.jsx';
import * as ChannelActions from 'actions/channel_actions.jsx';
+import * as GlobalActions from 'actions/global_actions.jsx';
+import {getPinnedPosts} from 'actions/post_actions.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
@@ -62,6 +65,7 @@ export default class Navbar extends React.Component {
this.hideChannelSwitchModal = this.hideChannelSwitchModal.bind(this);
this.openDirectMessageModal = this.openDirectMessageModal.bind(this);
+ this.getPinnedPosts = this.getPinnedPosts.bind(this);
const state = this.getStateFromStores();
state.showEditChannelPurposeModal = false;
@@ -216,6 +220,15 @@ export default class Navbar extends React.Component {
});
}
+ getPinnedPosts(e) {
+ e.preventDefault();
+ if (SearchStore.isPinnedPosts) {
+ GlobalActions.toggleSideBarAction(false);
+ } else {
+ getPinnedPosts(this.state.channel.id);
+ }
+ }
+
toggleFavorite = (e) => {
e.preventDefault();
@@ -244,6 +257,7 @@ export default class Navbar extends React.Component {
}
let viewInfoOption;
+ let viewPinnedPostsOption;
let addMembersOption;
let manageMembersOption;
let setChannelHeaderOption;
@@ -335,6 +349,21 @@ export default class Navbar extends React.Component {
</li>
);
+ viewPinnedPostsOption = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.getPinnedPosts}
+ >
+ <FormattedMessage
+ id='navbar.viewPinnedPosts'
+ defaultMessage='View Pinned Posts'
+ />
+ </a>
+ </li>
+ );
+
if (!ChannelStore.isDefault(channel)) {
addMembersOption = (
<li role='presentation'>
@@ -525,10 +554,10 @@ export default class Navbar extends React.Component {
id='channelHeader.removeFromFavorites'
defaultMessage='Remove from Favorites'
/> :
- <FormattedMessage
- id='channelHeader.addToFavorites'
- defaultMessage='Add to Favorites'
- />}
+ <FormattedMessage
+ id='channelHeader.addToFavorites'
+ defaultMessage='Add to Favorites'
+ />}
</a>
</li>
);
@@ -561,6 +590,7 @@ export default class Navbar extends React.Component {
role='menu'
>
{viewInfoOption}
+ {viewPinnedPostsOption}
{notificationPreferenceOption}
{addMembersOption}
{manageMembersOption}
diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx
index 5cb714ebc..11e75bfb7 100644
--- a/webapp/components/needs_team.jsx
+++ b/webapp/components/needs_team.jsx
@@ -12,6 +12,7 @@ import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
+import PostStore from 'stores/post_store.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from 'actions/status_actions.jsx';
import {startPeriodicSync, stopPeriodicSync} from 'actions/websocket_actions.jsx';
@@ -177,12 +178,25 @@ export default class NeedsTeam extends React.Component {
</div>
);
}
+
+ let channel = ChannelStore.getByName(this.props.params.channel);
+ if (channel == null) {
+ // the permalink view is not really tied to a particular channel but still needs it
+ const postId = PostStore.getFocusedPostId();
+ const post = PostStore.getEarliestPostFromPage(postId);
+
+ // the post take some time before being available on page load
+ if (post != null) {
+ channel = ChannelStore.get(post.channel_id);
+ }
+ }
+
return (
<div className='channel-view'>
<ErrorBar/>
<WebrtcNotification/>
<div className='container-fluid'>
- <SidebarRight/>
+ <SidebarRight channel={channel}/>
<SidebarRightMenu teamType={this.state.team.type}/>
<WebrtcSidebar/>
{content}
diff --git a/webapp/components/new_channel_modal.jsx b/webapp/components/new_channel_modal.jsx
index 2f9533b0e..f16b4596f 100644
--- a/webapp/components/new_channel_modal.jsx
+++ b/webapp/components/new_channel_modal.jsx
@@ -77,7 +77,7 @@ export default class NewChannelModal extends React.Component {
e.preventDefault();
const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
- if (displayName.length < 1) {
+ if (displayName.length < Constants.MIN_CHANNELNAME_LENGTH) {
this.setState({displayNameError: true});
return;
}
@@ -104,7 +104,7 @@ export default class NewChannelModal extends React.Component {
<p className='input__help error'>
<FormattedMessage
id='channel_modal.displayNameError'
- defaultMessage='This field is required'
+ defaultMessage='Channel name must be 2 or more characters'
/>
{this.state.displayNameError}
</p>
@@ -232,7 +232,7 @@ export default class NewChannelModal extends React.Component {
ref='display_name'
className='form-control'
placeholder={Utils.localizeMessage('channel_modal.nameEx', 'E.g.: "Bugs", "Marketing", "客户支持"')}
- maxLength='22'
+ maxLength={Constants.MAX_CHANNELNAME_LENGTH}
value={this.props.channelData.displayName}
autoFocus={true}
tabIndex='1'
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index bd2f744c7..1518b1ebf 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -233,7 +233,7 @@ export default class PopoverListMembers extends React.Component {
}
return (
- <div>
+ <div className='member-popover__container'>
<div
id='member_popover'
className='member-popover__trigger'
@@ -243,13 +243,11 @@ export default class PopoverListMembers extends React.Component {
AsyncClient.getProfilesInChannel(this.props.channel.id, 0);
}}
>
- <div>
- {countText}
- <span
- className='fa fa-user'
- aria-hidden='true'
- />
- </div>
+ {countText}
+ <span
+ className='fa fa-user'
+ aria-hidden='true'
+ />
</div>
<Overlay
rootClose={true}
diff --git a/webapp/components/post_view/components/date_separator.jsx b/webapp/components/post_view/components/date_separator.jsx
new file mode 100644
index 000000000..18dc0c7ff
--- /dev/null
+++ b/webapp/components/post_view/components/date_separator.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import {FormattedDate} from 'react-intl';
+
+export default class DateSeparator extends React.Component {
+ render() {
+ return (
+ <div
+ className='date-separator'
+ >
+ <hr className='separator__hr'/>
+ <div className='separator__text'>
+ <FormattedDate
+ value={this.props.date}
+ weekday='short'
+ month='short'
+ day='2-digit'
+ year='numeric'
+ />
+ </div>
+ </div>
+ );
+ }
+}
+
+DateSeparator.propTypes = {
+ date: React.PropTypes.instanceOf(Date)
+};
diff --git a/webapp/components/post_view/components/post_attachment_opengraph.jsx b/webapp/components/post_view/components/post_attachment_opengraph.jsx
index 12437e672..da85905c0 100644
--- a/webapp/components/post_view/components/post_attachment_opengraph.jsx
+++ b/webapp/components/post_view/components/post_attachment_opengraph.jsx
@@ -32,7 +32,6 @@ export default class PostAttachmentOpenGraph extends React.Component {
this.onImageLoad = this.onImageLoad.bind(this);
this.onImageError = this.onImageError.bind(this);
this.truncateText = this.truncateText.bind(this);
- this.setImageWidth = this.setImageWidth.bind(this);
}
IMAGE_LOADED = {
@@ -75,20 +74,16 @@ export default class PostAttachmentOpenGraph extends React.Component {
componentDidMount() {
OpenGraphStore.addUrlDataChangeListener(this.onOpenGraphMetadataChange);
- this.setImageWidth();
- window.addEventListener('resize', this.setImageWidth);
}
componentDidUpdate() {
if (this.props.childComponentDidUpdateFunction) {
this.props.childComponentDidUpdateFunction();
}
- this.setImageWidth();
}
componentWillUnmount() {
OpenGraphStore.removeUrlDataChangeListener(this.onOpenGraphMetadataChange);
- window.removeEventListener('resize', this.setImageWidth);
}
onOpenGraphMetadataChange(url) {
@@ -163,9 +158,6 @@ export default class PostAttachmentOpenGraph extends React.Component {
return (
<div
className='attachment__image__container--openraph'
- style={{
- width: (this.imageDimentions.height * this.imageRatio) + this.smallImageContainerLeftPadding
- }} // Initially set the width accordinly to max image heigh, ie 80px. Later on it would be modified according to actul height of image.
ref={(div) => {
this.smallImageContainer = div;
}}
@@ -215,20 +207,6 @@ export default class PostAttachmentOpenGraph extends React.Component {
return element;
}
- setImageWidth() {
- if (
- this.state.imageLoaded === this.IMAGE_LOADED.YES &&
- this.smallImageContainer &&
- this.smallImageElement
- ) {
- this.smallImageContainer.style.width = (
- (this.smallImageElement.offsetHeight * this.imageRatio) +
- this.smallImageContainerLeftPadding +
- 'px'
- );
- }
- }
-
truncateText(text, maxLength = this.textMaxLenght, ellipsis = this.textEllipsis) {
if (text.length > maxLength) {
return text.substring(0, maxLength - ellipsis.length) + ellipsis;
diff --git a/webapp/components/post_view/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index 331fdeb00..5318ec272 100644
--- a/webapp/components/post_view/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -26,6 +26,8 @@ export default class PostInfo extends React.Component {
this.removePost = this.removePost.bind(this);
this.flagPost = this.flagPost.bind(this);
this.unflagPost = this.unflagPost.bind(this);
+ this.pinPost = this.pinPost.bind(this);
+ this.unpinPost = this.unpinPost.bind(this);
this.canEdit = false;
this.canDelete = false;
@@ -148,6 +150,42 @@ export default class PostInfo extends React.Component {
);
}
+ if (this.props.post.is_pinned) {
+ dropdownContents.push(
+ <li
+ key='unpinLink'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unpinPost}
+ >
+ <FormattedMessage
+ id='post_info.unpin'
+ defaultMessage='Un-pin from channel'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='pinLink'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.pinPost}
+ >
+ <FormattedMessage
+ id='post_info.pin'
+ defaultMessage='Pin to channel'
+ />
+ </a>
+ </li>
+ );
+ }
+
if (this.canDelete) {
dropdownContents.push(
<li
@@ -250,6 +288,16 @@ export default class PostInfo extends React.Component {
);
}
+ pinPost(e) {
+ e.preventDefault();
+ PostActions.pinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
+ unpinPost(e) {
+ e.preventDefault();
+ PostActions.unpinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
flagPost(e) {
e.preventDefault();
PostActions.flagPost(this.props.post.id);
@@ -374,6 +422,18 @@ export default class PostInfo extends React.Component {
);
}
+ let pinnedBadge;
+ if (post.is_pinned) {
+ pinnedBadge = (
+ <span className='post__pinned-badge'>
+ <FormattedMessage
+ id='post_info.pinned'
+ defaultMessage='Pinned'
+ />
+ </span>
+ );
+ }
+
return (
<ul className='post__header--info'>
<li className='col'>
@@ -384,6 +444,7 @@ export default class PostInfo extends React.Component {
useMilitaryTime={this.props.useMilitaryTime}
postId={post.id}
/>
+ {pinnedBadge}
{flagTrigger}
</li>
{options}
diff --git a/webapp/components/post_view/components/post_time.jsx b/webapp/components/post_view/components/post_time.jsx
index 25d533e0a..77f3f3266 100644
--- a/webapp/components/post_view/components/post_time.jsx
+++ b/webapp/components/post_view/components/post_time.jsx
@@ -40,12 +40,15 @@ export default class PostTime extends React.Component {
}
renderTimeTag() {
+ const date = getDateForUnixTicks(this.props.eventTime);
+
return (
<time
className='post__time'
- dateTime={getDateForUnixTicks(this.props.eventTime).toISOString()}
+ dateTime={date.toISOString()}
+ title={date}
>
- {getDateForUnixTicks(this.props.eventTime).toLocaleString('en', {hour: '2-digit', minute: '2-digit', hour12: !this.props.useMilitaryTime})}
+ {date.toLocaleString('en', {hour: '2-digit', minute: '2-digit', hour12: !this.props.useMilitaryTime})}
</time>
);
}
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index cb527d850..52e4d9851 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -10,7 +10,7 @@ import ReactionListContainer from 'components/post_view/components/reaction_list
import RhsDropdown from 'components/rhs_dropdown.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
-import {flagPost, unflagPost} from 'actions/post_actions.jsx';
+import {flagPost, unflagPost, pinPost, unpinPost} from 'actions/post_actions.jsx';
import TeamStore from 'stores/team_store.jsx';
@@ -36,6 +36,8 @@ export default class RhsComment extends React.Component {
this.removePost = this.removePost.bind(this);
this.flagPost = this.flagPost.bind(this);
this.unflagPost = this.unflagPost.bind(this);
+ this.pinPost = this.pinPost.bind(this);
+ this.unpinPost = this.unpinPost.bind(this);
this.canEdit = false;
this.canDelete = false;
@@ -128,6 +130,16 @@ export default class RhsComment extends React.Component {
unflagPost(this.props.post.id);
}
+ pinPost(e) {
+ e.preventDefault();
+ pinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
+ unpinPost(e) {
+ e.preventDefault();
+ unpinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
createDropdown() {
const post = this.props.post;
@@ -195,6 +207,42 @@ export default class RhsComment extends React.Component {
</li>
);
+ if (post.is_pinned) {
+ dropdownContents.push(
+ <li
+ key='rhs-comment-unpin'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unpinPost}
+ >
+ <FormattedMessage
+ id='rhs_root.unpin'
+ defaultMessage='Un-pin from channel'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='rhs-comment-pin'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.pinPost}
+ >
+ <FormattedMessage
+ id='rhs_root.pin'
+ defaultMessage='Pin to channel'
+ />
+ </a>
+ </li>
+ );
+ }
+
if (this.canDelete) {
dropdownContents.push(
<li
@@ -503,10 +551,19 @@ export default class RhsComment extends React.Component {
);
}
+ let pinnedBadge;
+ if (post.is_pinned) {
+ pinnedBadge = (
+ <span className='post__pinned-badge'>
+ <FormattedMessage
+ id='post_info.pinned'
+ defaultMessage='Pinned'
+ />
+ </span>
+ );
+ }
+
const timeOptions = {
- day: 'numeric',
- month: 'short',
- year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: !this.props.useMilitaryTime
@@ -524,6 +581,7 @@ export default class RhsComment extends React.Component {
{botIndicator}
<li className='col'>
{this.renderTimeTag(post, timeOptions)}
+ {pinnedBadge}
{flagTrigger}
</li>
{options}
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 0c1037501..83d930bca 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -14,7 +14,7 @@ import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
-import {flagPost, unflagPost} from 'actions/post_actions.jsx';
+import {flagPost, unflagPost, pinPost, unpinPost} from 'actions/post_actions.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
@@ -35,6 +35,8 @@ export default class RhsRootPost extends React.Component {
this.handlePermalink = this.handlePermalink.bind(this);
this.flagPost = this.flagPost.bind(this);
this.unflagPost = this.unflagPost.bind(this);
+ this.pinPost = this.pinPost.bind(this);
+ this.unpinPost = this.unpinPost.bind(this);
this.canEdit = false;
this.canDelete = false;
@@ -143,6 +145,16 @@ export default class RhsRootPost extends React.Component {
);
}
+ pinPost(e) {
+ e.preventDefault();
+ pinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
+ unpinPost(e) {
+ e.preventDefault();
+ unpinPost(this.props.post.channel_id, this.props.post.id);
+ }
+
render() {
const post = this.props.post;
const user = this.props.user;
@@ -240,6 +252,42 @@ export default class RhsRootPost extends React.Component {
</li>
);
+ if (post.is_pinned) {
+ dropdownContents.push(
+ <li
+ key='rhs-root-unpin'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unpinPost}
+ >
+ <FormattedMessage
+ id='rhs_root.unpin'
+ defaultMessage='Un-pin from channel'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='rhs-root-pin'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.pinPost}
+ >
+ <FormattedMessage
+ id='rhs_root.pin'
+ defaultMessage='Pin to channel'
+ />
+ </a>
+ </li>
+ );
+ }
+
if (this.canDelete) {
dropdownContents.push(
<li
@@ -450,10 +498,19 @@ export default class RhsRootPost extends React.Component {
flagFunc = this.flagPost;
}
+ let pinnedBadge;
+ if (post.is_pinned) {
+ pinnedBadge = (
+ <span className='post__pinned-badge'>
+ <FormattedMessage
+ id='post_info.pinned'
+ defaultMessage='Pinned'
+ />
+ </span>
+ );
+ }
+
const timeOptions = {
- day: 'numeric',
- month: 'short',
- year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: !this.props.useMilitaryTime
@@ -470,6 +527,7 @@ export default class RhsRootPost extends React.Component {
{botIndicator}
<li className='col'>
{this.renderTimeTag(post, timeOptions)}
+ {pinnedBadge}
<OverlayTrigger
key={'rootpostflagtooltipkey' + flagVisible}
delayShow={Constants.OVERLAY_TIME_DELAY}
diff --git a/webapp/components/rhs_thread.jsx b/webapp/components/rhs_thread.jsx
index 3c0b2e114..2c1d03901 100644
--- a/webapp/components/rhs_thread.jsx
+++ b/webapp/components/rhs_thread.jsx
@@ -8,6 +8,7 @@ import RootPost from './rhs_root_post.jsx';
import Comment from './rhs_comment.jsx';
import FileUploadOverlay from './file_upload_overlay.jsx';
import FloatingTimestamp from './post_view/components/floating_timestamp.jsx';
+import DateSeparator from './post_view/components/date_separator.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -325,6 +326,7 @@ export default class RhsThread extends React.Component {
const postsArray = this.state.postsArray;
const selected = this.state.selected;
const profiles = this.state.profiles || {};
+ let previousPostDay = Utils.getDateForUnixTicks(selected.create_at);
if (postsArray == null || selected == null) {
return (
@@ -355,6 +357,55 @@ export default class RhsThread extends React.Component {
rootStatus = this.state.statuses[selected.user_id] || 'offline';
}
+ const commentsLists = [];
+ for (let i = 0; i < postsArray.length; i++) {
+ const comPost = postsArray[i];
+ let p;
+ if (UserStore.getCurrentId() === comPost.user_id) {
+ p = UserStore.getCurrentUser();
+ } else {
+ p = profiles[comPost.user_id];
+ }
+
+ let isFlagged = false;
+ if (this.state.flaggedPosts) {
+ isFlagged = this.state.flaggedPosts.get(comPost.id) === 'true';
+ }
+
+ let status = 'offline';
+ if (this.state.statuses && p && p.id) {
+ status = this.state.statuses[p.id] || 'offline';
+ }
+
+ const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
+
+ const currentPostDay = Utils.getDateForUnixTicks(comPost.create_at);
+
+ if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
+ previousPostDay = currentPostDay;
+ commentsLists.push(
+ <DateSeparator
+ date={currentPostDay}
+ />);
+ }
+
+ commentsLists.push(
+ <div key={keyPrefix + 'commentKey'}>
+ <Comment
+ ref={comPost.id}
+ post={comPost}
+ user={p}
+ currentUser={this.props.currentUser}
+ compactDisplay={this.state.compactDisplay}
+ useMilitaryTime={this.props.useMilitaryTime}
+ isFlagged={isFlagged}
+ status={status}
+ isBusy={this.state.isBusy}
+ />
+ </div>
+ );
+ }
+
return (
<div className='post-right__container'>
<FileUploadOverlay overlayType='right'/>
@@ -384,6 +435,9 @@ export default class RhsThread extends React.Component {
onScroll={this.handleScroll}
>
<div className='post-right__scroll'>
+ <DateSeparator
+ date={previousPostDay}
+ />
<RootPost
ref={selected.id}
post={selected}
@@ -401,41 +455,7 @@ export default class RhsThread extends React.Component {
ref='rhspostlist'
className='post-right-comments-container'
>
- {postsArray.map((comPost) => {
- let p;
- if (UserStore.getCurrentId() === comPost.user_id) {
- p = UserStore.getCurrentUser();
- } else {
- p = profiles[comPost.user_id];
- }
-
- let isFlagged = false;
- if (this.state.flaggedPosts) {
- isFlagged = this.state.flaggedPosts.get(comPost.id) === 'true';
- }
-
- let status = 'offline';
- if (this.state.statuses && p && p.id) {
- status = this.state.statuses[p.id] || 'offline';
- }
-
- const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
-
- return (
- <Comment
- ref={comPost.id}
- key={keyPrefix + 'commentKey'}
- post={comPost}
- user={p}
- currentUser={this.props.currentUser}
- compactDisplay={this.state.compactDisplay}
- useMilitaryTime={this.props.useMilitaryTime}
- isFlagged={isFlagged}
- status={status}
- isBusy={this.state.isBusy}
- />
- );
- })}
+ {commentsLists}
</div>
<div className='post-create__container'>
<CreateComment
diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx
index 1c9f607e6..b88e67a11 100644
--- a/webapp/components/search_bar.jsx
+++ b/webapp/components/search_bar.jsx
@@ -216,7 +216,10 @@ export default class SearchBar extends React.Component {
);
const flaggedTooltip = (
- <Tooltip id='flaggedTooltip'>
+ <Tooltip
+ id='flaggedTooltip'
+ className='text-nowrap'
+ >
<FormattedMessage
id='channel_header.flagged'
defaultMessage='Flagged Posts'
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 4c0105738..ce72ec3b1 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -213,6 +213,37 @@ export default class SearchResults extends React.Component {
</ul>
</div>
);
+ } else if (this.props.isPinnedPosts && noResults) {
+ ctls = (
+ <div className='sidebar--right__subheader'>
+ <ul>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usagePin1'
+ defaultMessage='There are no pinned messages yet.'
+ />
+ </li>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usagePin2'
+ defaultMessage='All members of this channel can pin important or useful messages.'
+ />
+ </li>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usagePin3'
+ defaultMessage='Pinned messages are visible to all channel members.'
+ />
+ </li>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usagePin4'
+ defaultMessage={'To pin a message: Go to the message that you want to pin and click [...] > "Pin to channel".'}
+ />
+ </li>
+ </ul>
+ </div>
+ );
} else if (!searchTerm && noResults) {
ctls = (
<div className='sidebar--right__subheader'>
@@ -289,6 +320,8 @@ export default class SearchResults extends React.Component {
toggleSize={this.props.toggleSize}
shrink={this.props.shrink}
isFlaggedPosts={this.props.isFlaggedPosts}
+ isPinnedPosts={this.props.isPinnedPosts}
+ channelDisplayName={this.props.channelDisplayName}
/>
<div
id='search-items-container'
@@ -307,5 +340,7 @@ SearchResults.propTypes = {
useMilitaryTime: React.PropTypes.bool.isRequired,
toggleSize: React.PropTypes.func,
shrink: React.PropTypes.func,
- isFlaggedPosts: React.PropTypes.bool
+ isFlaggedPosts: React.PropTypes.bool,
+ isPinnedPosts: React.PropTypes.bool,
+ channelDisplayName: React.PropTypes.string.isRequired
};
diff --git a/webapp/components/search_results_header.jsx b/webapp/components/search_results_header.jsx
index 1f2818e98..288d883ee 100644
--- a/webapp/components/search_results_header.jsx
+++ b/webapp/components/search_results_header.jsx
@@ -79,6 +79,16 @@ export default class SearchResultsHeader extends React.Component {
defaultMessage='Flagged Posts'
/>
);
+ } else if (this.props.isPinnedPosts) {
+ title = (
+ <FormattedMessage
+ id='search_header.title4'
+ defaultMessage='Pinned posts in {channelDisplayName}'
+ values={{
+ channelDisplayName: this.props.channelDisplayName
+ }}
+ />
+ );
}
return (
@@ -131,5 +141,7 @@ SearchResultsHeader.propTypes = {
isMentionSearch: React.PropTypes.bool,
toggleSize: React.PropTypes.func,
shrink: React.PropTypes.func,
- isFlaggedPosts: React.PropTypes.bool
+ isFlaggedPosts: React.PropTypes.bool,
+ isPinnedPosts: React.PropTypes.bool,
+ channelDisplayName: React.PropTypes.string.isRequired
};
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index b3de3492c..1c7309f51 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -289,6 +289,18 @@ export default class SearchResultsItem extends React.Component {
);
}
+ let pinnedBadge;
+ if (post.is_pinned) {
+ pinnedBadge = (
+ <span className='post__pinned-badge'>
+ <FormattedMessage
+ id='post_info.pinned'
+ defaultMessage='Pinned'
+ />
+ </span>
+ );
+ }
+
return (
<div className='search-item__container'>
<div className='date-separator'>
@@ -322,6 +334,7 @@ export default class SearchResultsItem extends React.Component {
{botIndicator}
<li className='col'>
{this.renderTimeTag(post)}
+ {pinnedBadge}
{flagContent}
</li>
{rhsControls}
diff --git a/webapp/components/searchable_user_list.jsx b/webapp/components/searchable_user_list.jsx
index d25c8a506..ab3f9ee9b 100644
--- a/webapp/components/searchable_user_list.jsx
+++ b/webapp/components/searchable_user_list.jsx
@@ -19,6 +19,7 @@ export default class SearchableUserList extends React.Component {
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.doSearch = this.doSearch.bind(this);
+ this.focusSearchBar = this.focusSearchBar.bind(this);
this.nextTimeoutId = 0;
@@ -30,15 +31,14 @@ export default class SearchableUserList extends React.Component {
}
componentDidMount() {
- if (this.props.focusOnMount) {
- this.refs.filter.focus();
- }
+ this.focusSearchBar();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.page !== prevState.page) {
$(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
}
+ this.focusSearchBar();
}
componentWillUnmount() {
@@ -57,6 +57,12 @@ export default class SearchableUserList extends React.Component {
this.setState({page: this.state.page - 1});
}
+ focusSearchBar() {
+ if (this.props.focusOnMount) {
+ this.refs.filter.focus();
+ }
+ }
+
doSearch() {
const term = this.refs.filter.value;
this.props.search(term);
diff --git a/webapp/components/setting_item_max.jsx b/webapp/components/setting_item_max.jsx
index 5b6a5d53a..9f3c4f0cf 100644
--- a/webapp/components/setting_item_max.jsx
+++ b/webapp/components/setting_item_max.jsx
@@ -31,12 +31,30 @@ export default class SettingItemMax extends React.Component {
render() {
var clientError = null;
if (this.props.client_error) {
- clientError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.client_error}</label></div>);
+ clientError = (
+ <div className='form-group'>
+ <label
+ id='clientError'
+ className='col-sm-12 has-error'
+ >
+ {this.props.client_error}
+ </label>
+ </div>
+ );
}
var serverError = null;
if (this.props.server_error) {
- serverError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.server_error}</label></div>);
+ serverError = (
+ <div className='form-group'>
+ <label
+ id='serverError'
+ className='col-sm-12 has-error'
+ >
+ {this.props.server_error}
+ </label>
+ </div>
+ );
}
var extraInfo = null;
@@ -48,6 +66,7 @@ export default class SettingItemMax extends React.Component {
if (this.props.submit) {
submit = (
<input
+ id='saveSetting'
type='submit'
className='btn btn-sm btn-primary'
href='#'
@@ -88,6 +107,7 @@ export default class SettingItemMax extends React.Component {
{clientError}
{submit}
<a
+ id={this.props.title + 'Cancel'}
className='btn btn-sm'
href='#'
onClick={this.props.updateSection}
diff --git a/webapp/components/setting_item_min.jsx b/webapp/components/setting_item_min.jsx
index 96d8bf459..4f756c46e 100644
--- a/webapp/components/setting_item_min.jsx
+++ b/webapp/components/setting_item_min.jsx
@@ -12,6 +12,7 @@ export default class SettingItemMin extends React.Component {
editButton = (
<li className='col-xs-12 col-sm-3 section-edit'>
<a
+ id={this.props.title}
className='theme'
href='#'
onClick={this.props.updateSection}
@@ -33,7 +34,12 @@ export default class SettingItemMin extends React.Component {
>
<li className='col-xs-12 col-sm-9 section-title'>{this.props.title}</li>
{editButton}
- <li className='col-xs-12 section-describe'>{this.props.describe}</li>
+ <li
+ id={this.props.title + 'Desc'}
+ className='col-xs-12 section-describe'
+ >
+ {this.props.describe}
+ </li>
</ul>
);
}
diff --git a/webapp/components/setting_picture.jsx b/webapp/components/setting_picture.jsx
index b74ee8eb7..45ac4096d 100644
--- a/webapp/components/setting_picture.jsx
+++ b/webapp/components/setting_picture.jsx
@@ -1,8 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
import loadingGif from 'images/load.gif';
@@ -14,17 +12,35 @@ export default class SettingPicture extends React.Component {
super(props);
this.setPicture = this.setPicture.bind(this);
+ this.confirmImage = this.confirmImage.bind(this);
}
setPicture(file) {
if (file) {
var reader = new FileReader();
- var img = ReactDOM.findDOMNode(this.refs.image);
- reader.onload = function load(e) {
- $(img).attr('src', e.target.result);
- };
+ reader.onload = (e) => {
+ const canvas = this.refs.profileImageCanvas;
+ const context = canvas.getContext('2d');
+ const imageObj = new Image();
+ imageObj.onload = () => {
+ if (imageObj.width > imageObj.height) {
+ const side = imageObj.height;
+ const rem = imageObj.width - side;
+ const startX = parseInt(rem / 2, 10);
+ context.drawImage(imageObj, startX, 0, side, side,
+ 0, 0, canvas.width, canvas.height);
+ } else {
+ const side = imageObj.width;
+ const rem = imageObj.height - side;
+ const startY = parseInt(rem / 2, 10);
+ context.drawImage(imageObj, 0, startY, side, side,
+ 0, 0, canvas.width, canvas.height);
+ }
+ };
+ imageObj.src = e.target.result;
+ };
reader.readAsDataURL(file);
}
}
@@ -48,10 +64,11 @@ export default class SettingPicture extends React.Component {
var img = null;
if (this.props.picture) {
img = (
- <img
- ref='image'
- className='profile-img rounded'
- src=''
+ <canvas
+ ref='profileImageCanvas'
+ className='profile-img'
+ width='256px'
+ height='256px'
/>
);
} else {
@@ -83,7 +100,7 @@ export default class SettingPicture extends React.Component {
confirmButton = (
<a
className={confirmButtonClass}
- onClick={this.props.submit}
+ onClick={this.confirmImage}
>
<FormattedMessage
id='setting_picture.save'
@@ -147,6 +164,16 @@ export default class SettingPicture extends React.Component {
</ul>
);
}
+
+ confirmImage(e) {
+ e.persist();
+ this.refs.profileImageCanvas.toBlob((blob) => {
+ blob.lastModifiedDate = new Date();
+ blob.name = 'image.jpg';
+ this.props.imageCropChange(blob);
+ this.props.submit(e);
+ }, 'image/jpeg', 0.95);
+ }
}
SettingPicture.propTypes = {
@@ -158,5 +185,6 @@ SettingPicture.propTypes = {
submitActive: React.PropTypes.bool,
submit: React.PropTypes.func,
title: React.PropTypes.string,
- pictureChange: React.PropTypes.func
+ pictureChange: React.PropTypes.func,
+ imageCropChange: React.PropTypes.func
};
diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx
index 08d80d363..b9356c5a1 100644
--- a/webapp/components/sidebar.jsx
+++ b/webapp/components/sidebar.jsx
@@ -636,13 +636,15 @@ export default class Sidebar extends React.Component {
this.lastUnreadChannel = null;
// create elements for all 4 types of channels
- const favoriteItems = this.state.favoriteChannels.map((channel, index, arr) => {
- if (channel.type === Constants.DM_CHANNEL) {
- return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
- }
+ const favoriteItems = this.state.favoriteChannels.
+ sort(Utils.sortTeamsByDisplayName).
+ map((channel, index, arr) => {
+ if (channel.type === Constants.DM_CHANNEL) {
+ return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
+ }
- return this.createChannelElement(channel);
- });
+ return this.createChannelElement(channel);
+ });
const publicChannelItems = this.state.publicChannels.map(this.createChannelElement);
diff --git a/webapp/components/sidebar_header_dropdown.jsx b/webapp/components/sidebar_header_dropdown.jsx
index 484ca3298..34c228ac2 100644
--- a/webapp/components/sidebar_header_dropdown.jsx
+++ b/webapp/components/sidebar_header_dropdown.jsx
@@ -467,6 +467,7 @@ export default class SidebarHeaderDropdown extends React.Component {
<Dropdown.Menu>
<li>
<a
+ id='accountSettings'
href='#'
onClick={this.toggleAccountSettingsModal}
>
@@ -480,6 +481,7 @@ export default class SidebarHeaderDropdown extends React.Component {
{teamLink}
<li>
<a
+ id='logout'
href='#'
onClick={() => GlobalActions.emitUserLoggedOutEvent()}
>
diff --git a/webapp/components/sidebar_right.jsx b/webapp/components/sidebar_right.jsx
index fb120337a..42b7381f4 100644
--- a/webapp/components/sidebar_right.jsx
+++ b/webapp/components/sidebar_right.jsx
@@ -11,13 +11,13 @@ import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import WebrtcStore from 'stores/webrtc_store.jsx';
-import {getFlaggedPosts} from 'actions/post_actions.jsx';
+import {getFlaggedPosts, getPinnedPosts} from 'actions/post_actions.jsx';
import {trackEvent} from 'actions/diagnostics_actions.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
-import React from 'react';
+import React, {PropTypes} from 'react';
export default class SidebarRight extends React.Component {
constructor(props) {
@@ -27,6 +27,7 @@ export default class SidebarRight extends React.Component {
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onSelectedChange = this.onSelectedChange.bind(this);
+ this.onPostPinnedChange = this.onPostPinnedChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
this.onShowSearch = this.onShowSearch.bind(this);
@@ -39,6 +40,7 @@ export default class SidebarRight extends React.Component {
searchVisible: SearchStore.getSearchResults() !== null,
isMentionSearch: SearchStore.getIsMentionSearch(),
isFlaggedPosts: SearchStore.getIsFlaggedPosts(),
+ isPinnedPosts: SearchStore.getIsPinnedPosts(),
postRightVisible: Boolean(PostStore.getSelectedPost()),
expanded: false,
fromSearch: false,
@@ -50,6 +52,7 @@ export default class SidebarRight extends React.Component {
componentDidMount() {
SearchStore.addSearchChangeListener(this.onSearchChange);
PostStore.addSelectedPostChangeListener(this.onSelectedChange);
+ PostStore.addPostPinnedChangeListener(this.onPostPinnedChange);
SearchStore.addShowSearchListener(this.onShowSearch);
UserStore.addChangeListener(this.onUserChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
@@ -59,6 +62,7 @@ export default class SidebarRight extends React.Component {
componentWillUnmount() {
SearchStore.removeSearchChangeListener(this.onSearchChange);
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
+ PostStore.removePostPinnedChangeListener(this.onPostPinnedChange);
SearchStore.removeShowSearchListener(this.onShowSearch);
UserStore.removeChangeListener(this.onUserChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
@@ -137,6 +141,12 @@ export default class SidebarRight extends React.Component {
});
}
+ onPostPinnedChange() {
+ if (this.props.channel && this.state.isPinnedPosts) {
+ getPinnedPosts(this.props.channel.id);
+ }
+ }
+
onShrink() {
this.setState({
expanded: false
@@ -147,7 +157,8 @@ export default class SidebarRight extends React.Component {
this.setState({
searchVisible: SearchStore.getSearchResults() !== null,
isMentionSearch: SearchStore.getIsMentionSearch(),
- isFlaggedPosts: SearchStore.getIsFlaggedPosts()
+ isFlaggedPosts: SearchStore.getIsFlaggedPosts(),
+ isPinnedPosts: SearchStore.getIsPinnedPosts()
});
}
@@ -182,9 +193,11 @@ export default class SidebarRight extends React.Component {
<SearchResults
isMentionSearch={this.state.isMentionSearch}
isFlaggedPosts={this.state.isFlaggedPosts}
+ isPinnedPosts={this.state.isPinnedPosts}
useMilitaryTime={this.state.useMilitaryTime}
toggleSize={this.toggleSize}
shrink={this.onShrink}
+ channelDisplayName={this.props.channel ? this.props.channel.display_name : ''}
/>
);
} else if (this.state.postRightVisible) {
@@ -222,3 +235,7 @@ export default class SidebarRight extends React.Component {
);
}
}
+
+SidebarRight.propTypes = {
+ channel: PropTypes.object
+};
diff --git a/webapp/components/team_general_tab.jsx b/webapp/components/team_general_tab.jsx
index 0100cad64..288faae6c 100644
--- a/webapp/components/team_general_tab.jsx
+++ b/webapp/components/team_general_tab.jsx
@@ -106,14 +106,11 @@ class GeneralTab extends React.Component {
let valid = true;
const name = this.state.name.trim();
- if (!name) {
+ if (name) {
+ state.clientError = '';
+ } else {
state.clientError = Utils.localizeMessage('general_tab.required', 'This field is required');
valid = false;
- } else if (name === this.props.team.display_name) {
- state.clientError = Utils.localizeMessage('general_tab.chooseName', 'Please choose a new name for your team');
- valid = false;
- } else {
- state.clientError = '';
}
this.setState(state);
@@ -278,6 +275,7 @@ class GeneralTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='teamOpenInvite'
name='userOpenInviteOptions'
type='radio'
defaultChecked={this.state.allow_open_invite}
@@ -293,6 +291,7 @@ class GeneralTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='teamOpenInviteNo'
name='userOpenInviteOptions'
type='radio'
defaultChecked={!this.state.allow_open_invite}
@@ -352,6 +351,7 @@ class GeneralTab extends React.Component {
<label className='col-sm-5 control-label visible-xs-block'/>
<div className='col-sm-12'>
<input
+ id='teamInviteId'
className='form-control'
type='text'
onChange={this.updateInviteId}
@@ -360,6 +360,7 @@ class GeneralTab extends React.Component {
/>
<div className='padding-top x2'>
<a
+ id='teamInviteIdRegenerate'
href='#'
onClick={this.handleGenerateInviteId}
>
@@ -433,6 +434,7 @@ class GeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{teamNameLabel}</label>
<div className='col-sm-7'>
<input
+ id='teamName'
className='form-control'
type='text'
maxLength={Constants.MAX_TEAMNAME_LENGTH.toString()}
@@ -491,6 +493,7 @@ class GeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{teamDescriptionLabel}</label>
<div className='col-sm-7'>
<input
+ id='teamDescription'
className='form-control'
type='text'
maxLength={Constants.MAX_TEAMDESCRIPTION_LENGTH.toString()}
@@ -540,6 +543,7 @@ class GeneralTab extends React.Component {
<div>
<div className='modal-header'>
<button
+ id='closeButton'
type='button'
className='close'
data-dismiss='modal'
diff --git a/webapp/components/user_settings/desktop_notification_settings.jsx b/webapp/components/user_settings/desktop_notification_settings.jsx
index 3a330b623..be403ebb6 100644
--- a/webapp/components/user_settings/desktop_notification_settings.jsx
+++ b/webapp/components/user_settings/desktop_notification_settings.jsx
@@ -74,6 +74,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundOn'
type='radio'
name='notificationSounds'
checked={soundRadio[0]}
@@ -89,6 +90,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundOff'
type='radio'
name='notificationSounds'
checked={soundRadio[1]}
@@ -136,6 +138,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundDuration3'
type='radio'
name='desktopDuration'
checked={durationRadio[0]}
@@ -154,6 +157,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundDuration5'
type='radio'
name='desktopDuration'
checked={durationRadio[1]}
@@ -172,6 +176,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundDuration10'
type='radio'
name='desktopDuration'
checked={durationRadio[2]}
@@ -189,6 +194,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='soundDurationUnlimited'
type='radio'
name='desktopDuration'
checked={durationRadio[3]}
@@ -225,6 +231,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='desktopNotificationAllActivity'
type='radio'
name='desktopNotificationLevel'
checked={activityRadio[0]}
@@ -240,6 +247,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='desktopNotificationMentions'
type='radio'
name='desktopNotificationLevel'
checked={activityRadio[1]}
@@ -255,6 +263,7 @@ export default class DesktopNotificationSettings extends React.Component {
<div className='radio'>
<label>
<input
+ id='desktopNotificationNever'
type='radio'
name='desktopNotificationLevel'
checked={activityRadio[2]}
diff --git a/webapp/components/user_settings/email_notification_setting.jsx b/webapp/components/user_settings/email_notification_setting.jsx
index 457512507..1e6c5d7f5 100644
--- a/webapp/components/user_settings/email_notification_setting.jsx
+++ b/webapp/components/user_settings/email_notification_setting.jsx
@@ -113,6 +113,7 @@ export default class EmailNotificationSetting extends React.Component {
<div className='radio'>
<label>
<input
+ id='emailNotificationMinutes'
type='radio'
name='emailNotifications'
checked={this.props.enableEmail && this.state.emailInterval === Preferences.INTERVAL_FIFTEEN_MINUTES}
@@ -128,6 +129,7 @@ export default class EmailNotificationSetting extends React.Component {
<div className='radio'>
<label>
<input
+ id='emailNotificationHour'
type='radio'
name='emailNotifications'
checked={this.props.enableEmail && this.state.emailInterval === Preferences.INTERVAL_HOUR}
@@ -164,6 +166,7 @@ export default class EmailNotificationSetting extends React.Component {
<div className='radio'>
<label>
<input
+ id='emailNotificationImmediately'
type='radio'
name='emailNotifications'
checked={this.props.enableEmail && this.state.emailInterval === Preferences.INTERVAL_IMMEDIATE}
@@ -179,6 +182,7 @@ export default class EmailNotificationSetting extends React.Component {
<div className='radio'>
<label>
<input
+ id='emailNotificationNever'
type='radio'
name='emailNotifications'
checked={!this.props.enableEmail}
diff --git a/webapp/components/user_settings/import_theme_modal.jsx b/webapp/components/user_settings/import_theme_modal.jsx
index 7ae057cc4..9d3cff53d 100644
--- a/webapp/components/user_settings/import_theme_modal.jsx
+++ b/webapp/components/user_settings/import_theme_modal.jsx
@@ -173,6 +173,7 @@ export default class ImportThemeModal extends React.Component {
<div className='form-group less'>
<div className='col-sm-12'>
<input
+ id='themeVector'
type='text'
className='form-control'
value={this.state.value}
@@ -186,6 +187,7 @@ export default class ImportThemeModal extends React.Component {
</Modal.Body>
<Modal.Footer>
<button
+ id='cancelButton'
type='button'
className='btn btn-default'
onClick={() => this.setState({show: false})}
@@ -196,6 +198,7 @@ export default class ImportThemeModal extends React.Component {
/>
</button>
<button
+ id='submitButton'
onClick={this.handleSubmit}
type='submit'
className='btn btn-primary'
diff --git a/webapp/components/user_settings/manage_languages.jsx b/webapp/components/user_settings/manage_languages.jsx
index 4f5eb223d..2955c8030 100644
--- a/webapp/components/user_settings/manage_languages.jsx
+++ b/webapp/components/user_settings/manage_languages.jsx
@@ -96,6 +96,7 @@ export default class ManageLanguage extends React.Component {
</label>
<div className='padding-top'>
<select
+ id='displayLanguage'
ref='language'
className='form-control'
value={this.state.locale}
diff --git a/webapp/components/user_settings/user_settings_advanced.jsx b/webapp/components/user_settings/user_settings_advanced.jsx
index 3459af8b3..970856acc 100644
--- a/webapp/components/user_settings/user_settings_advanced.jsx
+++ b/webapp/components/user_settings/user_settings_advanced.jsx
@@ -185,6 +185,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='postFormattingOn'
type='radio'
name='formatting'
checked={this.state.settings.formatting !== 'false'}
@@ -200,6 +201,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='postFormattingOff'
type='radio'
name='formatting'
checked={this.state.settings.formatting === 'false'}
@@ -261,6 +263,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='joinLeaveOn'
type='radio'
name='join_leave'
checked={this.state.settings.join_leave !== 'false'}
@@ -276,6 +279,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='joinLeaveOff'
type='radio'
name='join_leave'
checked={this.state.settings.join_leave === 'false'}
@@ -367,6 +371,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='ctrlSendOn'
type='radio'
name='sendOnCtrlEnter'
checked={ctrlSendActive[0]}
@@ -382,6 +387,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='ctrlSendOff'
type='radio'
name='sendOnCtrlEnter'
checked={ctrlSendActive[1]}
@@ -464,6 +470,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div className='checkbox'>
<label>
<input
+ id={'advancedPreviewFeatures' + feature.label}
type='checkbox'
checked={this.state.settings[Constants.FeatureTogglePrefix + feature.label] === 'true'}
onChange={(e) => {
@@ -524,6 +531,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
<div>
<div className='modal-header'>
<button
+ id='closeButton'
type='button'
className='close'
data-dismiss='modal'
diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx
index f51128b6f..60f322467 100644
--- a/webapp/components/user_settings/user_settings_display.jsx
+++ b/webapp/components/user_settings/user_settings_display.jsx
@@ -160,6 +160,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='collapseFormat'
type='radio'
name='collapseFormat'
checked={collapseFormat[0]}
@@ -175,6 +176,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='collapseFormatOff'
type='radio'
name='collapseFormat'
checked={collapseFormat[1]}
@@ -277,6 +279,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='clockFormat12h'
type='radio'
name='clockFormat'
checked={clockFormat[0]}
@@ -292,6 +295,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='clockFormat24h'
type='radio'
name='clockFormat'
checked={clockFormat[1]}
@@ -397,6 +401,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='nameFormatUsername'
type='radio'
name='nameFormat'
checked={nameFormat[1]}
@@ -409,6 +414,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='nameFormatNickname'
type='radio'
name='nameFormat'
checked={nameFormat[0]}
@@ -421,6 +427,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='nameFormatFullName'
type='radio'
name='nameFormat'
checked={nameFormat[2]}
@@ -511,6 +518,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='messageFormatStandard'
type='radio'
name='messageDisplay'
checked={messageDisplay[0]}
@@ -533,6 +541,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='messageFormatCompact'
type='radio'
name='messageDisplay'
checked={messageDisplay[1]}
@@ -626,6 +635,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelDisplayFormatFullScreen'
type='radio'
name='channelDisplayMode'
checked={channelDisplayMode[0]}
@@ -641,6 +651,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='radio'>
<label>
<input
+ id='channelDisplayFormatCentered'
type='radio'
name='channelDisplayMode'
checked={channelDisplayMode[1]}
@@ -735,6 +746,7 @@ export default class UserSettingsDisplay extends React.Component {
className='dropdown'
>
<select
+ id='displayFontSelect'
className='form-control'
type='text'
value={this.state.selectedFont}
@@ -830,6 +842,7 @@ export default class UserSettingsDisplay extends React.Component {
<div>
<div className='modal-header'>
<button
+ id='closeButton'
type='button'
className='close'
data-dismiss='modal'
diff --git a/webapp/components/user_settings/user_settings_general.jsx b/webapp/components/user_settings/user_settings_general.jsx
index f9c624aa0..d558958f0 100644
--- a/webapp/components/user_settings/user_settings_general.jsx
+++ b/webapp/components/user_settings/user_settings_general.jsx
@@ -101,6 +101,7 @@ class UserSettingsGeneralTab extends React.Component {
this.updatePicture = this.updatePicture.bind(this);
this.updateSection = this.updateSection.bind(this);
this.updatePosition = this.updatePosition.bind(this);
+ this.updatedCroppedPicture = this.updatedCroppedPicture.bind(this);
this.state = this.setupInitialState(props);
}
@@ -311,6 +312,17 @@ class UserSettingsGeneralTab extends React.Component {
this.setState({confirmEmail: e.target.value});
}
+ updatedCroppedPicture(file) {
+ if (file) {
+ this.setState({picture: file});
+
+ this.submitActive = true;
+ this.setState({clientError: null});
+ } else {
+ this.setState({picture: null});
+ }
+ }
+
updatePicture(e) {
if (e.target.files && e.target.files[0]) {
this.setState({picture: e.target.files[0]});
@@ -410,6 +422,7 @@ class UserSettingsGeneralTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='primaryEmail'
className='form-control'
type='email'
onChange={this.updateEmail}
@@ -431,6 +444,7 @@ class UserSettingsGeneralTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='confirmEmail'
className='form-control'
type='email'
onChange={this.updateConfirmEmail}
@@ -684,6 +698,7 @@ class UserSettingsGeneralTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='firstName'
className='form-control'
type='text'
onChange={this.updateFirstName}
@@ -706,6 +721,7 @@ class UserSettingsGeneralTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='lastName'
className='form-control'
type='text'
onChange={this.updateLastName}
@@ -832,6 +848,7 @@ class UserSettingsGeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{nicknameLabel}</label>
<div className='col-sm-7'>
<input
+ id='nickname'
className='form-control'
type='text'
onChange={this.updateNickname}
@@ -916,6 +933,7 @@ class UserSettingsGeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{usernameLabel}</label>
<div className='col-sm-7'>
<input
+ id='username'
maxLength={Constants.MAX_USERNAME_LENGTH}
className='form-control'
type='text'
@@ -1006,6 +1024,7 @@ class UserSettingsGeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{positionLabel}</label>
<div className='col-sm-7'>
<input
+ id='position'
className='form-control'
type='text'
onChange={this.updatePosition}
@@ -1086,6 +1105,7 @@ class UserSettingsGeneralTab extends React.Component {
pictureChange={this.updatePicture}
submitActive={this.submitActive}
loadingPicture={this.state.loadingPicture}
+ imageCropChange={this.updatedCroppedPicture}
/>
);
} else {
@@ -1123,6 +1143,7 @@ class UserSettingsGeneralTab extends React.Component {
<div>
<div className='modal-header'>
<button
+ id='closeUserSettings'
type='button'
className='close'
data-dismiss='modal'
diff --git a/webapp/components/user_settings/user_settings_notifications.jsx b/webapp/components/user_settings/user_settings_notifications.jsx
index 7c82488f6..ebd43e5af 100644
--- a/webapp/components/user_settings/user_settings_notifications.jsx
+++ b/webapp/components/user_settings/user_settings_notifications.jsx
@@ -64,6 +64,9 @@ function getNotificationsStateFromStores() {
} else {
usernameKey = true;
keys.splice(keys.indexOf(user.username), 1);
+ if (keys.indexOf(`@${user.username}`) !== -1) {
+ keys.splice(keys.indexOf(`@${user.username}`), 1);
+ }
}
customKeys = keys.join(',');
@@ -281,6 +284,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationOnline'
type='radio'
name='pushNotificationStatus'
checked={pushStatusRadio[0]}
@@ -296,6 +300,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationAway'
type='radio'
name='pushNotificationStatus'
checked={pushStatusRadio[1]}
@@ -311,6 +316,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationOffline'
type='radio'
name='pushNotificationStatus'
checked={pushStatusRadio[2]}
@@ -347,6 +353,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationAllActivity'
type='radio'
name='pushNotificationLevel'
checked={pushActivityRadio[0]}
@@ -362,6 +369,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationMentions'
type='radio'
name='pushNotificationLevel'
checked={pushActivityRadio[1]}
@@ -377,6 +385,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='pushNotificationNever'
type='radio'
name='pushNotificationLevel'
checked={pushActivityRadio[2]}
@@ -520,6 +529,7 @@ export default class NotificationsTab extends React.Component {
<div className='checkbox'>
<label>
<input
+ id='notificationTriggerFirst'
type='checkbox'
checked={this.state.firstNameKey}
onChange={handleUpdateFirstNameKey}
@@ -545,6 +555,7 @@ export default class NotificationsTab extends React.Component {
<div className='checkbox'>
<label>
<input
+ id='notificationTriggerUsername'
type='checkbox'
checked={this.state.usernameKey}
onChange={handleUpdateUsernameKey}
@@ -569,6 +580,7 @@ export default class NotificationsTab extends React.Component {
<div className='checkbox'>
<label>
<input
+ id='notificationTriggerShouts'
type='checkbox'
checked={this.state.channelKey}
onChange={handleUpdateChannelKey}
@@ -587,6 +599,7 @@ export default class NotificationsTab extends React.Component {
<div className='checkbox'>
<label>
<input
+ id='notificationTriggerCustom'
ref='customcheck'
type='checkbox'
checked={this.state.customKeysChecked}
@@ -599,6 +612,7 @@ export default class NotificationsTab extends React.Component {
</label>
</div>
<input
+ id='notificationTriggerCustomText'
ref='custommentions'
className='form-control mentions-input'
type='text'
@@ -697,6 +711,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='notificationCommentsAny'
type='radio'
name='commentsNotificationLevel'
checked={commentsActive[0]}
@@ -712,6 +727,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='notificationCommentsRoot'
type='radio'
name='commentsNotificationLevel'
checked={commentsActive[1]}
@@ -727,6 +743,7 @@ export default class NotificationsTab extends React.Component {
<div className='radio'>
<label>
<input
+ id='notificationCommentsNever'
type='radio'
name='commentsNotificationLevel'
checked={commentsActive[2]}
@@ -804,6 +821,7 @@ export default class NotificationsTab extends React.Component {
<div>
<div className='modal-header'>
<button
+ id='closeButton'
type='button'
className='close'
data-dismiss='modal'
diff --git a/webapp/components/user_settings/user_settings_security.jsx b/webapp/components/user_settings/user_settings_security.jsx
index b6ee2d915..9ca7f4b62 100644
--- a/webapp/components/user_settings/user_settings_security.jsx
+++ b/webapp/components/user_settings/user_settings_security.jsx
@@ -331,6 +331,7 @@ export default class SecurityTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='currentPassword'
className='form-control'
type='password'
onChange={this.updateCurrentPassword}
@@ -352,6 +353,7 @@ export default class SecurityTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='newPassword'
className='form-control'
type='password'
onChange={this.updateNewPassword}
@@ -373,6 +375,7 @@ export default class SecurityTab extends React.Component {
</label>
<div className='col-sm-7'>
<input
+ id='confirmPassword'
className='form-control'
type='password'
onChange={this.updateConfirmPassword}
diff --git a/webapp/components/user_settings/user_settings_theme.jsx b/webapp/components/user_settings/user_settings_theme.jsx
index 5a286a396..35df0bd13 100644
--- a/webapp/components/user_settings/user_settings_theme.jsx
+++ b/webapp/components/user_settings/user_settings_theme.jsx
@@ -218,6 +218,7 @@ export default class ThemeSetting extends React.Component {
>
<label>
<input
+ id='standardThemes'
type='radio'
name='theme'
checked={!displayCustom}
@@ -241,6 +242,7 @@ export default class ThemeSetting extends React.Component {
>
<label>
<input
+ id='customThemes'
type='radio'
name='theme'
checked={displayCustom}
@@ -260,6 +262,7 @@ export default class ThemeSetting extends React.Component {
<div key='otherThemes'>
<br/>
<a
+ id='otherThemes'
href='http://docs.mattermost.com/help/settings/theme-colors.html#custom-theme-examples'
target='_blank'
rel='noopener noreferrer'
@@ -278,6 +281,7 @@ export default class ThemeSetting extends React.Component {
className='padding-top'
>
<a
+ id='slackImportTheme'
className='theme'
onClick={this.handleImportModal}
>
@@ -295,6 +299,7 @@ export default class ThemeSetting extends React.Component {
<div className='checkbox user-settings__submit-checkbox'>
<label>
<input
+ id='applyThemeToAllTeams'
type='checkbox'
checked={this.state.applyToAllTeams}
onChange={(e) => this.setState({applyToAllTeams: e.target.checked})}
diff --git a/webapp/components/view_image.jsx b/webapp/components/view_image.jsx
index 385138d54..e5c3caa0a 100644
--- a/webapp/components/view_image.jsx
+++ b/webapp/components/view_image.jsx
@@ -185,7 +185,6 @@ export default class ViewImageModal extends React.Component {
<ImagePreview
fileInfo={fileInfo}
fileUrl={fileUrl}
- maxHeight={this.state.imgHeight}
/>
);
} else if (fileType === 'video' || fileType === 'audio') {
@@ -193,7 +192,6 @@ export default class ViewImageModal extends React.Component {
<AudioVideoPreview
fileInfo={fileInfo}
fileUrl={fileUrl}
- maxHeight={this.state.imgHeight}
/>
);
} else if (PDFPreview.supports(fileInfo)) {
@@ -344,7 +342,7 @@ LoadingImagePreview.propTypes = {
loading: React.PropTypes.string
};
-function ImagePreview({fileInfo, fileUrl, maxHeight}) {
+function ImagePreview({fileInfo, fileUrl}) {
let previewUrl;
if (fileInfo.has_preview_image) {
previewUrl = FileStore.getFilePreviewUrl(fileInfo.id);
@@ -359,16 +357,12 @@ function ImagePreview({fileInfo, fileUrl, maxHeight}) {
rel='noopener noreferrer'
download={true}
>
- <img
- style={{maxHeight}}
- src={previewUrl}
- />
+ <img src={previewUrl}/>
</a>
);
}
ImagePreview.propTypes = {
fileInfo: React.PropTypes.object.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- maxHeight: React.PropTypes.number.isRequired
+ fileUrl: React.PropTypes.string.isRequired
};