commit: 632178d7543f48f493a63afce0d3c6243aac5fae
parent: 291feba6f113588cce4f06206754b31eba60044b
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Wed, 4 Oct 2017 01:23:52 +0200
Use own, shorter relative timestamps (#5171)
* Use own, shorter relative timestamps
* Add acct to title tooltip of display name in statuses
* Improve i18n of the relative times
Diffstat:
2 files changed, 109 insertions(+), 6 deletions(-)
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
@@ -1,7 +1,15 @@
import React from 'react';
-import { injectIntl, FormattedRelative } from 'react-intl';
+import { injectIntl, defineMessages } from 'react-intl';
import PropTypes from 'prop-types';
+const messages = defineMessages({
+ just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
+ seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
+ minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
+ hours: { id: 'relative_time.hours', defaultMessage: '{number}h' },
+ days: { id: 'relative_time.days', defaultMessage: '{number}d' },
+});
+
const dateFormatOptions = {
hour12: false,
year: 'numeric',
@@ -11,6 +19,47 @@ const dateFormatOptions = {
minute: '2-digit',
};
+const shortDateFormatOptions = {
+ month: 'numeric',
+ day: 'numeric',
+};
+
+const SECOND = 1000;
+const MINUTE = 1000 * 60;
+const HOUR = 1000 * 60 * 60;
+const DAY = 1000 * 60 * 60 * 24;
+
+const MAX_DELAY = 2147483647;
+
+const selectUnits = delta => {
+ const absDelta = Math.abs(delta);
+
+ if (absDelta < MINUTE) {
+ return 'second';
+ } else if (absDelta < HOUR) {
+ return 'minute';
+ } else if (absDelta < DAY) {
+ return 'hour';
+ }
+
+ return 'day';
+};
+
+const getUnitDelay = units => {
+ switch (units) {
+ case 'second':
+ return SECOND;
+ case 'minute':
+ return MINUTE;
+ case 'hour':
+ return HOUR;
+ case 'day':
+ return DAY;
+ default:
+ return MAX_DELAY;
+ }
+};
+
@injectIntl
export default class RelativeTimestamp extends React.Component {
@@ -19,20 +68,74 @@ export default class RelativeTimestamp extends React.Component {
timestamp: PropTypes.string.isRequired,
};
- shouldComponentUpdate (nextProps) {
+ state = {
+ now: this.props.intl.now(),
+ };
+
+ shouldComponentUpdate (nextProps, nextState) {
// As of right now the locale doesn't change without a new page load,
// but we might as well check in case that ever changes.
return this.props.timestamp !== nextProps.timestamp ||
- this.props.intl.locale !== nextProps.intl.locale;
+ this.props.intl.locale !== nextProps.intl.locale ||
+ this.state.now !== nextState.now;
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (this.props.timestamp !== nextProps.timestamp) {
+ this.setState({ now: this.props.intl.now() });
+ }
+ }
+
+ componentDidMount () {
+ this._scheduleNextUpdate(this.props, this.state);
+ }
+
+ componentWillUpdate (nextProps, nextState) {
+ this._scheduleNextUpdate(nextProps, nextState);
+ }
+
+ _scheduleNextUpdate (props, state) {
+ clearTimeout(this._timer);
+
+ const { timestamp } = props;
+ const delta = (new Date(timestamp)).getTime() - state.now;
+ const unitDelay = getUnitDelay(selectUnits(delta));
+ const unitRemainder = Math.abs(delta % unitDelay);
+ const updateInterval = 1000 * 10;
+ const delay = delta < 0 ? Math.max(updateInterval, unitDelay - unitRemainder) : Math.max(updateInterval, unitRemainder);
+
+ this._timer = setTimeout(() => {
+ this.setState({ now: this.props.intl.now() });
+ }, delay);
}
render () {
const { timestamp, intl } = this.props;
- const date = new Date(timestamp);
+
+ const date = new Date(timestamp);
+ const delta = this.state.now - date.getTime();
+
+ let relativeTime;
+
+ if (delta < 10 * SECOND) {
+ relativeTime = intl.formatMessage(messages.just_now);
+ } else if (delta < 3 * DAY) {
+ if (delta < MINUTE) {
+ relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
+ } else if (delta < HOUR) {
+ relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
+ } else if (delta < DAY) {
+ relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
+ } else {
+ relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
+ }
+ } else {
+ relativeTime = intl.formatDate(date, shortDateFormatOptions);
+ }
return (
<time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
- <FormattedRelative value={date} />
+ {relativeTime}
</time>
);
}
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
@@ -165,7 +165,7 @@ export default class Status extends ImmutablePureComponent {
<div className='status__info'>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
- <a onClick={this.handleAccountClick} target='_blank' data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name'>
+ <a onClick={this.handleAccountClick} target='_blank' data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name'>
<div className='status__avatar'>
{statusAvatar}
</div>