Multiple UI Improvements
Этот коммит содержится в:
@@ -120,8 +120,7 @@ class LicenseSettings extends React.Component {
|
||||
expires: Utils.displayDate(parseInt(global.window.mm_license.ExpiresAt, 10)),
|
||||
ldap: global.window.mm_license.LDAP
|
||||
}}
|
||||
defaultMessage='<div><p>This compiled release of Mattermost platform is provided under a <a href="http://mattermost.com" target="_blank">commercial license</a>
|
||||
from Mattermost, Inc. based on your subscription level and is subject to the <a href="{terms}" target="_blank">Terms of Service.</a></p>
|
||||
defaultMessage='<div><p>This compiled release of Mattermost platform is provided under a <a href="http://mattermost.com" target="_blank">commercial license</a> from Mattermost, Inc. based on your subscription level and is subject to the <a href="{terms}" target="_blank">Terms of Service.</a></p>
|
||||
<p>Your subscription details are as follows:</p>
|
||||
Name: {name}<br />
|
||||
Company or organization name: {company}<br/>
|
||||
@@ -152,9 +151,7 @@ class LicenseSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
<FormattedHTMLMessage
|
||||
id='admin.licence.keyMigration'
|
||||
defaultMessage='If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start,
|
||||
<a href="http://mattermost.com" target="_blank">disable all Enterprise Edition features on this server</a>.
|
||||
This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'
|
||||
defaultMessage='If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, <a href="http://mattermost.com" target="_blank">disable all Enterprise Edition features on this server</a>. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -44,6 +44,7 @@ export default class ChannelMembersModal extends React.Component {
|
||||
}
|
||||
getStateFromStores() {
|
||||
const extraInfo = ChannelStore.getCurrentExtraInfo();
|
||||
const profiles = UserStore.getActiveOnlyProfiles();
|
||||
|
||||
if (extraInfo.member_count !== extraInfo.members.length) {
|
||||
AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
|
||||
@@ -53,9 +54,8 @@ export default class ChannelMembersModal extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
// clone the member list since we mutate it later on
|
||||
const memberList = extraInfo.members.map((member) => {
|
||||
return Object.assign({}, member);
|
||||
return profiles[member.id];
|
||||
});
|
||||
|
||||
function compareByUsername(a, b) {
|
||||
@@ -130,8 +130,8 @@ export default class ChannelMembersModal extends React.Component {
|
||||
onClick={this.handleRemove.bind(this, user)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='channel_members_modal.removeMember'
|
||||
defaultMessage='Remove Member'
|
||||
id='channel_members_modal.remove'
|
||||
defaultMessage='Remove'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
@@ -178,7 +178,6 @@ export default class ChannelMembersModal extends React.Component {
|
||||
this.props.onModalDismissed();
|
||||
}}
|
||||
>
|
||||
<i className='glyphicon glyphicon-envelope'/>
|
||||
<FormattedMessage
|
||||
id='channel_members_modal.addNew'
|
||||
defaultMessage=' Add New Members'
|
||||
|
||||
@@ -130,7 +130,7 @@ class CreatePost extends React.Component {
|
||||
post.message,
|
||||
false,
|
||||
(data) => {
|
||||
PostStore.storeDraft(data.channel_id, null);
|
||||
PostStore.storeDraft(this.state.channelId, null);
|
||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||
|
||||
if (data.goto_location && data.goto_location.length > 0) {
|
||||
@@ -262,9 +262,7 @@ class CreatePost extends React.Component {
|
||||
message = err.message;
|
||||
}
|
||||
|
||||
if (clientId === -1) {
|
||||
this.setState({serverError: message});
|
||||
} else {
|
||||
if (clientId !== -1) {
|
||||
const draft = PostStore.getDraft(this.state.channelId);
|
||||
|
||||
const index = draft.uploadsInProgress.indexOf(clientId);
|
||||
@@ -274,8 +272,10 @@ class CreatePost extends React.Component {
|
||||
|
||||
PostStore.storeDraft(this.state.channelId, draft);
|
||||
|
||||
this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: message});
|
||||
this.setState({uploadsInProgress: draft.uploadsInProgress});
|
||||
}
|
||||
|
||||
this.setState({serverError: message});
|
||||
}
|
||||
removePreview(id) {
|
||||
const previews = Object.assign([], this.state.previews);
|
||||
|
||||
@@ -19,12 +19,10 @@ class FileAttachment extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.loadFiles = this.loadFiles.bind(this);
|
||||
this.playGif = this.playGif.bind(this);
|
||||
this.stopGif = this.stopGif.bind(this);
|
||||
this.addBackgroundImage = this.addBackgroundImage.bind(this);
|
||||
|
||||
this.canSetState = false;
|
||||
this.state = {fileSize: -1, mime: '', playing: false, loading: false, format: ''};
|
||||
this.state = {fileSize: -1};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.loadFiles();
|
||||
@@ -95,42 +93,6 @@ class FileAttachment extends React.Component {
|
||||
|
||||
return true;
|
||||
}
|
||||
playGif(e, filename) {
|
||||
var img = new Image();
|
||||
var fileUrl = utils.getFileUrl(filename);
|
||||
|
||||
this.setState({loading: true});
|
||||
img.load(fileUrl);
|
||||
img.onload = () => {
|
||||
var state = {playing: true, loading: false};
|
||||
|
||||
switch (true) {
|
||||
case img.width > img.height:
|
||||
state.format = 'landscape';
|
||||
break;
|
||||
case img.height > img.width:
|
||||
state.format = 'portrait';
|
||||
break;
|
||||
default:
|
||||
state.format = 'quadrat';
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
|
||||
// keep displaying background image for a short moment while browser is
|
||||
// loading gif, to prevent white background flashing through
|
||||
setTimeout(() => this.removeBackgroundImage.bind(this)(filename), 100);
|
||||
};
|
||||
img.onError = () => this.setState({loading: false});
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
stopGif(e, filename) {
|
||||
this.setState({playing: false});
|
||||
this.addBackgroundImage(filename);
|
||||
e.stopPropagation();
|
||||
}
|
||||
getFileInfoFromName(name) {
|
||||
var fileInfo = utils.splitFileLocation(name);
|
||||
|
||||
@@ -164,74 +126,18 @@ class FileAttachment extends React.Component {
|
||||
var filename = this.props.filename;
|
||||
|
||||
var fileInfo = utils.splitFileLocation(filename);
|
||||
var fileUrl = utils.getFileUrl(filename);
|
||||
var fileUrl = utils.getFileUrl(filename, true);
|
||||
var type = utils.getFileType(fileInfo.ext);
|
||||
|
||||
var playbackControls = '';
|
||||
var loadedFile = '';
|
||||
var loadingIndicator = '';
|
||||
if (this.state.mime === 'image/gif') {
|
||||
playbackControls = (
|
||||
<div
|
||||
className='file-playback-controls play'
|
||||
onClick={(e) => this.playGif(e, filename)}
|
||||
>
|
||||
{"►"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.state.playing) {
|
||||
loadedFile = (
|
||||
<img
|
||||
className={'file__loaded ' + this.state.format}
|
||||
src={fileUrl}
|
||||
/>
|
||||
);
|
||||
playbackControls = (
|
||||
<div
|
||||
className='file-playback-controls stop'
|
||||
onClick={(e) => this.stopGif(e, filename)}
|
||||
>
|
||||
{"■"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.state.loading) {
|
||||
loadingIndicator = (
|
||||
<img
|
||||
className='spinner file__loading'
|
||||
src='/static/images/load.gif'
|
||||
/>
|
||||
);
|
||||
playbackControls = '';
|
||||
}
|
||||
|
||||
var thumbnail;
|
||||
if (type === 'image') {
|
||||
if (this.state.playing) {
|
||||
thumbnail = (
|
||||
<div
|
||||
ref={filename}
|
||||
className='post__load'
|
||||
style={{backgroundImage: 'url(/static/images/load.gif)'}}
|
||||
>
|
||||
{playbackControls}
|
||||
{loadedFile}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
thumbnail = (
|
||||
<div
|
||||
ref={filename}
|
||||
className='post__load'
|
||||
style={{backgroundImage: 'url(/static/images/load.gif)'}}
|
||||
>
|
||||
{loadingIndicator}
|
||||
{playbackControls}
|
||||
{loadedFile}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
thumbnail = (
|
||||
<div
|
||||
ref={filename}
|
||||
className='post__load'
|
||||
style={{backgroundImage: 'url(/static/images/load.gif)'}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
thumbnail = <div className={'file-icon ' + utils.getIconClassName(type)}/>;
|
||||
}
|
||||
@@ -242,7 +148,7 @@ class FileAttachment extends React.Component {
|
||||
filename,
|
||||
function success(data) {
|
||||
if (this.canSetState) {
|
||||
this.setState({fileSize: parseInt(data.size, 10), mime: data.mime});
|
||||
this.setState({fileSize: parseInt(data.size, 10)});
|
||||
}
|
||||
}.bind(this),
|
||||
function error() {
|
||||
|
||||
@@ -108,12 +108,12 @@ class FileUpload extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleChange() {
|
||||
var element = $(ReactDOM.findDOMNode(this.refs.fileInput));
|
||||
handleChange(e) {
|
||||
if (e.target.files.length > 0) {
|
||||
this.uploadFiles(e.target.files);
|
||||
|
||||
this.uploadFiles(element.prop('files'));
|
||||
|
||||
Utils.clearFileInput(element[0]);
|
||||
Utils.clearFileInput(e.target);
|
||||
}
|
||||
}
|
||||
|
||||
handleDrop(e) {
|
||||
|
||||
@@ -41,8 +41,6 @@ export default class GetLinkModal extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const userCreationEnabled = global.window.mm_config.EnableUserCreation === 'true';
|
||||
|
||||
let helpText = null;
|
||||
if (this.props.helpText) {
|
||||
helpText = (
|
||||
@@ -55,7 +53,7 @@ export default class GetLinkModal extends React.Component {
|
||||
}
|
||||
|
||||
let copyLink = null;
|
||||
if (userCreationEnabled && document.queryCommandSupported('copy')) {
|
||||
if (document.queryCommandSupported('copy')) {
|
||||
copyLink = (
|
||||
<button
|
||||
data-copy-btn='true'
|
||||
@@ -71,17 +69,14 @@ export default class GetLinkModal extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let linkText = null;
|
||||
if (userCreationEnabled) {
|
||||
linkText = (
|
||||
<textarea
|
||||
className='form-control no-resize min-height'
|
||||
readOnly='true'
|
||||
ref='textarea'
|
||||
value={this.props.link}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const linkText = (
|
||||
<textarea
|
||||
className='form-control no-resize min-height'
|
||||
readOnly='true'
|
||||
ref='textarea'
|
||||
value={this.props.link}
|
||||
/>
|
||||
);
|
||||
|
||||
var copyLinkConfirm = null;
|
||||
if (this.state.copiedLink) {
|
||||
|
||||
@@ -495,7 +495,7 @@ class InviteMemberModal extends React.Component {
|
||||
<ConfirmModal
|
||||
title={formatMessage(holders.modalTitle)}
|
||||
message={formatMessage(holders.modalMessage)}
|
||||
confirm_button={formatMessage(holders.modalButton)}
|
||||
confirmButton={formatMessage(holders.modalButton)}
|
||||
show={this.state.showConfirmModal}
|
||||
onConfirm={this.handleHide.bind(this, false)}
|
||||
onCancel={() => this.setState({showConfirmModal: false})}
|
||||
@@ -512,4 +512,4 @@ InviteMemberModal.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(InviteMemberModal);
|
||||
export default injectIntl(InviteMemberModal);
|
||||
|
||||
@@ -107,7 +107,7 @@ export default class NavbarDropdown extends React.Component {
|
||||
</li>
|
||||
);
|
||||
|
||||
if (this.props.teamType === Constants.OPEN_TEAM) {
|
||||
if (this.props.teamType === Constants.OPEN_TEAM && global.window.mm_config.EnableUserCreation === 'true') {
|
||||
teamLink = (
|
||||
<li>
|
||||
<a
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
import * as Utils from '../utils/utils.jsx';
|
||||
|
||||
export default class SettingsSidebar extends React.Component {
|
||||
componentDidUpdate() {
|
||||
$('.settings-modal').find('.modal-body').scrollTop(0);
|
||||
$('.settings-modal').find('.modal-body').perfectScrollbar('update');
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export default class SearchSuggestionList extends SuggestionList {
|
||||
text = (
|
||||
<FormattedMessage
|
||||
id='suggestion.search.private'
|
||||
defaultMessage='Public Groups'
|
||||
defaultMessage='Private Groups'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -611,7 +611,9 @@ class GeneralTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i className='modal-back'></i>
|
||||
<div className='modal-back'>
|
||||
<i className='fa fa-angle-left'></i>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='general_tab.title'
|
||||
defaultMessage='General Settings'
|
||||
|
||||
@@ -131,7 +131,10 @@ class TeamImportTab extends React.Component {
|
||||
<h4
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
><i className='modal-back'></i>
|
||||
>
|
||||
<div className='modal-back'>
|
||||
<i className='fa fa-angle-left'></i>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='team_import_tab.import'
|
||||
defaultMessage='Import'
|
||||
|
||||
@@ -308,8 +308,8 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<span className='fa fa-pencil'></span>
|
||||
<span>{currentRoles} </span>
|
||||
<span className='fa fa-chevron-down'></span>
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu member-menu'
|
||||
|
||||
@@ -91,7 +91,7 @@ class TeamSettingsModal extends React.Component {
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='modal-body'>
|
||||
<div className='modal-body settings-modal__body'>
|
||||
<div className='settings-table'>
|
||||
<div className='settings-links'>
|
||||
<SettingsSidebar
|
||||
|
||||
@@ -129,12 +129,12 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasText = this.props.messageText.length > 0;
|
||||
|
||||
let previewLink = null;
|
||||
if (Utils.isFeatureEnabled(PreReleaseFeatures.MARKDOWN_PREVIEW)) {
|
||||
const previewLinkVisible = this.props.messageText.length > 0;
|
||||
previewLink = (
|
||||
<a
|
||||
style={{visibility: previewLinkVisible ? 'visible' : 'hidden'}}
|
||||
onClick={this.showPreview}
|
||||
className='textbox-preview-link'
|
||||
>
|
||||
@@ -153,6 +153,51 @@ export default class Textbox extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let helpText = (
|
||||
<div
|
||||
style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
|
||||
className='help_format_text'
|
||||
>
|
||||
<b>
|
||||
<FormattedMessage
|
||||
id='textbox.bold'
|
||||
defaultMessage='**bold**'
|
||||
/>
|
||||
</b>
|
||||
<i>
|
||||
<FormattedMessage
|
||||
id='textbox.italic'
|
||||
defaultMessage='_italic_'
|
||||
/>
|
||||
</i>
|
||||
<span>~~<strike>
|
||||
<FormattedMessage
|
||||
id='textbox.strike'
|
||||
defaultMessage='strike'
|
||||
/>
|
||||
</strike>~~ </span>
|
||||
<code>
|
||||
<FormattedMessage
|
||||
id='textbox.inlinecode'
|
||||
defaultMessage='`inline code`'
|
||||
/>
|
||||
</code>
|
||||
<code>
|
||||
<FormattedMessage
|
||||
id='textbox.preformatted'
|
||||
defaultMessage='```preformatted```'
|
||||
/>
|
||||
</code>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='textbox.quote'
|
||||
defaultMessage='>quote'
|
||||
/>
|
||||
</span>
|
||||
{previewLink}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref='wrapper'
|
||||
@@ -184,7 +229,7 @@ export default class Textbox extends React.Component {
|
||||
dangerouslySetInnerHTML={{__html: this.state.preview ? TextFormatting.formatText(this.props.messageText) : ''}}
|
||||
>
|
||||
</div>
|
||||
{previewLink}
|
||||
{helpText}
|
||||
<a
|
||||
target='_blank'
|
||||
href='http://docs.mattermost.com/help/getting-started/messaging-basics.html'
|
||||
|
||||
@@ -10,9 +10,9 @@ export default function UserListRow({user, actions}) {
|
||||
|
||||
let name = user.username;
|
||||
if (user.nickname && nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME) {
|
||||
name = `${user.nickname} (${user.username})`;
|
||||
name = `${user.nickname} (@${user.username})`;
|
||||
} else if ((user.first_name || user.last_name) && (nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME || nameFormat === Constants.Preferences.DISPLAY_PREFER_FULL_NAME)) {
|
||||
name = `${Utils.getFullName(user)} (${user.username})`;
|
||||
name = `${Utils.getFullName(user)} (@${user.username})`;
|
||||
}
|
||||
|
||||
const buttons = actions.map((Action, index) => {
|
||||
|
||||
@@ -300,10 +300,12 @@ class AdvancedSettingsDisplay extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.title'
|
||||
defaultMessage='Advanced Settings'
|
||||
|
||||
@@ -94,10 +94,12 @@ class DeveloperTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.developer.title'
|
||||
defaultMessage='Developer Settings'
|
||||
|
||||
@@ -444,10 +444,12 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.display.title'
|
||||
defaultMessage='Display Settings'
|
||||
|
||||
@@ -759,10 +759,12 @@ class UserSettingsGeneralTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.title'
|
||||
defaultMessage='General Settings'
|
||||
|
||||
@@ -163,10 +163,12 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.integrations.title'
|
||||
defaultMessage='Integration Settings'
|
||||
|
||||
@@ -277,7 +277,7 @@ class UserSettingsModal extends React.Component {
|
||||
/>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body ref='modalBody'>
|
||||
<Modal.Body bsClass='settings-modal__body modal' ref='modalBody'>
|
||||
<div className='settings-table'>
|
||||
<div className='settings-links'>
|
||||
<SettingsSidebar
|
||||
@@ -309,7 +309,7 @@ class UserSettingsModal extends React.Component {
|
||||
<ConfirmModal
|
||||
title={formatMessage(holders.confirmTitle)}
|
||||
message={formatMessage(holders.confirmMsg)}
|
||||
confirm_button={formatMessage(holders.confirmBtns)}
|
||||
confirmButton={formatMessage(holders.confirmBtns)}
|
||||
show={this.state.showConfirmModal}
|
||||
onConfirm={this.handleConfirm}
|
||||
onCancel={this.handleCancelConfirmation}
|
||||
|
||||
@@ -774,10 +774,12 @@ class NotificationsTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.title'
|
||||
defaultMessage='Notification Settings'
|
||||
|
||||
@@ -389,10 +389,12 @@ class SecurityTab extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
<div className='modal-back'>
|
||||
<i
|
||||
className='fa fa-angle-left'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.title'
|
||||
defaultMessage='Security Settings'
|
||||
|
||||
Ссылка в новой задаче
Block a user