-
Notifications
You must be signed in to change notification settings - Fork 0
Timeago component refactor #182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
process.env.TZ = 'UTC'; | ||
|
||
module.exports = { | ||
rootDir: '..', | ||
moduleNameMapper: { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,37 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Renders Timeago component 1`] = ` | ||
<span | ||
title={1582205173526} | ||
exports[`Timeago now should render custom string 1`] = ` | ||
<time | ||
dateTime="2018-09-17T12:58:43.000Z" | ||
title="2018-09-17T12:58:43.000Z" | ||
> | ||
2/20/2020 | ||
</span> | ||
customMessage | ||
</time> | ||
`; | ||
|
||
exports[`Timeago now should render custom string 2`] = ` | ||
<time | ||
dateTime="2018-09-17T12:58:43.000Z" | ||
title="2018-09-17T12:58:43.000Z" | ||
> | ||
customMessage | ||
</time> | ||
`; | ||
|
||
exports[`Timeago now should render default string 1`] = ` | ||
<time | ||
dateTime="2018-09-17T12:58:43.000Z" | ||
title="2018-09-17T12:58:43.000Z" | ||
> | ||
now | ||
</time> | ||
`; | ||
|
||
exports[`Timeago now should render default string 2`] = ` | ||
<time | ||
dateTime="2018-09-17T12:58:43.000Z" | ||
title="2018-09-17T12:58:43.000Z" | ||
> | ||
now | ||
</time> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,81 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
/* istanbul ignore next */ | ||
function getTimeDistanceString(datetime) { | ||
const date = new Date(parseInt(datetime, 10)); | ||
const now = Date.now(); | ||
const diffInSeconds = (now - date) / 1000; | ||
export const SECOND_MILLISECONDS = 1000; | ||
export const MINUTE_SECONDS = 60; | ||
export const HOUR_SECONDS = 60 * MINUTE_SECONDS; | ||
export const DAY_SECONDS = 24 * HOUR_SECONDS; | ||
export const FIVE_DAYS_SECONDS = 5 * DAY_SECONDS; | ||
|
||
if (diffInSeconds > 432000) { | ||
// more than 5 days ago - show date | ||
return date.toLocaleDateString(); | ||
} | ||
|
||
if (diffInSeconds > 86400) { | ||
// more than a day ago | ||
return `${Math.round(diffInSeconds / 60 / 60 / 24)}d`; | ||
} | ||
|
||
if (diffInSeconds > 3600) { | ||
// more than an hour ago | ||
return `${Math.round(diffInSeconds / 60 / 60)}h`; | ||
} | ||
|
||
if (diffInSeconds < 60) { | ||
// less than a minute ago | ||
return 'now'; | ||
} | ||
|
||
return `${Math.round(diffInSeconds / 60)}m`; | ||
} | ||
export const TIMEAGO_SHOW_DATE_THRESHOLD = FIVE_DAYS_SECONDS; | ||
export const TIMEAGO_NOW_THRESHOLD = MINUTE_SECONDS; | ||
|
||
/** | ||
* The Timeago component is a small component that | ||
* shows the number of seconds/minutes/days from given datetime. | ||
* | ||
* It all happens after the component is mounted so it's safe to use this | ||
* component on the Back-End without messing up the hydration. | ||
*/ | ||
export default class Timeago extends React.Component { | ||
state = { | ||
display: this.props.datetime, | ||
}; | ||
export default function Timeago({ | ||
datetime, renderNow, renderDate, renderDays, renderHours, renderMinutes, | ||
}) { | ||
const date = new Date(datetime); | ||
|
||
static propTypes = { | ||
datetime: PropTypes.oneOfType([ | ||
PropTypes.instanceOf(Date), | ||
PropTypes.string, | ||
]).isRequired, | ||
}; | ||
const withCustomRender = (renderFunction, value, defaultElement = value) => (typeof (renderFunction) === 'function' ? renderFunction(value) : defaultElement); | ||
|
||
const renderTime = () => { | ||
const now = Date.now(); | ||
const diffInSeconds = (now - date.getTime()) / SECOND_MILLISECONDS; | ||
|
||
componentDidMount() { | ||
const { datetime } = this.props; | ||
if (diffInSeconds > TIMEAGO_SHOW_DATE_THRESHOLD) { | ||
const dateString = date.toLocaleDateString(); | ||
|
||
this.setState({ | ||
display: getTimeDistanceString(datetime), | ||
}); | ||
} | ||
return withCustomRender(renderDate, dateString); | ||
} | ||
|
||
render() { | ||
const { datetime } = this.props; | ||
const { display } = this.state; | ||
if (diffInSeconds > DAY_SECONDS) { | ||
// more than a day ago | ||
const days = Math.round(diffInSeconds / DAY_SECONDS); | ||
return withCustomRender(renderDays, days, `${days}d`); | ||
} | ||
|
||
return ( | ||
<span title={datetime}> | ||
{display} | ||
</span> | ||
); | ||
} | ||
if (diffInSeconds > HOUR_SECONDS) { | ||
// more than an hour ago | ||
const hours = Math.round(diffInSeconds / HOUR_SECONDS); | ||
return withCustomRender(renderHours, hours, `${hours}h`); | ||
} | ||
|
||
if (diffInSeconds > TIMEAGO_NOW_THRESHOLD) { | ||
const minutes = Math.round(diffInSeconds / MINUTE_SECONDS); | ||
return withCustomRender(renderMinutes, minutes, `${minutes}m`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this string (the default ones) be translated maybe? Just a guess. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think react-common doesn't handle translations. That's why I thought we need those custom renders to provide translations in specific apps. With this, we can create a wrapper for Timeago for example in Feeds which will handle translations there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some components of |
||
} | ||
|
||
// less than a minute ago | ||
return typeof (renderNow) === 'function' ? renderNow() : 'now'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be translated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
}; | ||
|
||
|
||
return ( | ||
<time dateTime={date.toLocaleString()} title={date.toLocaleString()}> | ||
{renderTime()} | ||
</time> | ||
); | ||
} | ||
|
||
Timeago.propTypes = { | ||
datetime: PropTypes.oneOfType([ | ||
PropTypes.instanceOf(Date), | ||
PropTypes.string, | ||
]).isRequired, | ||
renderDate: PropTypes.func, | ||
renderDays: PropTypes.func, | ||
renderHours: PropTypes.func, | ||
renderMinutes: PropTypes.func, | ||
renderNow: PropTypes.func, | ||
}; | ||
|
||
Timeago.defaultProps = { | ||
renderDays: null, | ||
renderHours: null, | ||
renderMinutes: null, | ||
renderDate: null, | ||
renderNow: null, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can
render*
function return JSX/JS string? Ideally Id like to userenderNow={() => <strong>NOW!</string>}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can return anything because it will just call your
render*
function and return its result. Check this fragment out:return typeof (renderNow) === 'function' ? renderNow() : 'now';
So sure, you can use it like you did it above :)