forked from cybrespace/mastodon
Adding react-intl i18n to the frontend. No translations yet
This commit is contained in:
parent
546c4718e7
commit
01e43c3e57
|
@ -1,4 +1,5 @@
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
padding: '15px',
|
padding: '15px',
|
||||||
|
@ -30,7 +31,7 @@ const ColumnBackButton = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div onClick={this.handleClick} style={outerStyle} className='column-back-button'>
|
<div onClick={this.handleClick} style={outerStyle} className='column-back-button'>
|
||||||
<i className='fa fa-fw fa-chevron-left' style={iconStyle} />
|
<i className='fa fa-fw fa-chevron-left' style={iconStyle} />
|
||||||
Back
|
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import IconButton from './icon_button';
|
import IconButton from './icon_button';
|
||||||
import { Motion, spring } from 'react-motion';
|
import { Motion, spring } from 'react-motion';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const overlayStyle = {
|
const overlayStyle = {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
|
@ -40,14 +41,14 @@ const Lightbox = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { isVisible, onOverlayClicked, onCloseClicked, children } = this.props;
|
const { intl, isVisible, onOverlayClicked, onCloseClicked, children } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='lightbox' style={{...overlayStyle, display: isVisible ? 'flex' : 'none'}} onClick={onOverlayClicked}>
|
<div className='lightbox' style={{...overlayStyle, display: isVisible ? 'flex' : 'none'}} onClick={onOverlayClicked}>
|
||||||
<Motion defaultStyle={{ y: -200 }} style={{ y: spring(isVisible ? 0 : -200) }}>
|
<Motion defaultStyle={{ y: -200 }} style={{ y: spring(isVisible ? 0 : -200) }}>
|
||||||
{({ y }) =>
|
{({ y }) =>
|
||||||
<div style={{...dialogStyle, transform: `translateY(${y}px)`}}>
|
<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}
|
{children}
|
||||||
</div>
|
</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 LoadingIndicator = () => {
|
||||||
const style = {
|
const style = {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
@ -7,7 +9,7 @@ const LoadingIndicator = () => {
|
||||||
paddingTop: '120px'
|
paddingTop: '120px'
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div style={style}>Loading...</div>;
|
return <div style={style}><FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' /></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoadingIndicator;
|
export default LoadingIndicator;
|
||||||
|
|
|
@ -1,52 +1,24 @@
|
||||||
import moment from 'moment';
|
import {
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
FormattedMessage,
|
||||||
|
FormattedDate,
|
||||||
|
FormattedRelative
|
||||||
|
} from 'react-intl';
|
||||||
|
|
||||||
moment.updateLocale('en', {
|
const RelativeTimestamp = ({ timestamp, now }) => {
|
||||||
relativeTime : {
|
const diff = (new Date(now)) - (new Date(timestamp));
|
||||||
future: "in %s",
|
|
||||||
past: "%s",
|
if (diff < 0) {
|
||||||
s: "%ds",
|
return <FormattedMessage id='relative_time.just_now' defaultMessage='Just now' />
|
||||||
m: "1m",
|
} else if (diff > (3600 * 24 * 7 * 1000)) {
|
||||||
mm: "%dm",
|
return <FormattedDate value={timestamp} />
|
||||||
h: "1h",
|
} else {
|
||||||
hh: "%dh",
|
return <FormattedRelative value={timestamp} initialNow={now} updateInterval={0} />
|
||||||
d: "1d",
|
|
||||||
dd: "%dd",
|
|
||||||
M: "1mo",
|
|
||||||
MM: "%dmo",
|
|
||||||
y: "1y",
|
|
||||||
yy: "%dy"
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
const RelativeTimestamp = React.createClass({
|
RelativeTimestamp.propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
timestamp: React.PropTypes.string.isRequired,
|
timestamp: React.PropTypes.string.isRequired,
|
||||||
now: React.PropTypes.any
|
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export default RelativeTimestamp;
|
export default RelativeTimestamp;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import MediaGallery from './media_gallery';
|
||||||
import VideoPlayer from './video_player';
|
import VideoPlayer from './video_player';
|
||||||
import StatusContent from './status_content';
|
import StatusContent from './status_content';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
const Status = React.createClass({
|
const Status = React.createClass({
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ const Status = React.createClass({
|
||||||
<div style={{ cursor: 'default' }}>
|
<div style={{ cursor: 'default' }}>
|
||||||
<div style={{ marginLeft: '68px', color: '#616b86', padding: '8px 0', paddingBottom: '2px', fontSize: '14px', position: 'relative' }}>
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<Status {...other} wrapped={true} status={status.get('reblog')} />
|
<Status {...other} wrapped={true} status={status.get('reblog')} />
|
||||||
|
|
|
@ -2,6 +2,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import IconButton from './icon_button';
|
import IconButton from './icon_button';
|
||||||
import DropdownMenu from './dropdown_menu';
|
import DropdownMenu from './dropdown_menu';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const StatusActionBar = React.createClass({
|
const StatusActionBar = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -36,20 +37,20 @@ const StatusActionBar = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, me } = this.props;
|
const { status, me, intl } = this.props;
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
if (status.getIn(['account', 'id']) === me) {
|
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 {
|
} else {
|
||||||
menu.push({ text: 'Mention', action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: '10px', overflow: 'hidden' }}>
|
<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 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='Reblog' icon='retweet' onClick={this.handleReblogClick} /></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='Favourite' icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></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' }}>
|
<div style={{ width: '18px', height: '18px', float: 'left' }}>
|
||||||
<DropdownMenu items={menu} icon='ellipsis-h' size={18} />
|
<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 PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import { ScrollContainer } from 'react-router-scroll';
|
import { ScrollContainer } from 'react-router-scroll';
|
||||||
import StatusContainer from '../containers/status_container';
|
import StatusContainer from '../containers/status_container';
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
const StatusList = React.createClass({
|
const StatusList = React.createClass({
|
||||||
|
|
||||||
|
@ -21,14 +20,14 @@ const StatusList = React.createClass({
|
||||||
|
|
||||||
getInitialState () {
|
getInitialState () {
|
||||||
return {
|
return {
|
||||||
now: moment()
|
now: new Date()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this._interval = setInterval(() => this.setState({ now: moment() }), 60000);
|
this._interval = setInterval(() => this.setState({ now: new Date() }), 60000);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import IconButton from './icon_button';
|
import IconButton from './icon_button';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const videoStyle = {
|
const videoStyle = {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
@ -59,14 +60,16 @@ const VideoPlayer = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { media, intl, width, height } = this.props;
|
||||||
|
|
||||||
return (
|
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={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${width}px`, height: `${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>
|
<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={this.props.media.get('url')} autoPlay='true' loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} />
|
<video src={media.get('url')} autoPlay='true' loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default VideoPlayer;
|
export default injectIntl(VideoPlayer);
|
||||||
|
|
|
@ -32,6 +32,7 @@ import Following from '../features/following';
|
||||||
import Reblogs from '../features/reblogs';
|
import Reblogs from '../features/reblogs';
|
||||||
import Favourites from '../features/favourites';
|
import Favourites from '../features/favourites';
|
||||||
import HashtagTimeline from '../features/hashtag_timeline';
|
import HashtagTimeline from '../features/hashtag_timeline';
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
||||||
|
@ -44,7 +45,8 @@ const Mastodon = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
token: React.PropTypes.string.isRequired,
|
token: React.PropTypes.string.isRequired,
|
||||||
timelines: React.PropTypes.object,
|
timelines: React.PropTypes.object,
|
||||||
account: React.PropTypes.string
|
account: React.PropTypes.string,
|
||||||
|
locale: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
@ -81,7 +83,10 @@ const Mastodon = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { locale } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<IntlProvider locale={locale}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
||||||
<Route path='/' component={UI}>
|
<Route path='/' component={UI}>
|
||||||
|
@ -105,6 +110,7 @@ const Mastodon = React.createClass({
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import DropdownMenu from '../../../components/dropdown_menu';
|
import DropdownMenu from '../../../components/dropdown_menu';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
borderTop: '1px solid #363c4b',
|
borderTop: '1px solid #363c4b',
|
||||||
|
@ -36,22 +37,22 @@ const ActionBar = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, me } = this.props;
|
const { account, me, intl } = this.props;
|
||||||
|
|
||||||
let menu = [];
|
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) {
|
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'])) {
|
} 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'])) {
|
} else if (account.getIn(['relationship', 'following'])) {
|
||||||
menu.push({ text: 'Unfollow', action: this.props.onFollow });
|
menu.push({ text: intl.formatMessage({ id: 'account.unfollow', defaultMessage: 'Unfollow' }), action: this.props.onFollow });
|
||||||
menu.push({ text: 'Block', action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: 'Follow', action: this.props.onFollow });
|
menu.push({ text: intl.formatMessage({ id: 'account.follow', defaultMessage: 'Follow' }), action: this.props.onFollow });
|
||||||
menu.push({ text: 'Block', action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -62,24 +63,24 @@ const ActionBar = React.createClass({
|
||||||
|
|
||||||
<div style={outerLinksStyle}>
|
<div style={outerLinksStyle}>
|
||||||
<Link to={`/accounts/${account.get('id')}`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}>
|
<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', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.posts' defaultMessage='Posts' /></span>
|
||||||
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('statuses_count')}</span>
|
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('statuses_count')} /></span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to={`/accounts/${account.get('id')}/following`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px 5px' }}>
|
<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', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.follows' defaultMessage='Follows' /></span>
|
||||||
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('following_count')}</span>
|
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('following_count')} /></span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to={`/accounts/${account.get('id')}/followers`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', padding: '10px 5px', borderLeft: '1px solid #363c4b' }}>
|
<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', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}><FormattedMessage id='account.followers' defaultMessage='Followers' /></span>
|
||||||
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('followers_count')}</span>
|
<span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}><FormattedNumber value={account.get('followers_count')} /></span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ActionBar;
|
export default injectIntl(ActionBar);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Autosuggest from 'react-autosuggest';
|
||||||
import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container';
|
import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container';
|
||||||
import { debounce } from 'react-decoration';
|
import { debounce } from 'react-decoration';
|
||||||
import UploadButtonContainer from '../containers/upload_button_container';
|
import UploadButtonContainer from '../containers/upload_button_container';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const getTokenForSuggestions = (str, caretPosition) => {
|
const getTokenForSuggestions = (str, caretPosition) => {
|
||||||
let word;
|
let word;
|
||||||
|
@ -134,6 +135,7 @@ const ComposeForm = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
let replyArea = '';
|
let replyArea = '';
|
||||||
const disabled = this.props.is_submitting || this.props.is_uploading;
|
const disabled = this.props.is_submitting || this.props.is_uploading;
|
||||||
|
|
||||||
|
@ -142,7 +144,7 @@ const ComposeForm = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputProps = {
|
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,
|
value: this.props.text,
|
||||||
onKeyUp: this.handleKeyUp,
|
onKeyUp: this.handleKeyUp,
|
||||||
onChange: this.handleChange,
|
onChange: this.handleChange,
|
||||||
|
@ -167,7 +169,7 @@ const ComposeForm = React.createClass({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={{ marginTop: '10px', overflow: 'hidden' }}>
|
<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>
|
<div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div>
|
||||||
<UploadButtonContainer style={{ paddingTop: '4px' }} />
|
<UploadButtonContainer style={{ paddingTop: '4px' }} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -177,4 +179,4 @@ const ComposeForm = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ComposeForm;
|
export default injectIntl(ComposeForm);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Avatar from '../../../components/avatar';
|
||||||
import IconButton from '../../../components/icon_button';
|
import IconButton from '../../../components/icon_button';
|
||||||
import DisplayName from '../../../components/display_name';
|
import DisplayName from '../../../components/display_name';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
const NavigationBar = React.createClass({
|
const NavigationBar = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -19,7 +20,7 @@ const NavigationBar = React.createClass({
|
||||||
|
|
||||||
<div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}>
|
<div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}>
|
||||||
<strong style={{ fontWeight: '500', display: 'block', color: '#fff' }}>{this.props.account.get('acct')}</strong>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,11 +3,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Avatar from '../../../components/avatar';
|
import Avatar from '../../../components/avatar';
|
||||||
import IconButton from '../../../components/icon_button';
|
import IconButton from '../../../components/icon_button';
|
||||||
import DisplayName from '../../../components/display_name';
|
import DisplayName from '../../../components/display_name';
|
||||||
import emojione from 'emojione';
|
import emojify from '../../../emoji';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
emojione.imageType = 'png';
|
|
||||||
emojione.sprites = false;
|
|
||||||
emojione.imagePathPNG = '/emoji/';
|
|
||||||
|
|
||||||
const ReplyIndicator = React.createClass({
|
const ReplyIndicator = React.createClass({
|
||||||
|
|
||||||
|
@ -34,12 +31,13 @@ const ReplyIndicator = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
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 (
|
return (
|
||||||
<div style={{ background: '#9baec8', padding: '10px' }}>
|
<div style={{ background: '#9baec8', padding: '10px' }}>
|
||||||
<div style={{ overflow: 'hidden', marginBottom: '5px' }}>
|
<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' }}>
|
<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>
|
<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 Autosuggest from 'react-autosuggest';
|
||||||
import AutosuggestAccountContainer from '../containers/autosuggest_account_container';
|
import AutosuggestAccountContainer from '../containers/autosuggest_account_container';
|
||||||
import { debounce } from 'react-decoration';
|
import { debounce } from 'react-decoration';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const getSuggestionValue = suggestion => suggestion.value;
|
const getSuggestionValue = suggestion => suggestion.value;
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ const Search = React.createClass({
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
placeholder: 'Search',
|
placeholder: this.props.intl.formatMessage({ id: 'search.placeholder', defaultMessage: 'Search' }),
|
||||||
value: this.props.value,
|
value: this.props.value,
|
||||||
onChange: this.onChange,
|
onChange: this.onChange,
|
||||||
style: inputStyle
|
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 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 = {
|
const outerStyle = {
|
||||||
position: 'relative'
|
position: 'relative'
|
||||||
|
@ -66,13 +67,13 @@ const SuggestionsBox = React.createClass({
|
||||||
let nextLink = '';
|
let nextLink = '';
|
||||||
|
|
||||||
if (accountIds.size > perWindow) {
|
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 (
|
return (
|
||||||
<div style={outerStyle}>
|
<div style={outerStyle}>
|
||||||
<strong style={headerStyle}>
|
<strong style={headerStyle}>
|
||||||
Who to follow {nextLink}
|
<FormattedMessage id='suggestions_box.who_to_follow' defaultMessage='Who to follow' /> {nextLink}
|
||||||
</strong>
|
</strong>
|
||||||
|
|
||||||
{accountIds.skip(perWindow * this.state.index).take(perWindow).map(accountId => <AccountContainer key={accountId} id={accountId} withNote={false} />)}
|
{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 PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import IconButton from '../../../components/icon_button';
|
import IconButton from '../../../components/icon_button';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const UploadButton = React.createClass({
|
const UploadButton = React.createClass({
|
||||||
|
|
||||||
|
@ -26,9 +27,11 @@ const UploadButton = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={this.props.style}>
|
<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' }} />
|
<input ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={this.props.disabled} style={{ display: 'none' }} />
|
||||||
</div>
|
</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 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({
|
const UploadForm = React.createClass({
|
||||||
|
|
||||||
|
@ -13,10 +14,12 @@ const UploadForm = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
const uploads = this.props.media.map(attachment => (
|
const uploads = this.props.media.map(attachment => (
|
||||||
<div key={attachment.get('id')} style={{ borderRadius: '4px', marginBottom: '10px' }} className='transparent-background'>
|
<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' }}>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
|
@ -30,4 +33,4 @@ const UploadForm = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default UploadForm;
|
export default injectIntl(UploadForm);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Avatar from '../../../components/avatar';
|
||||||
import DisplayName from '../../../components/display_name';
|
import DisplayName from '../../../components/display_name';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import IconButton from '../../../components/icon_button';
|
import IconButton from '../../../components/icon_button';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
|
@ -51,7 +52,7 @@ const Account = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, me, withNote } = this.props;
|
const { account, me, withNote, intl } = this.props;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return <div />;
|
return <div />;
|
||||||
|
@ -68,7 +69,7 @@ const Account = React.createClass({
|
||||||
|
|
||||||
buttons = (
|
buttons = (
|
||||||
<div style={buttonsStyle}>
|
<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>
|
</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 { Link } from 'react-router';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
const GettingStarted = () => {
|
const GettingStarted = () => {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column>
|
||||||
<div className='static-content'>
|
<div className='static-content'>
|
||||||
<h1>Getting started</h1>
|
<h1><FormattedMessage id='getting_started.heading' defaultMessage='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><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>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_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>The developer of this project can be followed as Gargron@mastodon.social</p>
|
<p><FormattedMessage id='getting_started.about_developer' defaultMessage='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>
|
|
||||||
</div>
|
</div>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
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 { refreshTimeline } from '../../actions/timelines';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const HomeTimeline = React.createClass({
|
const HomeTimeline = React.createClass({
|
||||||
|
|
||||||
|
@ -17,8 +18,10 @@ const HomeTimeline = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column icon='home' heading='Home'>
|
<Column icon='home' heading={intl.formatMessage({ id: 'column.home', defaultMessage: 'Home' })}>
|
||||||
<StatusListContainer {...this.props} type='home' />
|
<StatusListContainer {...this.props} type='home' />
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
@ -26,4 +29,4 @@ const HomeTimeline = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect()(HomeTimeline);
|
export default connect()(injectIntl(HomeTimeline));
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
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 { refreshTimeline } from '../../actions/timelines';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const MentionsTimeline = React.createClass({
|
const MentionsTimeline = React.createClass({
|
||||||
|
|
||||||
|
@ -17,8 +18,10 @@ const MentionsTimeline = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column icon='at' heading='Mentions'>
|
<Column icon='at' heading={intl.formatMessage({ id: 'column.mentions', defaultMessage: 'Mentions' })}>
|
||||||
<StatusListContainer {...this.props} type='mentions' />
|
<StatusListContainer {...this.props} type='mentions' />
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
@ -26,4 +29,4 @@ const MentionsTimeline = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect()(MentionsTimeline);
|
export default connect()(injectIntl(MentionsTimeline));
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
updateTimeline,
|
updateTimeline,
|
||||||
deleteFromTimelines
|
deleteFromTimelines
|
||||||
} from '../../actions/timelines';
|
} from '../../actions/timelines';
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const PublicTimeline = React.createClass({
|
const PublicTimeline = React.createClass({
|
||||||
|
|
||||||
|
@ -44,8 +45,10 @@ const PublicTimeline = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column icon='globe' heading='Public'>
|
<Column icon='globe' heading={intl.formatMessage({ id: 'column.public', defaultMessage: 'Public' })}>
|
||||||
<StatusListContainer type='public' />
|
<StatusListContainer type='public' />
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
@ -53,4 +56,4 @@ const PublicTimeline = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect()(PublicTimeline);
|
export default connect()(injectIntl(PublicTimeline));
|
||||||
|
|
|
@ -2,6 +2,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import IconButton from '../../../components/icon_button';
|
import IconButton from '../../../components/icon_button';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
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({
|
const ActionBar = React.createClass({
|
||||||
|
|
||||||
|
@ -38,21 +39,21 @@ const ActionBar = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, me } = this.props;
|
const { status, me, intl } = this.props;
|
||||||
|
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
if (me === status.getIn(['account', 'id'])) {
|
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 {
|
} else {
|
||||||
menu.push({ text: 'Mention', action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ background: '#2f3441', display: 'flex', flexDirection: 'row', borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px 0' }}>
|
<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 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='Reblog' icon='retweet' onClick={this.handleReblogClick} /></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='Favourite' icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></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 style={{ flex: '1 1 auto', textAlign: 'center' }}><DropdownMenu size={18} icon='ellipsis-h' items={menu} /></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -60,4 +61,4 @@ const ActionBar = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ActionBar;
|
export default injectIntl(ActionBar);
|
||||||
|
|
|
@ -5,8 +5,8 @@ import DisplayName from '../../../components/display_name';
|
||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
import MediaGallery from '../../../components/media_gallery';
|
import MediaGallery from '../../../components/media_gallery';
|
||||||
import VideoPlayer from '../../../components/video_player';
|
import VideoPlayer from '../../../components/video_player';
|
||||||
import moment from 'moment';
|
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { FormattedDate, FormattedNumber } from 'react-intl';
|
||||||
|
|
||||||
const DetailedStatus = React.createClass({
|
const DetailedStatus = React.createClass({
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ const DetailedStatus = React.createClass({
|
||||||
{media}
|
{media}
|
||||||
|
|
||||||
<div style={{ marginTop: '15px', color: '#616b86', fontSize: '14px', lineHeight: '18px' }}>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
const outerStyle = {
|
const outerStyle = {
|
||||||
background: '#373b4a',
|
background: '#373b4a',
|
||||||
|
@ -28,10 +29,10 @@ const tabActiveStyle = {
|
||||||
const TabsBar = () => {
|
const TabsBar = () => {
|
||||||
return (
|
return (
|
||||||
<div style={outerStyle}>
|
<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='/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' /> Home</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' /> Mentions</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' /> Public</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ module HomeHelper
|
||||||
{
|
{
|
||||||
token: @token,
|
token: @token,
|
||||||
account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json),
|
account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json),
|
||||||
|
locale: I18n.locale,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
"jsdom": "^9.6.0",
|
"jsdom": "^9.6.0",
|
||||||
"mocha": "^3.1.1",
|
"mocha": "^3.1.1",
|
||||||
"moment": "^2.14.1",
|
|
||||||
"react": "^15.3.2",
|
"react": "^15.3.2",
|
||||||
"react-addons-perf": "^15.3.2",
|
"react-addons-perf": "^15.3.2",
|
||||||
"react-addons-pure-render-mixin": "^15.3.1",
|
"react-addons-pure-render-mixin": "^15.3.1",
|
||||||
|
@ -46,6 +45,7 @@
|
||||||
"http-link-header": "^0.5.0",
|
"http-link-header": "^0.5.0",
|
||||||
"react-autosuggest": "^7.0.1",
|
"react-autosuggest": "^7.0.1",
|
||||||
"react-decoration": "^1.4.0",
|
"react-decoration": "^1.4.0",
|
||||||
|
"react-intl": "^2.1.5",
|
||||||
"react-motion": "^0.4.5",
|
"react-motion": "^0.4.5",
|
||||||
"react-responsive": "^1.1.5",
|
"react-responsive": "^1.1.5",
|
||||||
"react-router-scroll": "^0.3.2"
|
"react-router-scroll": "^0.3.2"
|
||||||
|
|
39
yarn.lock
39
yarn.lock
|
@ -2497,12 +2497,38 @@ interpret@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
|
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:
|
invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@2.x.x:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54"
|
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54"
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.0.0"
|
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:
|
ipaddr.js@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230"
|
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"
|
through2 "^2.0.0"
|
||||||
xtend "^4.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:
|
ms@0.7.1:
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
||||||
|
@ -3912,6 +3934,15 @@ react-inspector@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-dom "^1.0.5"
|
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:
|
react-komposer@^1.9.0:
|
||||||
version "1.13.1"
|
version "1.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-komposer/-/react-komposer-1.13.1.tgz#4b8ac4bcc71323bd7413dcab95c831197f50eed0"
|
resolved "https://registry.yarnpkg.com/react-komposer/-/react-komposer-1.13.1.tgz#4b8ac4bcc71323bd7413dcab95c831197f50eed0"
|
||||||
|
|
Loading…
Reference in New Issue