Adding react-intl i18n to the frontend. No translations yet
This commit is contained in:
		
							parent
							
								
									546c4718e7
								
							
						
					
					
						commit
						01e43c3e57
					
				
					 31 changed files with 263 additions and 223 deletions
				
			
		|  | @ -1,4 +1,5 @@ | |||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   padding: '15px', | ||||
|  | @ -30,7 +31,7 @@ const ColumnBackButton = React.createClass({ | |||
|     return ( | ||||
|       <div onClick={this.handleClick} style={outerStyle} className='column-back-button'> | ||||
|         <i className='fa fa-fw fa-chevron-left' style={iconStyle} /> | ||||
|         Back | ||||
|         <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import IconButton from './icon_button'; | ||||
| import { Motion, spring } from 'react-motion'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const overlayStyle = { | ||||
|   position: 'fixed', | ||||
|  | @ -40,14 +41,14 @@ const Lightbox = React.createClass({ | |||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   render () { | ||||
|     const { isVisible, onOverlayClicked, onCloseClicked, children } = this.props; | ||||
|     const { intl, isVisible, onOverlayClicked, onCloseClicked, children } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='lightbox' style={{...overlayStyle, display: isVisible ? 'flex' : 'none'}} onClick={onOverlayClicked}> | ||||
|         <Motion defaultStyle={{ y: -200 }} style={{ y: spring(isVisible ? 0 : -200) }}> | ||||
|           {({ y }) => | ||||
|             <div style={{...dialogStyle, transform: `translateY(${y}px)`}}> | ||||
|               <IconButton title='Close' icon='times' onClick={onCloseClicked} size={16} style={closeStyle} /> | ||||
|               <IconButton title={intl.formatMessage({ id: 'lightbox.close', defaultMessage: 'Close' })} icon='times' onClick={onCloseClicked} size={16} style={closeStyle} /> | ||||
|               {children} | ||||
|             </div> | ||||
|           } | ||||
|  | @ -58,4 +59,4 @@ const Lightbox = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default Lightbox; | ||||
| export default injectIntl(Lightbox); | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const LoadingIndicator = () => { | ||||
|   const style = { | ||||
|     textAlign: 'center', | ||||
|  | @ -7,7 +9,7 @@ const LoadingIndicator = () => { | |||
|     paddingTop: '120px' | ||||
|   }; | ||||
| 
 | ||||
|   return <div style={style}>Loading...</div>; | ||||
|   return <div style={style}><FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' /></div>; | ||||
| }; | ||||
| 
 | ||||
| export default LoadingIndicator; | ||||
|  |  | |||
|  | @ -1,52 +1,24 @@ | |||
| import moment          from 'moment'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import { | ||||
|   FormattedMessage, | ||||
|   FormattedDate, | ||||
|   FormattedRelative | ||||
| } from 'react-intl'; | ||||
| 
 | ||||
| moment.updateLocale('en', { | ||||
|   relativeTime : { | ||||
|     future: "in %s", | ||||
|     past:   "%s", | ||||
|     s:  "%ds", | ||||
|     m:  "1m", | ||||
|     mm: "%dm", | ||||
|     h:  "1h", | ||||
|     hh: "%dh", | ||||
|     d:  "1d", | ||||
|     dd: "%dd", | ||||
|     M:  "1mo", | ||||
|     MM: "%dmo", | ||||
|     y:  "1y", | ||||
|     yy: "%dy" | ||||
| const RelativeTimestamp = ({ timestamp, now }) => { | ||||
|   const diff = (new Date(now)) - (new Date(timestamp)); | ||||
| 
 | ||||
|   if (diff < 0) { | ||||
|     return <FormattedMessage id='relative_time.just_now' defaultMessage='Just now' /> | ||||
|   } else if (diff > (3600 * 24 * 7 * 1000)) { | ||||
|     return <FormattedDate value={timestamp} /> | ||||
|   } else { | ||||
|     return <FormattedRelative value={timestamp} initialNow={now} updateInterval={0} /> | ||||
|   } | ||||
| }); | ||||
| }; | ||||
| 
 | ||||
| const RelativeTimestamp = React.createClass({ | ||||
| 
 | ||||
|   propTypes: { | ||||
|     timestamp: React.PropTypes.string.isRequired, | ||||
|     now: React.PropTypes.any | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   render () { | ||||
|     const timestamp = moment(this.props.timestamp); | ||||
|     const now       = this.props.now; | ||||
| 
 | ||||
|     let string = ''; | ||||
| 
 | ||||
|     if (timestamp.isAfter(now)) { | ||||
|       string = 'Just now'; | ||||
|     } else { | ||||
|       string = timestamp.from(now); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <span> | ||||
|         {string} | ||||
|       </span> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| RelativeTimestamp.propTypes = { | ||||
|   timestamp: React.PropTypes.string.isRequired, | ||||
|   now: React.PropTypes.any | ||||
| }; | ||||
| 
 | ||||
| export default RelativeTimestamp; | ||||
|  |  | |||
|  | @ -1,12 +1,13 @@ | |||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from './avatar'; | ||||
| import RelativeTimestamp  from './relative_timestamp'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import DisplayName        from './display_name'; | ||||
| import MediaGallery       from './media_gallery'; | ||||
| import VideoPlayer        from './video_player'; | ||||
| import StatusContent      from './status_content'; | ||||
| import StatusActionBar    from './status_action_bar'; | ||||
| import Avatar from './avatar'; | ||||
| import RelativeTimestamp from './relative_timestamp'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import DisplayName from './display_name'; | ||||
| import MediaGallery from './media_gallery'; | ||||
| import VideoPlayer from './video_player'; | ||||
| import StatusContent from './status_content'; | ||||
| import StatusActionBar from './status_action_bar'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const Status = React.createClass({ | ||||
| 
 | ||||
|  | @ -59,7 +60,7 @@ const Status = React.createClass({ | |||
|         <div style={{ cursor: 'default' }}> | ||||
|           <div style={{ marginLeft: '68px', color: '#616b86', padding: '8px 0', paddingBottom: '2px', fontSize: '14px', position: 'relative' }}> | ||||
|             <div style={{ position: 'absolute', 'left': '-26px'}}><i className='fa fa-fw fa-retweet'></i></div> | ||||
|             <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'><strong style={{ color: '#616b86'}}>{displayName}</strong></a> reblogged | ||||
|             <FormattedMessage id='status.reblogged_by' defaultMessage='{name} reblogged' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'><strong style={{ color: '#616b86'}}>{displayName}</strong></a> }} /> | ||||
|           </div> | ||||
| 
 | ||||
|           <Status {...other} wrapped={true} status={status.get('reblog')} /> | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import IconButton         from './icon_button'; | ||||
| import DropdownMenu       from './dropdown_menu'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import IconButton from './icon_button'; | ||||
| import DropdownMenu from './dropdown_menu'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const StatusActionBar = React.createClass({ | ||||
|   propTypes: { | ||||
|  | @ -36,20 +37,20 @@ const StatusActionBar = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { status, me } = this.props; | ||||
|     const { status, me, intl } = this.props; | ||||
|     let menu = []; | ||||
| 
 | ||||
|     if (status.getIn(['account', 'id']) === me) { | ||||
|       menu.push({ text: 'Delete', action: this.handleDeleteClick }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); | ||||
|     } else { | ||||
|       menu.push({ text: 'Mention', action: this.handleMentionClick }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ marginTop: '10px', overflow: 'hidden' }}> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton title='Reply' icon='reply' onClick={this.handleReplyClick} /></div> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('reblogged')} title='Reblog' icon='retweet' onClick={this.handleReblogClick} /></div> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('favourited')} title='Favourite' icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton title={intl.formatMessage({ id: 'status.reply', defaultMessage: 'Reply' })} icon='reply' onClick={this.handleReplyClick} /></div> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('reblogged')} title={intl.formatMessage({ id: 'status.reblog', defaultMessage: 'Reblog' })} icon='retweet' onClick={this.handleReblogClick} /></div> | ||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('favourited')} title={intl.formatMessage({ id: 'status.favourite', defaultMessage: 'Favourite' })} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | ||||
| 
 | ||||
|         <div style={{ width: '18px', height: '18px', float: 'left' }}> | ||||
|           <DropdownMenu items={menu} icon='ellipsis-h' size={18} /> | ||||
|  | @ -60,4 +61,4 @@ const StatusActionBar = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default StatusActionBar; | ||||
| export default injectIntl(StatusActionBar); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ import ImmutablePropTypes  from 'react-immutable-proptypes'; | |||
| import PureRenderMixin     from 'react-addons-pure-render-mixin'; | ||||
| import { ScrollContainer } from 'react-router-scroll'; | ||||
| import StatusContainer     from '../containers/status_container'; | ||||
| import moment              from 'moment'; | ||||
| 
 | ||||
| const StatusList = React.createClass({ | ||||
| 
 | ||||
|  | @ -21,14 +20,14 @@ const StatusList = React.createClass({ | |||
| 
 | ||||
|   getInitialState () { | ||||
|     return { | ||||
|       now: moment() | ||||
|       now: new Date() | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   componentDidMount () { | ||||
|     this._interval = setInterval(() => this.setState({ now: moment() }), 60000); | ||||
|     this._interval = setInterval(() => this.setState({ now: new Date() }), 60000); | ||||
|   }, | ||||
| 
 | ||||
|   componentWillUnmount () { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import IconButton         from './icon_button'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import IconButton from './icon_button'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const videoStyle = { | ||||
|   position: 'relative', | ||||
|  | @ -59,14 +60,16 @@ const VideoPlayer = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { media, intl, width, height } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${this.props.width}px`, height: `${this.props.height}px`, boxSizing: 'border-box', background: '#000', position: 'relative' }}> | ||||
|         <div style={muteStyle}><IconButton title='Toggle sound' icon={this.state.muted ? 'volume-up' : 'volume-off'} onClick={this.handleClick} /></div> | ||||
|         <video src={this.props.media.get('url')} autoPlay='true' loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} /> | ||||
|       <div style={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${width}px`, height: `${height}px`, boxSizing: 'border-box', background: '#000', position: 'relative' }}> | ||||
|         <div style={muteStyle}><IconButton title={intl.formatMessage({ id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' })} icon={this.state.muted ? 'volume-up' : 'volume-off'} onClick={this.handleClick} /></div> | ||||
|         <video src={media.get('url')} autoPlay='true' loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default VideoPlayer; | ||||
| export default injectIntl(VideoPlayer); | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ import Following from '../features/following'; | |||
| import Reblogs from '../features/reblogs'; | ||||
| import Favourites from '../features/favourites'; | ||||
| import HashtagTimeline from '../features/hashtag_timeline'; | ||||
| import { IntlProvider } from 'react-intl'; | ||||
| 
 | ||||
| const store = configureStore(); | ||||
| 
 | ||||
|  | @ -44,7 +45,8 @@ const Mastodon = React.createClass({ | |||
|   propTypes: { | ||||
|     token: React.PropTypes.string.isRequired, | ||||
|     timelines: React.PropTypes.object, | ||||
|     account: React.PropTypes.string | ||||
|     account: React.PropTypes.string, | ||||
|     locale: React.PropTypes.string.isRequired | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
|  | @ -81,30 +83,34 @@ const Mastodon = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { locale } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <Provider store={store}> | ||||
|         <Router history={browserHistory} render={applyRouterMiddleware(useScroll())}> | ||||
|           <Route path='/' component={UI}> | ||||
|             <IndexRoute component={GettingStarted} /> | ||||
|       <IntlProvider locale={locale}> | ||||
|         <Provider store={store}> | ||||
|           <Router history={browserHistory} render={applyRouterMiddleware(useScroll())}> | ||||
|             <Route path='/' component={UI}> | ||||
|               <IndexRoute component={GettingStarted} /> | ||||
| 
 | ||||
|             <Route path='timelines/home' component={HomeTimeline} /> | ||||
|             <Route path='timelines/mentions' component={MentionsTimeline} /> | ||||
|             <Route path='timelines/public' component={PublicTimeline} /> | ||||
|             <Route path='timelines/tag/:id' component={HashtagTimeline} /> | ||||
|               <Route path='timelines/home' component={HomeTimeline} /> | ||||
|               <Route path='timelines/mentions' component={MentionsTimeline} /> | ||||
|               <Route path='timelines/public' component={PublicTimeline} /> | ||||
|               <Route path='timelines/tag/:id' component={HashtagTimeline} /> | ||||
| 
 | ||||
|             <Route path='statuses/new' component={Compose} /> | ||||
|             <Route path='statuses/:statusId' component={Status} /> | ||||
|             <Route path='statuses/:statusId/reblogs' component={Reblogs} /> | ||||
|             <Route path='statuses/:statusId/favourites' component={Favourites} /> | ||||
|               <Route path='statuses/new' component={Compose} /> | ||||
|               <Route path='statuses/:statusId' component={Status} /> | ||||
|               <Route path='statuses/:statusId/reblogs' component={Reblogs} /> | ||||
|               <Route path='statuses/:statusId/favourites' component={Favourites} /> | ||||
| 
 | ||||
|             <Route path='accounts/:accountId' component={Account}> | ||||
|               <IndexRoute component={AccountTimeline} /> | ||||
|               <Route path='followers' component={Followers} /> | ||||
|               <Route path='following' component={Following} /> | ||||
|               <Route path='accounts/:accountId' component={Account}> | ||||
|                 <IndexRoute component={AccountTimeline} /> | ||||
|                 <Route path='followers' component={Followers} /> | ||||
|                 <Route path='following' component={Following} /> | ||||
|               </Route> | ||||
|             </Route> | ||||
|           </Route> | ||||
|         </Router> | ||||
|       </Provider> | ||||
|           </Router> | ||||
|         </Provider> | ||||
|       </IntlProvider> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import DropdownMenu       from '../../../components/dropdown_menu'; | ||||
| import { Link }           from 'react-router'; | ||||
| import DropdownMenu from '../../../components/dropdown_menu'; | ||||
| import { Link } from 'react-router'; | ||||
| import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   borderTop: '1px solid #363c4b', | ||||
|  | @ -36,22 +37,22 @@ const ActionBar = React.createClass({ | |||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   render () { | ||||
|     const { account, me } = this.props; | ||||
|     const { account, me, intl } = this.props; | ||||
| 
 | ||||
|     let menu = []; | ||||
| 
 | ||||
|     menu.push({ text: 'Mention', action: this.props.onMention }); | ||||
|     menu.push({ text: intl.formatMessage({ id: 'account.mention', defaultMessage: 'Mention' }), action: this.props.onMention }); | ||||
| 
 | ||||
|     if (account.get('id') === me) { | ||||
|       menu.push({ text: 'Edit profile', href: '/settings/profile' }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.edit_profile', defaultMessage: 'Edit profile' }), href: '/settings/profile' }); | ||||
|     } else if (account.getIn(['relationship', 'blocking'])) { | ||||
|       menu.push({ text: 'Unblock', action: this.props.onBlock }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.unblock', defaultMessage: 'Unblock' }), action: this.props.onBlock }); | ||||
|     } else if (account.getIn(['relationship', 'following'])) { | ||||
|       menu.push({ text: 'Unfollow', action: this.props.onFollow }); | ||||
|       menu.push({ text: 'Block', action: this.props.onBlock }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.unfollow', defaultMessage: 'Unfollow' }), action: this.props.onFollow }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock }); | ||||
|     } else { | ||||
|       menu.push({ text: 'Follow', action: this.props.onFollow }); | ||||
|       menu.push({ text: 'Block', action: this.props.onBlock }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.follow', defaultMessage: 'Follow' }), action: this.props.onFollow }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock }); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|  | @ -62,24 +63,24 @@ const ActionBar = React.createClass({ | |||
| 
 | ||||
|         <div style={outerLinksStyle}> | ||||
|           <Link to={`/accounts/${account.get('id')}`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Posts</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('statuses_count')}</span> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.posts' defaultMessage='Posts' /></span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('statuses_count')} /></span> | ||||
|           </Link> | ||||
| 
 | ||||
|           <Link to={`/accounts/${account.get('id')}/following`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px 5px' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Follows</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('following_count')}</span> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.follows' defaultMessage='Follows' /></span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('following_count')} /></span> | ||||
|           </Link> | ||||
| 
 | ||||
|           <Link to={`/accounts/${account.get('id')}/followers`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', padding: '10px 5px', borderLeft: '1px solid #363c4b' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Followers</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('followers_count')}</span> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.followers' defaultMessage='Followers' /></span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('followers_count')} /></span> | ||||
|           </Link> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   }, | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default ActionBar; | ||||
| export default injectIntl(ActionBar); | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| import { connect }            from 'react-redux'; | ||||
| import PureRenderMixin        from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes     from 'react-immutable-proptypes'; | ||||
| import { connect } from 'react-redux'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import { | ||||
|   fetchAccountTimeline, | ||||
|   expandAccountTimeline | ||||
| }                             from '../../actions/accounts'; | ||||
| import StatusList             from '../../components/status_list'; | ||||
| import LoadingIndicator       from '../../components/loading_indicator'; | ||||
| } from '../../actions/accounts'; | ||||
| import StatusList from '../../components/status_list'; | ||||
| import LoadingIndicator from '../../components/loading_indicator'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]), | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import Autosuggest from 'react-autosuggest'; | |||
| import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container'; | ||||
| import { debounce } from 'react-decoration'; | ||||
| import UploadButtonContainer from '../containers/upload_button_container'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const getTokenForSuggestions = (str, caretPosition) => { | ||||
|   let word; | ||||
|  | @ -134,6 +135,7 @@ const ComposeForm = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
|     let replyArea  = ''; | ||||
|     const disabled = this.props.is_submitting || this.props.is_uploading; | ||||
| 
 | ||||
|  | @ -142,7 +144,7 @@ const ComposeForm = React.createClass({ | |||
|     } | ||||
| 
 | ||||
|     const inputProps = { | ||||
|       placeholder: 'What is on your mind?', | ||||
|       placeholder: intl.formatMessage({ id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }), | ||||
|       value: this.props.text, | ||||
|       onKeyUp: this.handleKeyUp, | ||||
|       onChange: this.handleChange, | ||||
|  | @ -167,7 +169,7 @@ const ComposeForm = React.createClass({ | |||
|         /> | ||||
| 
 | ||||
|         <div style={{ marginTop: '10px', overflow: 'hidden' }}> | ||||
|           <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={disabled} /></div> | ||||
|           <div style={{ float: 'right' }}><Button text={intl.formatMessage({ id: 'compose_form.publish', defaultMessage: 'Publish' })} onClick={this.handleSubmit} disabled={disabled} /></div> | ||||
|           <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div> | ||||
|           <UploadButtonContainer style={{ paddingTop: '4px' }} /> | ||||
|         </div> | ||||
|  | @ -177,4 +179,4 @@ const ComposeForm = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default ComposeForm; | ||||
| export default injectIntl(ComposeForm); | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from '../../../components/avatar'; | ||||
| import IconButton         from '../../../components/icon_button'; | ||||
| import DisplayName        from '../../../components/display_name'; | ||||
| import { Link }           from 'react-router'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import DisplayName from '../../../components/display_name'; | ||||
| import { Link } from 'react-router'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const NavigationBar = React.createClass({ | ||||
|   propTypes: { | ||||
|  | @ -19,7 +20,7 @@ const NavigationBar = React.createClass({ | |||
| 
 | ||||
|         <div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}> | ||||
|           <strong style={{ fontWeight: '500', display: 'block', color: '#fff' }}>{this.props.account.get('acct')}</strong> | ||||
|           <a href='/settings/profile' style={{ color: 'inherit', textDecoration: 'none' }}>Settings</a> · <Link to='/timelines/public' style={{ color: 'inherit', textDecoration: 'none' }}>Public timeline</Link> · <a href='/auth/sign_out' data-method='delete' style={{ color: 'inherit', textDecoration: 'none' }}>Logout</a> | ||||
|           <a href='/settings/profile' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.settings' defaultMessage='Settings' /></a> · <Link to='/timelines/public' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.public_timeline' defaultMessage='Public timeline' /></Link> · <a href='/auth/sign_out' data-method='delete' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
|  | @ -3,11 +3,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
| import Avatar from '../../../components/avatar'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import DisplayName from '../../../components/display_name'; | ||||
| import emojione from 'emojione'; | ||||
| 
 | ||||
| emojione.imageType = 'png'; | ||||
| emojione.sprites = false; | ||||
| emojione.imagePathPNG = '/emoji/'; | ||||
| import emojify from '../../../emoji'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const ReplyIndicator = React.createClass({ | ||||
| 
 | ||||
|  | @ -34,12 +31,13 @@ const ReplyIndicator = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     let content = { __html: emojione.unicodeToImage(this.props.status.get('content')) }; | ||||
|     const { intl } = this.props; | ||||
|     const content  = { __html: emojify(this.props.status.get('content')) }; | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ background: '#9baec8', padding: '10px' }}> | ||||
|         <div style={{ overflow: 'hidden', marginBottom: '5px' }}> | ||||
|           <div style={{ float: 'right', lineHeight: '24px' }}><IconButton title='Cancel' icon='times' onClick={this.handleClick} /></div> | ||||
|           <div style={{ float: 'right', lineHeight: '24px' }}><IconButton title={intl.formatMessage({ id: 'reply_indicator.cancel', defaultMessage: 'Cancel' })} icon='times' onClick={this.handleClick} /></div> | ||||
| 
 | ||||
|           <a href={this.props.status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name' style={{ display: 'block', maxWidth: '100%', paddingRight: '25px', color: '#282c37', textDecoration: 'none', overflow: 'hidden', lineHeight: '24px' }}> | ||||
|             <div style={{ float: 'left', marginRight: '5px' }}><Avatar size={24} src={this.props.status.getIn(['account', 'avatar'])} /></div> | ||||
|  | @ -54,4 +52,4 @@ const ReplyIndicator = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default ReplyIndicator; | ||||
| export default injectIntl(ReplyIndicator); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
| import Autosuggest from 'react-autosuggest'; | ||||
| import AutosuggestAccountContainer from '../containers/autosuggest_account_container'; | ||||
| import { debounce } from 'react-decoration'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const getSuggestionValue = suggestion => suggestion.value; | ||||
| 
 | ||||
|  | @ -94,7 +95,7 @@ const Search = React.createClass({ | |||
| 
 | ||||
|   render () { | ||||
|     const inputProps = { | ||||
|       placeholder: 'Search', | ||||
|       placeholder: this.props.intl.formatMessage({ id: 'search.placeholder', defaultMessage: 'Search' }), | ||||
|       value: this.props.value, | ||||
|       onChange: this.onChange, | ||||
|       style: inputStyle | ||||
|  | @ -125,4 +126,4 @@ const Search = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default Search; | ||||
| export default injectIntl(Search); | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import AccountContainer   from '../../followers/containers/account_container'; | ||||
| import AccountContainer from '../../followers/containers/account_container'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   position: 'relative' | ||||
|  | @ -66,13 +67,13 @@ const SuggestionsBox = React.createClass({ | |||
|     let nextLink = ''; | ||||
| 
 | ||||
|     if (accountIds.size > perWindow) { | ||||
|       nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}>Refresh</a>; | ||||
|       nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}><FormattedMessage id='suggestions_box.refresh' defaultMessage='Refresh' /></a>; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={outerStyle}> | ||||
|         <strong style={headerStyle}> | ||||
|           Who to follow {nextLink} | ||||
|           <FormattedMessage id='suggestions_box.who_to_follow' defaultMessage='Who to follow' /> {nextLink} | ||||
|         </strong> | ||||
| 
 | ||||
|         {accountIds.skip(perWindow * this.state.index).take(perWindow).map(accountId => <AccountContainer key={accountId} id={accountId} withNote={false} />)} | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const UploadButton = React.createClass({ | ||||
| 
 | ||||
|  | @ -26,9 +27,11 @@ const UploadButton = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={this.props.style}> | ||||
|         <IconButton icon='photo' title='Add media' disabled={this.props.disabled} onClick={this.handleClick} size={24} /> | ||||
|         <IconButton icon='photo' title={intl.formatMessage({ id: 'upload_button.label', defaultMessage: 'Add media' })} disabled={this.props.disabled} onClick={this.handleClick} size={24} /> | ||||
|         <input ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={this.props.disabled} style={{ display: 'none' }} /> | ||||
|       </div> | ||||
|     ); | ||||
|  | @ -36,4 +39,4 @@ const UploadButton = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default UploadButton; | ||||
| export default injectIntl(UploadButton); | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import IconButton         from '../../../components/icon_button'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const UploadForm = React.createClass({ | ||||
| 
 | ||||
|  | @ -13,10 +14,12 @@ const UploadForm = React.createClass({ | |||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
| 
 | ||||
|     const uploads = this.props.media.map(attachment => ( | ||||
|       <div key={attachment.get('id')} style={{ borderRadius: '4px', marginBottom: '10px' }} className='transparent-background'> | ||||
|         <div style={{ width: '100%', height: '100px', borderRadius: '4px', background: `url(${attachment.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }}> | ||||
|           <IconButton icon='times' title='Undo' size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} /> | ||||
|           <IconButton icon='times' title={intl.formatMessage({ id: 'upload_form.undo', defaultMessage: 'Undo' })} size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     )); | ||||
|  | @ -30,4 +33,4 @@ const UploadForm = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default UploadForm; | ||||
| export default injectIntl(UploadForm); | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| import { connect }            from 'react-redux'; | ||||
| import PureRenderMixin        from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes     from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator       from '../../components/loading_indicator'; | ||||
| import { fetchFavourites }    from '../../actions/interactions'; | ||||
| import { ScrollContainer }    from 'react-router-scroll'; | ||||
| import AccountContainer       from '../followers/containers/account_container'; | ||||
| import Column                 from '../ui/components/column'; | ||||
| import ColumnBackButton       from '../../components/column_back_button'; | ||||
| import { connect } from 'react-redux'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator from '../../components/loading_indicator'; | ||||
| import { fetchFavourites } from '../../actions/interactions'; | ||||
| import { ScrollContainer } from 'react-router-scroll'; | ||||
| import AccountContainer from '../followers/containers/account_container'; | ||||
| import Column from '../ui/components/column'; | ||||
| import ColumnBackButton from '../../components/column_back_button'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   accountIds: state.getIn(['user_lists', 'favourited_by', Number(props.params.statusId)]) | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from '../../../components/avatar'; | ||||
| import DisplayName        from '../../../components/display_name'; | ||||
| import { Link }           from 'react-router'; | ||||
| import IconButton         from '../../../components/icon_button'; | ||||
| import Avatar from '../../../components/avatar'; | ||||
| import DisplayName from '../../../components/display_name'; | ||||
| import { Link } from 'react-router'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   padding: '10px', | ||||
|  | @ -51,7 +52,7 @@ const Account = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { account, me, withNote } = this.props; | ||||
|     const { account, me, withNote, intl } = this.props; | ||||
| 
 | ||||
|     if (!account) { | ||||
|       return <div />; | ||||
|  | @ -68,7 +69,7 @@ const Account = React.createClass({ | |||
| 
 | ||||
|       buttons = ( | ||||
|         <div style={buttonsStyle}> | ||||
|           <IconButton icon={following ? 'user-times' : 'user-plus'} title='Follow' onClick={this.handleFollow} active={following} /> | ||||
|           <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage({ id: 'account.follow', defaultMessage: 'Follow' })} onClick={this.handleFollow} active={following} /> | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|  | @ -91,4 +92,4 @@ const Account = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default Account; | ||||
| export default injectIntl(Account); | ||||
|  |  | |||
|  | @ -1,15 +1,15 @@ | |||
| import Column   from '../ui/components/column'; | ||||
| import Column from '../ui/components/column'; | ||||
| import { Link } from 'react-router'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const GettingStarted = () => { | ||||
|   return ( | ||||
|     <Column> | ||||
|       <div className='static-content'> | ||||
|         <h1>Getting started</h1> | ||||
|         <p>You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form in the bottom of the sidebar.</p> | ||||
|         <p>If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.</p> | ||||
|         <p>The developer of this project can be followed as Gargron@mastodon.social</p> | ||||
|         <p>Also <Link to='/timelines/public' style={{ color: '#2b90d9', textDecoration: 'none' }}>check out the public timeline for a start</Link>!</p> | ||||
|         <h1><FormattedMessage id='getting_started.heading' defaultMessage='Getting started' /></h1> | ||||
|         <p><FormattedMessage id='getting_started.about_addressing' defaultMessage='You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form at the top of the sidebar.' /></p> | ||||
|         <p><FormattedMessage id='getting_started.about_shortcuts' defaultMessage='If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.' /></p> | ||||
|         <p><FormattedMessage id='getting_started.about_developer' defaultMessage='The developer of this project can be followed as Gargron@mastodon.social' /></p> | ||||
|       </div> | ||||
|     </Column> | ||||
|   ); | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import { connect }         from 'react-redux'; | ||||
| import PureRenderMixin     from 'react-addons-pure-render-mixin'; | ||||
| import { connect } from 'react-redux'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import StatusListContainer from '../ui/containers/status_list_container'; | ||||
| import Column              from '../ui/components/column'; | ||||
| import Column from '../ui/components/column'; | ||||
| import { refreshTimeline } from '../../actions/timelines'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const HomeTimeline = React.createClass({ | ||||
| 
 | ||||
|  | @ -17,8 +18,10 @@ const HomeTimeline = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <Column icon='home' heading='Home'> | ||||
|       <Column icon='home' heading={intl.formatMessage({ id: 'column.home', defaultMessage: 'Home' })}> | ||||
|         <StatusListContainer {...this.props} type='home' /> | ||||
|       </Column> | ||||
|     ); | ||||
|  | @ -26,4 +29,4 @@ const HomeTimeline = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect()(HomeTimeline); | ||||
| export default connect()(injectIntl(HomeTimeline)); | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import { connect }         from 'react-redux'; | ||||
| import PureRenderMixin     from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import StatusListContainer from '../ui/containers/status_list_container'; | ||||
| import Column              from '../ui/components/column'; | ||||
| import Column from '../ui/components/column'; | ||||
| import { refreshTimeline } from '../../actions/timelines'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const MentionsTimeline = React.createClass({ | ||||
| 
 | ||||
|  | @ -17,8 +18,10 @@ const MentionsTimeline = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <Column icon='at' heading='Mentions'> | ||||
|       <Column icon='at' heading={intl.formatMessage({ id: 'column.mentions', defaultMessage: 'Mentions' })}> | ||||
|         <StatusListContainer {...this.props} type='mentions' /> | ||||
|       </Column> | ||||
|     ); | ||||
|  | @ -26,4 +29,4 @@ const MentionsTimeline = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect()(MentionsTimeline); | ||||
| export default connect()(injectIntl(MentionsTimeline)); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import { | |||
|   updateTimeline, | ||||
|   deleteFromTimelines | ||||
| } from '../../actions/timelines'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const PublicTimeline = React.createClass({ | ||||
| 
 | ||||
|  | @ -44,8 +45,10 @@ const PublicTimeline = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <Column icon='globe' heading='Public'> | ||||
|       <Column icon='globe' heading={intl.formatMessage({ id: 'column.public', defaultMessage: 'Public' })}> | ||||
|         <StatusListContainer type='public' /> | ||||
|       </Column> | ||||
|     ); | ||||
|  | @ -53,4 +56,4 @@ const PublicTimeline = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect()(PublicTimeline); | ||||
| export default connect()(injectIntl(PublicTimeline)); | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| import { connect }            from 'react-redux'; | ||||
| import PureRenderMixin        from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes     from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator       from '../../components/loading_indicator'; | ||||
| import { fetchReblogs }       from '../../actions/interactions'; | ||||
| import { ScrollContainer }    from 'react-router-scroll'; | ||||
| import AccountContainer       from '../followers/containers/account_container'; | ||||
| import Column                 from '../ui/components/column'; | ||||
| import ColumnBackButton       from '../../components/column_back_button'; | ||||
| import { connect } from 'react-redux'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator from '../../components/loading_indicator'; | ||||
| import { fetchReblogs } from '../../actions/interactions'; | ||||
| import { ScrollContainer } from 'react-router-scroll'; | ||||
| import AccountContainer from '../followers/containers/account_container'; | ||||
| import Column from '../ui/components/column'; | ||||
| import ColumnBackButton from '../../components/column_back_button'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   accountIds: state.getIn(['user_lists', 'reblogged_by', Number(props.params.statusId)]) | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import IconButton         from '../../../components/icon_button'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import IconButton from '../../../components/icon_button'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import DropdownMenu       from '../../../components/dropdown_menu'; | ||||
| import DropdownMenu from '../../../components/dropdown_menu'; | ||||
| import { injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| const ActionBar = React.createClass({ | ||||
| 
 | ||||
|  | @ -38,21 +39,21 @@ const ActionBar = React.createClass({ | |||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { status, me } = this.props; | ||||
|     const { status, me, intl } = this.props; | ||||
| 
 | ||||
|     let menu = []; | ||||
| 
 | ||||
|     if (me === status.getIn(['account', 'id'])) { | ||||
|       menu.push({ text: 'Delete', action: this.handleDeleteClick }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); | ||||
|     } else { | ||||
|       menu.push({ text: 'Mention', action: this.handleMentionClick }); | ||||
|       menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ background: '#2f3441', display: 'flex', flexDirection: 'row', borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px 0' }}> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title='Reply' icon='reply' onClick={this.handleReplyClick} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('reblogged')} title='Reblog' icon='retweet' onClick={this.handleReblogClick} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('favourited')} title='Favourite' icon='star' onClick={this.handleFavouriteClick}  activeStyle={{ color: '#ca8f04' }} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title={intl.formatMessage({ id: 'status.reply', defaultMessage: 'Reply' })} icon='reply' onClick={this.handleReplyClick} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('reblogged')} title={intl.formatMessage({ id: 'status.reblog', defaultMessage: 'Reblog' })} icon='retweet' onClick={this.handleReblogClick} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('favourited')} title={intl.formatMessage({ id: 'status.favourite', defaultMessage: 'Favourite' })} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | ||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><DropdownMenu size={18} icon='ellipsis-h' items={menu} /></div> | ||||
|       </div> | ||||
|     ); | ||||
|  | @ -60,4 +61,4 @@ const ActionBar = React.createClass({ | |||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default ActionBar; | ||||
| export default injectIntl(ActionBar); | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from '../../../components/avatar'; | ||||
| import DisplayName        from '../../../components/display_name'; | ||||
| import StatusContent      from '../../../components/status_content'; | ||||
| import MediaGallery       from '../../../components/media_gallery'; | ||||
| import VideoPlayer        from '../../../components/video_player'; | ||||
| import moment             from 'moment'; | ||||
| import { Link }           from 'react-router'; | ||||
| import Avatar from '../../../components/avatar'; | ||||
| import DisplayName from '../../../components/display_name'; | ||||
| import StatusContent from '../../../components/status_content'; | ||||
| import MediaGallery from '../../../components/media_gallery'; | ||||
| import VideoPlayer from '../../../components/video_player'; | ||||
| import { Link } from 'react-router'; | ||||
| import { FormattedDate, FormattedNumber } from 'react-intl'; | ||||
| 
 | ||||
| const DetailedStatus = React.createClass({ | ||||
| 
 | ||||
|  | @ -54,7 +54,7 @@ const DetailedStatus = React.createClass({ | |||
|         {media} | ||||
| 
 | ||||
|         <div style={{ marginTop: '15px', color: '#616b86', fontSize: '14px', lineHeight: '18px' }}> | ||||
|           <a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'>{moment(status.get('created_at')).format('HH:mm, DD MMM Y')}</a> · <Link to={`/statuses/${status.get('id')}/reblogs`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('reblogs_count')}</span></Link> · <Link to={`/statuses/${status.get('id')}/favourites`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('favourites_count')}</span></Link> | ||||
|           <a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'><FormattedDate value={status.get('created_at')} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /></a> · <Link to={`/statuses/${status.get('id')}/reblogs`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('reblogs_count')} /></span></Link> · <Link to={`/statuses/${status.get('id')}/favourites`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}><FormattedNumber value={status.get('favourites_count')} /></span></Link> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import { Link } from 'react-router'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   background: '#373b4a', | ||||
|  | @ -28,10 +29,10 @@ const tabActiveStyle = { | |||
| const TabsBar = () => { | ||||
|   return ( | ||||
|     <div style={outerStyle}> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/statuses/new'><i className='fa fa-fw fa-pencil' /> Compose</Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/home'><i className='fa fa-fw fa-home' /> Home</Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/mentions'><i className='fa fa-fw fa-at' /> Mentions</Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/public'><i className='fa fa-fw fa-globe' /> Public</Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/statuses/new'><i className='fa fa-fw fa-pencil' /> <FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/home'><i className='fa fa-fw fa-home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/mentions'><i className='fa fa-fw fa-at' /> <FormattedMessage id='tabs_bar.mentions' defaultMessage='Mentions' /></Link> | ||||
|       <Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/public'><i className='fa fa-fw fa-globe' /> <FormattedMessage id='tabs_bar.public' defaultMessage='Public' /></Link> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ module HomeHelper | |||
|     { | ||||
|       token: @token, | ||||
|       account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json), | ||||
|       locale: I18n.locale, | ||||
|     } | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -21,7 +21,6 @@ | |||
|     "immutable": "^3.8.1", | ||||
|     "jsdom": "^9.6.0", | ||||
|     "mocha": "^3.1.1", | ||||
|     "moment": "^2.14.1", | ||||
|     "react": "^15.3.2", | ||||
|     "react-addons-perf": "^15.3.2", | ||||
|     "react-addons-pure-render-mixin": "^15.3.1", | ||||
|  | @ -46,6 +45,7 @@ | |||
|     "http-link-header": "^0.5.0", | ||||
|     "react-autosuggest": "^7.0.1", | ||||
|     "react-decoration": "^1.4.0", | ||||
|     "react-intl": "^2.1.5", | ||||
|     "react-motion": "^0.4.5", | ||||
|     "react-responsive": "^1.1.5", | ||||
|     "react-router-scroll": "^0.3.2" | ||||
|  |  | |||
							
								
								
									
										39
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								yarn.lock
									
									
									
									
									
								
							|  | @ -2497,12 +2497,38 @@ interpret@^1.0.0: | |||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" | ||||
| 
 | ||||
| intl-format-cache@^2.0.5: | ||||
|   version "2.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.0.5.tgz#b484cefcb9353f374f25de389a3ceea1af18d7c9" | ||||
| 
 | ||||
| intl-messageformat-parser@1.2.0: | ||||
|   version "1.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.2.0.tgz#5906b7f953ab7470e0dc8549097b648b991892ff" | ||||
| 
 | ||||
| intl-messageformat@^1.3.0, intl-messageformat@1.3.0: | ||||
|   version "1.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-1.3.0.tgz#f7d926aded7a3ab19b2dc601efd54e99a4bd4eae" | ||||
|   dependencies: | ||||
|     intl-messageformat-parser "1.2.0" | ||||
| 
 | ||||
| intl-relativeformat@^1.3.0: | ||||
|   version "1.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-1.3.0.tgz#893dc7076fccd380cf091a2300c380fa57ace45b" | ||||
|   dependencies: | ||||
|     intl-messageformat "1.3.0" | ||||
| 
 | ||||
| invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@2.x.x: | ||||
|   version "2.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" | ||||
|   dependencies: | ||||
|     loose-envify "^1.0.0" | ||||
| 
 | ||||
| invariant@^2.1.1: | ||||
|   version "2.2.2" | ||||
|   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" | ||||
|   dependencies: | ||||
|     loose-envify "^1.0.0" | ||||
| 
 | ||||
| ipaddr.js@1.1.1: | ||||
|   version "1.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" | ||||
|  | @ -3151,10 +3177,6 @@ module-deps@^4.0.2: | |||
|     through2 "^2.0.0" | ||||
|     xtend "^4.0.0" | ||||
| 
 | ||||
| moment@^2.14.1: | ||||
|   version "2.15.1" | ||||
|   resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.1.tgz#e979c2a29e22888e60f396f2220a6118f85cd94c" | ||||
| 
 | ||||
| ms@0.7.1: | ||||
|   version "0.7.1" | ||||
|   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" | ||||
|  | @ -3912,6 +3934,15 @@ react-inspector@^1.1.0: | |||
|   dependencies: | ||||
|     is-dom "^1.0.5" | ||||
| 
 | ||||
| react-intl: | ||||
|   version "2.1.5" | ||||
|   resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.1.5.tgz#f9795ea34b790dcb5d0d8ef7060dddbe85bf8763" | ||||
|   dependencies: | ||||
|     intl-format-cache "^2.0.5" | ||||
|     intl-messageformat "^1.3.0" | ||||
|     intl-relativeformat "^1.3.0" | ||||
|     invariant "^2.1.1" | ||||
| 
 | ||||
| react-komposer@^1.9.0: | ||||
|   version "1.13.1" | ||||
|   resolved "https://registry.yarnpkg.com/react-komposer/-/react-komposer-1.13.1.tgz#4b8ac4bcc71323bd7413dcab95c831197f50eed0" | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue