]> cat aescling's git repositories - mastodon.git/blob - app/javascript/flavours/glitch/components/relative_timestamp.js
Merge branch 'master' into glitch-soc/merge-upstream
[mastodon.git] / app / javascript / flavours / glitch / components / relative_timestamp.js
1 import React from 'react';
2 import { injectIntl, defineMessages } from 'react-intl';
3 import PropTypes from 'prop-types';
4
5 const messages = defineMessages({
6 just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
7 seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
8 minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
9 hours: { id: 'relative_time.hours', defaultMessage: '{number}h' },
10 days: { id: 'relative_time.days', defaultMessage: '{number}d' },
11 });
12
13 const dateFormatOptions = {
14 hour12: false,
15 year: 'numeric',
16 month: 'short',
17 day: '2-digit',
18 hour: '2-digit',
19 minute: '2-digit',
20 };
21
22 const shortDateFormatOptions = {
23 month: 'short',
24 day: 'numeric',
25 };
26
27 const SECOND = 1000;
28 const MINUTE = 1000 * 60;
29 const HOUR = 1000 * 60 * 60;
30 const DAY = 1000 * 60 * 60 * 24;
31
32 const MAX_DELAY = 2147483647;
33
34 const selectUnits = delta => {
35 const absDelta = Math.abs(delta);
36
37 if (absDelta < MINUTE) {
38 return 'second';
39 } else if (absDelta < HOUR) {
40 return 'minute';
41 } else if (absDelta < DAY) {
42 return 'hour';
43 }
44
45 return 'day';
46 };
47
48 const getUnitDelay = units => {
49 switch (units) {
50 case 'second':
51 return SECOND;
52 case 'minute':
53 return MINUTE;
54 case 'hour':
55 return HOUR;
56 case 'day':
57 return DAY;
58 default:
59 return MAX_DELAY;
60 }
61 };
62
63 export const timeAgoString = (intl, date, now, year) => {
64 const delta = now - date.getTime();
65
66 let relativeTime;
67
68 if (delta < 10 * SECOND) {
69 relativeTime = intl.formatMessage(messages.just_now);
70 } else if (delta < 7 * DAY) {
71 if (delta < MINUTE) {
72 relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
73 } else if (delta < HOUR) {
74 relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
75 } else if (delta < DAY) {
76 relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
77 } else {
78 relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
79 }
80 } else if (date.getFullYear() === year) {
81 relativeTime = intl.formatDate(date, shortDateFormatOptions);
82 } else {
83 relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
84 }
85
86 return relativeTime;
87 };
88
89 @injectIntl
90 export default class RelativeTimestamp extends React.Component {
91
92 static propTypes = {
93 intl: PropTypes.object.isRequired,
94 timestamp: PropTypes.string.isRequired,
95 year: PropTypes.number.isRequired,
96 };
97
98 state = {
99 now: this.props.intl.now(),
100 };
101
102 static defaultProps = {
103 year: (new Date()).getFullYear(),
104 };
105
106 shouldComponentUpdate (nextProps, nextState) {
107 // As of right now the locale doesn't change without a new page load,
108 // but we might as well check in case that ever changes.
109 return this.props.timestamp !== nextProps.timestamp ||
110 this.props.intl.locale !== nextProps.intl.locale ||
111 this.state.now !== nextState.now;
112 }
113
114 componentWillReceiveProps (nextProps) {
115 if (this.props.timestamp !== nextProps.timestamp) {
116 this.setState({ now: this.props.intl.now() });
117 }
118 }
119
120 componentDidMount () {
121 this._scheduleNextUpdate(this.props, this.state);
122 }
123
124 componentWillUpdate (nextProps, nextState) {
125 this._scheduleNextUpdate(nextProps, nextState);
126 }
127
128 componentWillUnmount () {
129 clearTimeout(this._timer);
130 }
131
132 _scheduleNextUpdate (props, state) {
133 clearTimeout(this._timer);
134
135 const { timestamp } = props;
136 const delta = (new Date(timestamp)).getTime() - state.now;
137 const unitDelay = getUnitDelay(selectUnits(delta));
138 const unitRemainder = Math.abs(delta % unitDelay);
139 const updateInterval = 1000 * 10;
140 const delay = delta < 0 ? Math.max(updateInterval, unitDelay - unitRemainder) : Math.max(updateInterval, unitRemainder);
141
142 this._timer = setTimeout(() => {
143 this.setState({ now: this.props.intl.now() });
144 }, delay);
145 }
146
147 render () {
148 const { timestamp, intl, year } = this.props;
149
150 const date = new Date(timestamp);
151 const relativeTime = timeAgoString(intl, date, this.state.now, year);
152
153 return (
154 <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
155 {relativeTime}
156 </time>
157 );
158 }
159
160 }
This page took 0.09811 seconds and 4 git commands to generate.