From 6a3806fe9d03abdfa1eb30fc62c7388a6922b60f Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 3 Feb 2016 00:46:56 -0300 Subject: PLT-7: Refactoring frontend (chunk 11) - channel view - Added translations for previous commits - Fix bug on signup_team email body --- .../components/admin_console/ldap_settings.jsx | 2 +- web/react/components/audio_video_preview.jsx | 4 +- web/react/components/center_panel.jsx | 9 ++- web/react/components/create_post.jsx | 42 ++++++++-- web/react/components/file_attachment.jsx | 16 +++- web/react/components/file_info_preview.jsx | 19 ++++- web/react/components/navbar.jsx | 89 +++++++++++++++++----- web/react/components/post_attachment.jsx | 22 +++++- web/react/components/post_body.jsx | 47 +++++++++--- web/react/components/post_focus_view.jsx | 9 ++- web/react/components/post_info.jsx | 32 ++++++-- web/react/components/posts_view.jsx | 27 ++++++- web/react/components/time_since.jsx | 20 +++-- web/react/components/view_image.jsx | 28 ++++++- web/react/components/view_image_popover_bar.jsx | 23 +++++- 15 files changed, 318 insertions(+), 71 deletions(-) (limited to 'web/react/components') diff --git a/web/react/components/admin_console/ldap_settings.jsx b/web/react/components/admin_console/ldap_settings.jsx index bc13b3bcd..535c264dd 100644 --- a/web/react/components/admin_console/ldap_settings.jsx +++ b/web/react/components/admin_console/ldap_settings.jsx @@ -164,7 +164,7 @@ class LdapSettings extends React.Component {
diff --git a/web/react/components/audio_video_preview.jsx b/web/react/components/audio_video_preview.jsx index 7d00fbdaa..739c8c95e 100644 --- a/web/react/components/audio_video_preview.jsx +++ b/web/react/components/audio_video_preview.jsx @@ -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 }; diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx index 443ecefde..7d2be04d6 100644 --- a/web/react/components/center_panel.jsx +++ b/web/react/components/center_panel.jsx @@ -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} > - {'Click here to jump to recent messages. '} - {} + + ); diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index de971c43f..ed672cd34 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -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(); @@ -361,7 +383,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, @@ -379,9 +402,10 @@ export default class CreatePost extends React.Component { screens.push(
-

{'Sending Messages'}

-

{'Type here to write a message and press '}{'Enter'}{' to post it.'}

-

{'Click the '}{'Attachment'}{' button to upload an image or a file.'}

+
); @@ -445,7 +469,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' @@ -482,3 +506,9 @@ export default class CreatePost extends React.Component { ); } } + +CreatePost.propTypes = { + intl: intlShape.isRequired +}; + +export default injectIntl(CreatePost); \ No newline at end of file diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index eeb218bfe..776394828 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -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); \ No newline at end of file diff --git a/web/react/components/file_info_preview.jsx b/web/react/components/file_info_preview.jsx index 45d89007f..1dac140c9 100644 --- a/web/react/components/file_info_preview.jsx +++ b/web/react/components/file_info_preview.jsx @@ -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(', '); diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index 7326a9ef8..8005678a2 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -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'} + ); @@ -145,7 +150,10 @@ export default class Navbar extends React.Component { href='#' onClick={this.showEditChannelHeaderModal} > - {'Set Channel Header...'} + ); @@ -159,7 +167,10 @@ export default class Navbar extends React.Component { href='#' onClick={() => this.setState({showEditChannelPurposeModal: true})} > - {'Set Channel Purpose...'} + ); @@ -175,7 +186,10 @@ export default class Navbar extends React.Component { dialogType={ChannelInviteModal} dialogProps={{channel}} > - {'Add Members'} + ); @@ -187,7 +201,10 @@ export default class Navbar extends React.Component { href='#' onClick={this.handleLeave} > - {'Leave Channel'} + ); @@ -205,7 +222,10 @@ export default class Navbar extends React.Component { href='#' onClick={() => this.setState({showMembersModal: true})} > - {'Manage Members'} + ); @@ -217,7 +237,10 @@ export default class Navbar extends React.Component { dialogType={DeleteChannelModal} dialogProps={{channel}} > - {'Delete Channel...'} + ); @@ -234,7 +257,10 @@ export default class Navbar extends React.Component { data-name={channel.name} data-channelid={channel.id} > - {'Rename Channel...'} + ); @@ -249,7 +275,10 @@ export default class Navbar extends React.Component { dialogType={ChannelNotificationsModal} dialogProps={{channel}} > - {'Notification Preferences'} + ); @@ -319,7 +348,12 @@ export default class Navbar extends React.Component { data-toggle='collapse' data-target='#navbar-collapse-1' > - {'Toggle sidebar'} + + + @@ -335,7 +369,12 @@ export default class Navbar extends React.Component { data-target='#sidebar-nav' onClick={this.toggleLeftSidebar} > - {'Toggle sidebar'} + + + @@ -405,6 +444,17 @@ export default class Navbar extends React.Component { } if (channel.header.length === 0) { + const link = ( + + + + ); popoverContent = (
- {'No channel header yet.'} -
- - {'Click here'} - - {' to add one.'} + ), + link: (link) + }} + />
); diff --git a/web/react/components/post_attachment.jsx b/web/react/components/post_attachment.jsx index 676bc91af..2eedfb7c1 100644 --- a/web/react/components/post_attachment.jsx +++ b/web/react/components/post_attachment.jsx @@ -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 ? '▲ collapse text' : ''); + const uncollapsedText = text + (shouldCollapse ? `${this.props.intl.formatMessage(holders.collapse)}` : ''); 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) + '▼ read more'; + return TextFormatting.formatText(text) + `${this.props.intl.formatMessage(holders.more)}`; } 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); diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index b1657f0eb..16f8528b2 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -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 = ( 2) { - message += ` plus ${parentPost.filenames.length - 1} other files`; + message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)}); } } comment = (
- {'Commented on '}{name}{apostrophe}{' message: '} + - {'Retry'} + ); } 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); \ No newline at end of file diff --git a/web/react/components/post_focus_view.jsx b/web/react/components/post_focus_view.jsx index adcd78839..b9b6acd5f 100644 --- a/web/react/components/post_focus_view.jsx +++ b/web/react/components/post_focus_view.jsx @@ -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 (
-

{'Beginning of Channel Archives'}

+

+ +

); } diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index 0fb9d7f4a..ddb393520 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -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'} + ); @@ -68,7 +73,10 @@ export default class PostInfo extends React.Component { href='#' onClick={(e) => this.setState({target: e.target, show: !this.state.show})} > - {'Permalink'} + ); @@ -84,7 +92,10 @@ export default class PostInfo extends React.Component { role='menuitem' onClick={() => EventHelpers.showDeletePostModal(post, dataComments)} > - {'Delete'} + ); @@ -108,7 +119,10 @@ export default class PostInfo extends React.Component { data-channelid={post.channel_id} data-comments={dataComments} > - {'Edit'} + ); @@ -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 ? (
{'Copy '}
) : 'Copy'; + const copyButtonText = this.state.copiedLink ? ( +
+ +
+ ) : (); + const permalinkOverlay = (
-
{currentPostDay.toDateString()}
+
+ +
); } @@ -276,7 +287,12 @@ export default class PostsView extends React.Component {
-
{'New Messages'}
+
+ +
); } @@ -420,7 +436,10 @@ export default class PostsView extends React.Component { href='#' onClick={this.loadMorePostsTop} > - {'Load more messages'} + ); } else { @@ -436,7 +455,7 @@ export default class PostsView extends React.Component { href='#' onClick={this.loadMorePostsBottom} > - {'Load more messages'} + ); } else { diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx index 0b549b1e6..ba8dbffcc 100644 --- a/web/react/components/time_since.jsx +++ b/web/react/components/time_since.jsx @@ -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 ( ); } const tooltip = ( - {displayDate + ' at ' + displayTime} + ); @@ -44,7 +50,7 @@ export default class TimeSince extends React.Component { overlay={tooltip} > ); diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index d11f8a21c..90885e495 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -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 = ; + content = ( + + ); } 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 = ( - {'Loading ' + progress + '%'} + {loading + progress + '%'} ); } @@ -386,3 +404,5 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) { ); } + +export default injectIntl(ViewImageModal); \ No newline at end of file diff --git a/web/react/components/view_image_popover_bar.jsx b/web/react/components/view_image_popover_bar.jsx index 1287f4fba..97671b845 100644 --- a/web/react/components/view_image_popover_bar.jsx +++ b/web/react/components/view_image_popover_bar.jsx @@ -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'} + {' | '} @@ -33,7 +38,16 @@ export default class ViewImagePopoverBar extends React.Component { ref='imageFooter' className={footerClass} > - {'File ' + (this.props.fileId + 1) + ' of ' + this.props.totalFiles} + + +
{publicLink} - {'Download'} +
-- cgit v1.2.3-1-g7c22