Merge branch 'master' into PLT-1429
Этот коммит содержится в:
@@ -339,7 +339,10 @@ class EmailSettings extends React.Component {
|
||||
defaultChecked={this.props.config.EmailSettings.EnableSignInWithEmail}
|
||||
onChange={this.handleChange.bind(this, 'allowSignInWithEmail_true')}
|
||||
/>
|
||||
{'true'}
|
||||
<FormattedMessage
|
||||
id='admin.email.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
@@ -349,7 +352,10 @@ class EmailSettings extends React.Component {
|
||||
defaultChecked={!this.props.config.EmailSettings.EnableSignInWithEmail}
|
||||
onChange={this.handleChange.bind(this, 'allowSignInWithEmail_false')}
|
||||
/>
|
||||
{'false'}
|
||||
<FormattedMessage
|
||||
id='admin.email.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
@@ -380,7 +386,10 @@ class EmailSettings extends React.Component {
|
||||
defaultChecked={this.props.config.EmailSettings.EnableSignInWithUsername}
|
||||
onChange={this.handleChange.bind(this, 'allowSignInWithUsername_true')}
|
||||
/>
|
||||
{'true'}
|
||||
<FormattedMessage
|
||||
id='admin.email.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
@@ -390,7 +399,10 @@ class EmailSettings extends React.Component {
|
||||
defaultChecked={!this.props.config.EmailSettings.EnableSignInWithUsername}
|
||||
onChange={this.handleChange.bind(this, 'allowSignInWithUsername_false')}
|
||||
/>
|
||||
{'false'}
|
||||
<FormattedMessage
|
||||
id='admin.email.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
|
||||
@@ -164,7 +164,7 @@ class LdapSettings extends React.Component {
|
||||
<div className='banner__content'>
|
||||
<FormattedHTMLMessage
|
||||
id='admin.ldap.noLicense'
|
||||
defaultMessage='<h4 className="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
|
||||
defaultMessage='<h4 class="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,6 +75,7 @@ export default class AudioVideoPreview extends React.Component {
|
||||
filename={this.props.filename}
|
||||
fileUrl={this.props.fileUrl}
|
||||
fileInfo={this.props.fileInfo}
|
||||
formatMessage={this.props.formatMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -110,5 +111,6 @@ AudioVideoPreview.propTypes = {
|
||||
filename: React.PropTypes.string.isRequired,
|
||||
fileUrl: React.PropTypes.string.isRequired,
|
||||
fileInfo: React.PropTypes.object.isRequired,
|
||||
maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired
|
||||
maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired,
|
||||
formatMessage: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -15,6 +15,8 @@ import UserStore from '../stores/user_store.jsx';
|
||||
|
||||
import * as Utils from '../utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
import Constants from '../utils/constants.jsx';
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
const Preferences = Constants.Preferences;
|
||||
@@ -69,8 +71,11 @@ export default class CenterPanel extends React.Component {
|
||||
onClick={handleClick}
|
||||
>
|
||||
<a href=''>
|
||||
{'Click here to jump to recent messages. '}
|
||||
{<i className='fa fa-arrow-down'></i>}
|
||||
<FormattedMessage
|
||||
id='center_panel.recent'
|
||||
defaultMessage='Click here to jump to recent messages. '
|
||||
/>
|
||||
<i className='fa fa-arrow-down'></i>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,40 @@ import PreferenceStore from '../stores/preference_store.jsx';
|
||||
import * as Utils from '../utils/utils.jsx';
|
||||
import Constants from '../utils/constants.jsx';
|
||||
|
||||
export default class ChannelLoader extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
socketError: {
|
||||
id: 'channel_loader.socketError',
|
||||
defaultMessage: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.'
|
||||
},
|
||||
someone: {
|
||||
id: 'channel_loader.someone',
|
||||
defaultMessage: 'Someone'
|
||||
},
|
||||
posted: {
|
||||
id: 'channel_loader.posted',
|
||||
defaultMessage: 'Posted'
|
||||
},
|
||||
uploadedImage: {
|
||||
id: 'channel_loader.uploadedImage',
|
||||
defaultMessage: ' uploaded an image'
|
||||
},
|
||||
uploadedFile: {
|
||||
id: 'channel_loader.uploadedFile',
|
||||
defaultMessage: ' uploaded a file'
|
||||
},
|
||||
something: {
|
||||
id: 'channel_loader.something',
|
||||
defaultMessage: ' did something new'
|
||||
},
|
||||
wrote: {
|
||||
id: 'channel_loader.wrote',
|
||||
defaultMessage: ' wrote: '
|
||||
}
|
||||
});
|
||||
|
||||
class ChannelLoader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -23,6 +56,17 @@ export default class ChannelLoader extends React.Component {
|
||||
|
||||
this.onSocketChange = this.onSocketChange.bind(this);
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
SocketStore.setTranslations({
|
||||
socketError: formatMessage(holders.socketError),
|
||||
someone: formatMessage(holders.someone),
|
||||
posted: formatMessage(holders.posted),
|
||||
uploadedImage: formatMessage(holders.uploadedImage),
|
||||
uploadedFile: formatMessage(holders.uploadedFile),
|
||||
something: formatMessage(holders.something),
|
||||
wrote: formatMessage(holders.wrote)
|
||||
});
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -126,3 +170,9 @@ export default class ChannelLoader extends React.Component {
|
||||
return <div/>;
|
||||
}
|
||||
}
|
||||
|
||||
ChannelLoader.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(ChannelLoader);
|
||||
@@ -21,12 +21,33 @@ import SocketStore from '../stores/socket_store.jsx';
|
||||
|
||||
import Constants from '../utils/constants.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedHTMLMessage} from 'mm-intl';
|
||||
|
||||
const Preferences = Constants.Preferences;
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
export default class CreatePost extends React.Component {
|
||||
const holders = defineMessages({
|
||||
comment: {
|
||||
id: 'create_post.comment',
|
||||
defaultMessage: 'Comment'
|
||||
},
|
||||
post: {
|
||||
id: 'create_post.post',
|
||||
defaultMessage: 'Post'
|
||||
},
|
||||
write: {
|
||||
id: 'create_post.write',
|
||||
defaultMessage: 'Write a message...'
|
||||
},
|
||||
deleteMsg: {
|
||||
id: 'create_post.deleteMsg',
|
||||
defaultMessage: '(message deleted)'
|
||||
}
|
||||
});
|
||||
|
||||
class CreatePost extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -49,6 +70,7 @@ export default class CreatePost extends React.Component {
|
||||
this.sendMessage = this.sendMessage.bind(this);
|
||||
|
||||
PostStore.clearDraftUploads();
|
||||
PostStore.deleteMessage(this.props.intl.formatMessage(holders.deleteMsg));
|
||||
|
||||
const draft = this.getCurrentDraft();
|
||||
|
||||
@@ -356,7 +378,8 @@ export default class CreatePost extends React.Component {
|
||||
if (!lastPost) {
|
||||
return;
|
||||
}
|
||||
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
|
||||
const {formatMessage} = this.props.intl;
|
||||
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? formatMessage(holders.comment) : formatMessage(holders.post);
|
||||
|
||||
AppDispatcher.handleViewAction({
|
||||
type: ActionTypes.RECIEVED_EDIT_POST,
|
||||
@@ -374,9 +397,10 @@ export default class CreatePost extends React.Component {
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4>{'Sending Messages'}</h4>
|
||||
<p>{'Type here to write a message and press '}<strong>{'Enter'}</strong>{' to post it.'}</p>
|
||||
<p>{'Click the '}<strong>{'Attachment'}</strong>{' button to upload an image or a file.'}</p>
|
||||
<FormattedHTMLMessage
|
||||
id='create_post.tutorialTip'
|
||||
defaultMessage='<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -440,7 +464,7 @@ export default class CreatePost extends React.Component {
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onHeightChange={this.resizePostHolder}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Write a message...'
|
||||
createMessage={this.props.intl.formatMessage(holders.write)}
|
||||
channelId={this.state.channelId}
|
||||
id='post_textbox'
|
||||
ref='textbox'
|
||||
@@ -477,3 +501,9 @@ export default class CreatePost extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CreatePost.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(CreatePost);
|
||||
@@ -5,7 +5,16 @@ import * as utils from '../utils/utils.jsx';
|
||||
import * as Client from '../utils/client.jsx';
|
||||
import Constants from '../utils/constants.jsx';
|
||||
|
||||
export default class FileAttachment extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
download: {
|
||||
id: 'file_attachment.download',
|
||||
defaultMessage: 'Download'
|
||||
}
|
||||
});
|
||||
|
||||
class FileAttachment extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -266,7 +275,7 @@ export default class FileAttachment extends React.Component {
|
||||
href={fileUrl}
|
||||
download={filenameString}
|
||||
data-toggle='tooltip'
|
||||
title={'Download \"' + filenameString + '\"'}
|
||||
title={this.props.intl.formatMessage(holders.download) + ' \"' + filenameString + '\"'}
|
||||
className='post-image__name'
|
||||
>
|
||||
{trimmedFilename}
|
||||
@@ -291,6 +300,7 @@ export default class FileAttachment extends React.Component {
|
||||
}
|
||||
|
||||
FileAttachment.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
// a list of file pathes displayed by the parent FileAttachmentList
|
||||
filename: React.PropTypes.string.isRequired,
|
||||
@@ -301,3 +311,5 @@ FileAttachment.propTypes = {
|
||||
// handler for when the thumbnail is clicked passed the index above
|
||||
handleImageClick: React.PropTypes.func
|
||||
};
|
||||
|
||||
export default injectIntl(FileAttachment);
|
||||
@@ -3,15 +3,28 @@
|
||||
|
||||
import * as Utils from '../utils/utils.jsx';
|
||||
|
||||
export default function FileInfoPreview({filename, fileUrl, fileInfo}) {
|
||||
import {defineMessages} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
type: {
|
||||
id: 'file_info_preview.type',
|
||||
defaultMessage: 'File type '
|
||||
},
|
||||
size: {
|
||||
id: 'file_info_preview.size',
|
||||
defaultMessage: 'Size '
|
||||
}
|
||||
});
|
||||
|
||||
export default function FileInfoPreview({filename, fileUrl, fileInfo, formatMessage}) {
|
||||
// non-image files include a section providing details about the file
|
||||
const infoParts = [];
|
||||
|
||||
if (fileInfo.extension !== '') {
|
||||
infoParts.push('File type ' + fileInfo.extension.toUpperCase());
|
||||
infoParts.push(formatMessage(holders.type) + fileInfo.extension.toUpperCase());
|
||||
}
|
||||
|
||||
infoParts.push('Size ' + Utils.fileSizeToString(fileInfo.size));
|
||||
infoParts.push(formatMessage(holders.size) + Utils.fileSizeToString(fileInfo.size));
|
||||
|
||||
const infoString = infoParts.join(', ');
|
||||
|
||||
|
||||
@@ -89,9 +89,9 @@ export default class LoginUsername extends React.Component {
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (err.message === 'api.user.login.not_verified.app_error') {
|
||||
if (err.id === 'api.user.login.not_verified.app_error') {
|
||||
state.serverError = formatMessage(holders.verifyEmailError);
|
||||
} else if (err.message === 'store.sql_user.get_by_username.app_error') {
|
||||
} else if (err.id === 'store.sql_user.get_by_username.app_error') {
|
||||
state.serverError = formatMessage(holders.userNotFoundError);
|
||||
} else {
|
||||
state.serverError = err.message;
|
||||
|
||||
@@ -24,6 +24,8 @@ import Constants from '../utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
const Popover = ReactBootstrap.Popover;
|
||||
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
@@ -133,7 +135,10 @@ export default class Navbar extends React.Component {
|
||||
dialogType={ChannelInfoModal}
|
||||
dialogProps={{channel}}
|
||||
>
|
||||
{'View Info'}
|
||||
<FormattedMessage
|
||||
id='navbar.viewInfo'
|
||||
defaultMessage='View Info'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
</li>
|
||||
);
|
||||
@@ -145,7 +150,10 @@ export default class Navbar extends React.Component {
|
||||
href='#'
|
||||
onClick={this.showEditChannelHeaderModal}
|
||||
>
|
||||
{'Set Channel Header...'}
|
||||
<FormattedMessage
|
||||
id='navbar.setHeader'
|
||||
defaultMessage='Set Channel Header...'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -159,7 +167,10 @@ export default class Navbar extends React.Component {
|
||||
href='#'
|
||||
onClick={() => this.setState({showEditChannelPurposeModal: true})}
|
||||
>
|
||||
{'Set Channel Purpose...'}
|
||||
<FormattedMessage
|
||||
id='navbar.setPurpose'
|
||||
defaultMessage='Set Channel Purpose...'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -175,7 +186,10 @@ export default class Navbar extends React.Component {
|
||||
dialogType={ChannelInviteModal}
|
||||
dialogProps={{channel}}
|
||||
>
|
||||
{'Add Members'}
|
||||
<FormattedMessage
|
||||
id='navbar.addMembers'
|
||||
defaultMessage='Add Members'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
</li>
|
||||
);
|
||||
@@ -187,7 +201,10 @@ export default class Navbar extends React.Component {
|
||||
href='#'
|
||||
onClick={this.handleLeave}
|
||||
>
|
||||
{'Leave Channel'}
|
||||
<FormattedMessage
|
||||
id='navbar.leave'
|
||||
defaultMessage='Leave Channel'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -205,7 +222,10 @@ export default class Navbar extends React.Component {
|
||||
href='#'
|
||||
onClick={() => this.setState({showMembersModal: true})}
|
||||
>
|
||||
{'Manage Members'}
|
||||
<FormattedMessage
|
||||
id='navbar.manageMembers'
|
||||
defaultMessage='Manage Members'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -217,7 +237,10 @@ export default class Navbar extends React.Component {
|
||||
dialogType={DeleteChannelModal}
|
||||
dialogProps={{channel}}
|
||||
>
|
||||
{'Delete Channel...'}
|
||||
<FormattedMessage
|
||||
id='navbar.delete'
|
||||
defaultMessage='Delete Channel...'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
</li>
|
||||
);
|
||||
@@ -234,7 +257,10 @@ export default class Navbar extends React.Component {
|
||||
data-name={channel.name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
{'Rename Channel...'}
|
||||
<FormattedMessage
|
||||
id='navbar.rename'
|
||||
defaultMessage='Rename Channel...'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -249,7 +275,10 @@ export default class Navbar extends React.Component {
|
||||
dialogType={ChannelNotificationsModal}
|
||||
dialogProps={{channel}}
|
||||
>
|
||||
{'Notification Preferences'}
|
||||
<FormattedMessage
|
||||
id='navbar.preferences'
|
||||
defaultMessage='Notification Preferences'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
</li>
|
||||
);
|
||||
@@ -319,7 +348,12 @@ export default class Navbar extends React.Component {
|
||||
data-toggle='collapse'
|
||||
data-target='#navbar-collapse-1'
|
||||
>
|
||||
<span className='sr-only'>{'Toggle sidebar'}</span>
|
||||
<span className='sr-only'>
|
||||
<FormattedMessage
|
||||
id='navbar.toggle1'
|
||||
defaultMessage='Toggle sidebar'
|
||||
/>
|
||||
</span>
|
||||
<span className='icon-bar'></span>
|
||||
<span className='icon-bar'></span>
|
||||
<span className='icon-bar'></span>
|
||||
@@ -335,7 +369,12 @@ export default class Navbar extends React.Component {
|
||||
data-target='#sidebar-nav'
|
||||
onClick={this.toggleLeftSidebar}
|
||||
>
|
||||
<span className='sr-only'>{'Toggle sidebar'}</span>
|
||||
<span className='sr-only'>
|
||||
<FormattedMessage
|
||||
id='navbar.toggle2'
|
||||
defaultMessage='Toggle sidebar'
|
||||
/>
|
||||
</span>
|
||||
<span className='icon-bar'></span>
|
||||
<span className='icon-bar'></span>
|
||||
<span className='icon-bar'></span>
|
||||
@@ -405,6 +444,17 @@ export default class Navbar extends React.Component {
|
||||
}
|
||||
|
||||
if (channel.header.length === 0) {
|
||||
const link = (
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.showEditChannelHeaderModal}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='navbar.click'
|
||||
defaultMessage='Click here'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
popoverContent = (
|
||||
<Popover
|
||||
bsStyle='info'
|
||||
@@ -412,15 +462,14 @@ export default class Navbar extends React.Component {
|
||||
id='header-popover'
|
||||
>
|
||||
<div>
|
||||
{'No channel header yet.'}
|
||||
<br/>
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.showEditChannelHeaderModal}
|
||||
>
|
||||
{'Click here'}
|
||||
</a>
|
||||
{' to add one.'}
|
||||
<FormattedMessage
|
||||
id='navbar.noHeader'
|
||||
defaultMessage='No channel header yet.{newline}{link} to add one.'
|
||||
values={{
|
||||
newline: (<br/>),
|
||||
link: (link)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,20 @@
|
||||
|
||||
import * as TextFormatting from '../utils/text_formatting.jsx';
|
||||
|
||||
export default class PostAttachment extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
collapse: {
|
||||
id: 'post_attachment.collapse',
|
||||
defaultMessage: '▲ collapse text'
|
||||
},
|
||||
more: {
|
||||
id: 'post_attachment.more',
|
||||
defaultMessage: '▼ read more'
|
||||
}
|
||||
});
|
||||
|
||||
class PostAttachment extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -28,7 +41,7 @@ export default class PostAttachment extends React.Component {
|
||||
getInitState() {
|
||||
const shouldCollapse = this.shouldCollapse();
|
||||
const text = TextFormatting.formatText(this.props.attachment.text || '');
|
||||
const uncollapsedText = text + (shouldCollapse ? '<a class="attachment-link-more" href="#">▲ collapse text</a>' : '');
|
||||
const uncollapsedText = text + (shouldCollapse ? `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.collapse)}</a>` : '');
|
||||
const collapsedText = shouldCollapse ? this.getCollapsedText() : text;
|
||||
|
||||
return {
|
||||
@@ -62,7 +75,7 @@ export default class PostAttachment extends React.Component {
|
||||
text = text.substr(0, 700);
|
||||
}
|
||||
|
||||
return TextFormatting.formatText(text) + '<a class="attachment-link-more" href="#">▼ read more</a>';
|
||||
return TextFormatting.formatText(text) + `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.more)}</a>`;
|
||||
}
|
||||
|
||||
getFieldsTable() {
|
||||
@@ -292,5 +305,8 @@ export default class PostAttachment extends React.Component {
|
||||
}
|
||||
|
||||
PostAttachment.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
attachment: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(PostAttachment);
|
||||
|
||||
@@ -14,7 +14,20 @@ import YoutubeVideo from './youtube_video.jsx';
|
||||
|
||||
import providers from './providers.json';
|
||||
|
||||
export default class PostBody extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
plusOne: {
|
||||
id: 'post_body.plusOne',
|
||||
defaultMessage: ' plus 1 other file'
|
||||
},
|
||||
plusMore: {
|
||||
id: 'post_body.plusMore',
|
||||
defaultMessage: ' plus {count} other files'
|
||||
}
|
||||
});
|
||||
|
||||
class PostBody extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -187,6 +200,7 @@ export default class PostBody extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const post = this.props.post;
|
||||
const filenames = this.props.post.filenames;
|
||||
const parentPost = this.props.parentPost;
|
||||
@@ -208,10 +222,12 @@ export default class PostBody extends React.Component {
|
||||
username = parentPost.props.override_username;
|
||||
}
|
||||
|
||||
if (username.slice(-1) === 's') {
|
||||
apostrophe = '\'';
|
||||
} else {
|
||||
apostrophe = '\'s';
|
||||
if (global.window.mm_locale === 'en') {
|
||||
if (username.slice(-1) === 's') {
|
||||
apostrophe = '\'';
|
||||
} else {
|
||||
apostrophe = '\'s';
|
||||
}
|
||||
}
|
||||
name = (
|
||||
<a
|
||||
@@ -230,16 +246,23 @@ export default class PostBody extends React.Component {
|
||||
message = parentPost.filenames[0].split('/').pop();
|
||||
|
||||
if (parentPost.filenames.length === 2) {
|
||||
message += ' plus 1 other file';
|
||||
message += formatMessage(holders.plusOne);
|
||||
} else if (parentPost.filenames.length > 2) {
|
||||
message += ` plus ${parentPost.filenames.length - 1} other files`;
|
||||
message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)});
|
||||
}
|
||||
}
|
||||
|
||||
comment = (
|
||||
<div className='post__link'>
|
||||
<span>
|
||||
{'Commented on '}{name}{apostrophe}{' message: '}
|
||||
<FormattedMessage
|
||||
id='post_body.commentedOn'
|
||||
defaultMessage='Commented on {name}{apostrophe} message: '
|
||||
values={{
|
||||
name: (name),
|
||||
apostrophe: apostrophe
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
className='theme'
|
||||
onClick={this.props.handleCommentClick}
|
||||
@@ -260,7 +283,10 @@ export default class PostBody extends React.Component {
|
||||
href='#'
|
||||
onClick={this.props.retryPost}
|
||||
>
|
||||
{'Retry'}
|
||||
<FormattedMessage
|
||||
id='post_body.retry'
|
||||
defaultMessage='Retry'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else if (post.state === Constants.POST_LOADING) {
|
||||
@@ -313,8 +339,11 @@ export default class PostBody extends React.Component {
|
||||
}
|
||||
|
||||
PostBody.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
post: React.PropTypes.object.isRequired,
|
||||
parentPost: React.PropTypes.object,
|
||||
retryPost: React.PropTypes.func.isRequired,
|
||||
handleCommentClick: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(PostBody);
|
||||
@@ -7,6 +7,8 @@ import PostStore from '../stores/post_store.jsx';
|
||||
import ChannelStore from '../stores/channel_store.jsx';
|
||||
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
export default class PostFocusView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -73,7 +75,12 @@ export default class PostFocusView extends React.Component {
|
||||
getIntroMessage() {
|
||||
return (
|
||||
<div className='channel-intro'>
|
||||
<h4 className='channel-intro__title'>{'Beginning of Channel Archives'}</h4>
|
||||
<h4 className='channel-intro__title'>
|
||||
<FormattedMessage
|
||||
id='post_focus_view.beginning'
|
||||
defaultMessage='Beginning of Channel Archives'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||
|
||||
import Constants from '../utils/constants.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
const Overlay = ReactBootstrap.Overlay;
|
||||
const Popover = ReactBootstrap.Popover;
|
||||
|
||||
@@ -53,7 +55,10 @@ export default class PostInfo extends React.Component {
|
||||
href='#'
|
||||
onClick={this.props.handleCommentClick}
|
||||
>
|
||||
{'Reply'}
|
||||
<FormattedMessage
|
||||
id='post_info.reply'
|
||||
defaultMessage='Reply'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -68,7 +73,10 @@ export default class PostInfo extends React.Component {
|
||||
href='#'
|
||||
onClick={(e) => this.setState({target: e.target, show: !this.state.show})}
|
||||
>
|
||||
{'Permalink'}
|
||||
<FormattedMessage
|
||||
id='post_info.permalink'
|
||||
defaultMessage='Permalink'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -84,7 +92,10 @@ export default class PostInfo extends React.Component {
|
||||
role='menuitem'
|
||||
onClick={() => EventHelpers.showDeletePostModal(post, dataComments)}
|
||||
>
|
||||
{'Delete'}
|
||||
<FormattedMessage
|
||||
id='post_info.del'
|
||||
defaultMessage='Delete'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -108,7 +119,10 @@ export default class PostInfo extends React.Component {
|
||||
data-channelid={post.channel_id}
|
||||
data-comments={dataComments}
|
||||
>
|
||||
{'Edit'}
|
||||
<FormattedMessage
|
||||
id='post_info.edit'
|
||||
defaultMessage='Edit'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -183,7 +197,15 @@ export default class PostInfo extends React.Component {
|
||||
var dropdown = this.createDropdown();
|
||||
|
||||
const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id;
|
||||
const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy';
|
||||
const copyButtonText = this.state.copiedLink ? (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='post_info.copy'
|
||||
defaultMessage='Copy '
|
||||
/>
|
||||
<i className='fa fa-check'/></div>
|
||||
) : (<FormattedMessage id='post_info.copy' />);
|
||||
|
||||
const permalinkOverlay = (
|
||||
<Popover
|
||||
id='permalink-overlay'
|
||||
|
||||
@@ -8,6 +8,9 @@ import * as Utils from '../utils/utils.jsx';
|
||||
import Post from './post.jsx';
|
||||
import Constants from '../utils/constants.jsx';
|
||||
import DelayedAction from '../utils/delayed_action.jsx';
|
||||
|
||||
import {FormattedDate, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const Preferences = Constants.Preferences;
|
||||
|
||||
export default class PostsView extends React.Component {
|
||||
@@ -250,7 +253,15 @@ export default class PostsView extends React.Component {
|
||||
className='date-separator'
|
||||
>
|
||||
<hr className='separator__hr' />
|
||||
<div className='separator__text'>{currentPostDay.toDateString()}</div>
|
||||
<div className='separator__text'>
|
||||
<FormattedDate
|
||||
value={currentPostDay}
|
||||
weekday='short'
|
||||
month='short'
|
||||
day='2-digit'
|
||||
year='numeric'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -276,7 +287,12 @@ export default class PostsView extends React.Component {
|
||||
<hr
|
||||
className='separator__hr'
|
||||
/>
|
||||
<div className='separator__text'>{'New Messages'}</div>
|
||||
<div className='separator__text'>
|
||||
<FormattedMessage
|
||||
id='posts_view.newMsg'
|
||||
defaultMessage='New Messages'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -420,7 +436,10 @@ export default class PostsView extends React.Component {
|
||||
href='#'
|
||||
onClick={this.loadMorePostsTop}
|
||||
>
|
||||
{'Load more messages'}
|
||||
<FormattedMessage
|
||||
id='posts_view.loadMore'
|
||||
defaultMessage='Load more messages'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
@@ -436,7 +455,7 @@ export default class PostsView extends React.Component {
|
||||
href='#'
|
||||
onClick={this.loadMorePostsBottom}
|
||||
>
|
||||
{'Load more messages'}
|
||||
<FormattedMessage id='posts_view.loadMore' />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import Constants from '../utils/constants.jsx';
|
||||
import * as Utils from '../utils/utils.jsx';
|
||||
|
||||
import {FormattedRelative, FormattedDate} from 'mm-intl';
|
||||
|
||||
var Tooltip = ReactBootstrap.Tooltip;
|
||||
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
@@ -20,20 +21,25 @@ export default class TimeSince extends React.Component {
|
||||
clearInterval(this.intervalId);
|
||||
}
|
||||
render() {
|
||||
const displayDate = Utils.displayDate(this.props.eventTime);
|
||||
const displayTime = Utils.displayTime(this.props.eventTime);
|
||||
|
||||
if (this.props.sameUser) {
|
||||
return (
|
||||
<time className='post__time'>
|
||||
{Utils.displayTime(this.props.eventTime)}
|
||||
<FormattedRelative value={this.props.eventTime} />
|
||||
</time>
|
||||
);
|
||||
}
|
||||
|
||||
const tooltip = (
|
||||
<Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
|
||||
{displayDate + ' at ' + displayTime}
|
||||
<FormattedDate
|
||||
value={this.props.eventTime}
|
||||
month='long'
|
||||
day='numeric'
|
||||
year='numeric'
|
||||
hour12={true}
|
||||
hour='numeric'
|
||||
minute='2-digit'
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -44,7 +50,7 @@ export default class TimeSince extends React.Component {
|
||||
overlay={tooltip}
|
||||
>
|
||||
<time className='post__time'>
|
||||
{Utils.displayDateTime(this.props.eventTime)}
|
||||
<FormattedRelative value={this.props.eventTime} />
|
||||
</time>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
||||
@@ -9,10 +9,20 @@ import Constants from '../utils/constants.jsx';
|
||||
import FileInfoPreview from './file_info_preview.jsx';
|
||||
import FileStore from '../stores/file_store.jsx';
|
||||
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
|
||||
|
||||
const Modal = ReactBootstrap.Modal;
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
export default class ViewImageModal extends React.Component {
|
||||
const holders = defineMessages({
|
||||
loading: {
|
||||
id: 'view_image.loading',
|
||||
defaultMessage: 'Loading '
|
||||
}
|
||||
});
|
||||
|
||||
class ViewImageModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -235,6 +245,7 @@ export default class ViewImageModal extends React.Component {
|
||||
fileUrl={fileUrl}
|
||||
fileInfo={this.state.fileInfo}
|
||||
maxHeight={this.state.imgHeight}
|
||||
formatMessage={this.props.intl.formatMessage}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -243,6 +254,7 @@ export default class ViewImageModal extends React.Component {
|
||||
filename={filename}
|
||||
fileUrl={fileUrl}
|
||||
fileInfo={fileInfo}
|
||||
formatMessage={this.props.intl.formatMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -250,7 +262,12 @@ export default class ViewImageModal extends React.Component {
|
||||
// display a progress indicator when the preview for an image is still loading
|
||||
const progress = Math.floor(this.state.progress[this.state.imgId]);
|
||||
|
||||
content = <LoadingImagePreview progress={progress} />;
|
||||
content = (
|
||||
<LoadingImagePreview
|
||||
progress={progress}
|
||||
loading={this.props.intl.formatMessage(holders.loading)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let leftArrow = null;
|
||||
@@ -335,6 +352,7 @@ ViewImageModal.defaultProps = {
|
||||
startId: 0
|
||||
};
|
||||
ViewImageModal.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired,
|
||||
filenames: React.PropTypes.array,
|
||||
@@ -344,12 +362,12 @@ ViewImageModal.propTypes = {
|
||||
startId: React.PropTypes.number
|
||||
};
|
||||
|
||||
function LoadingImagePreview({progress}) {
|
||||
function LoadingImagePreview({progress, loading}) {
|
||||
let progressView = null;
|
||||
if (progress) {
|
||||
progressView = (
|
||||
<span className='loader-percent'>
|
||||
{'Loading ' + progress + '%'}
|
||||
{loading + progress + '%'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -386,3 +404,5 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export default injectIntl(ViewImageModal);
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
export default class ViewImagePopoverBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -16,7 +18,10 @@ export default class ViewImagePopoverBar extends React.Component {
|
||||
data-title='Public Image'
|
||||
onClick={this.props.getPublicLink}
|
||||
>
|
||||
{'Get Public Link'}
|
||||
<FormattedMessage
|
||||
id='view_image_popover.publicLink'
|
||||
defaultMessage='Get Public Link'
|
||||
/>
|
||||
</a>
|
||||
<span className='text'>{' | '}</span>
|
||||
</div>
|
||||
@@ -33,7 +38,16 @@ export default class ViewImagePopoverBar extends React.Component {
|
||||
ref='imageFooter'
|
||||
className={footerClass}
|
||||
>
|
||||
<span className='pull-left text'>{'File ' + (this.props.fileId + 1) + ' of ' + this.props.totalFiles}</span>
|
||||
<span className='pull-left text'>
|
||||
<FormattedMessage
|
||||
id='view_image_popover.file'
|
||||
defaultMessage='File {count} of {total}'
|
||||
values={{
|
||||
count: (this.props.fileId + 1),
|
||||
total: this.props.totalFiles
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<div className='image-links'>
|
||||
{publicLink}
|
||||
<a
|
||||
@@ -41,7 +55,10 @@ export default class ViewImagePopoverBar extends React.Component {
|
||||
download={this.props.filename}
|
||||
className='text'
|
||||
>
|
||||
{'Download'}
|
||||
<FormattedMessage
|
||||
id='view_image_popover.download'
|
||||
defaultMessage='Download'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Ссылка в новой задаче
Block a user