123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- import React from 'react';
- import isNumber from 'lodash/isNumber';
- import isString from 'lodash/isString';
- import moment from 'moment-timezone';
- import {t} from 'app/locale';
- import ConfigStore from 'app/stores/configStore';
- import {getDuration} from 'app/utils/formatters';
- import getDynamicText from 'app/utils/getDynamicText';
- import Tooltip from './tooltip';
- const ONE_MINUTE_IN_MS = 60000;
- type RelaxedDateType = string | number | Date;
- type DefaultProps = {
- /**
- * Suffix after elapsed time
- * e.g. "ago" in "5 minutes ago"
- */
- suffix: string;
- };
- type TimeProps = React.HTMLProps<HTMLTimeElement>;
- type Props = DefaultProps & {
- /**
- * The date value, can be string, number (e.g. timestamp), or instance of Date
- */
- date: RelaxedDateType;
- /**
- * By default we show tooltip with absolute date on hover, this prop disables that
- */
- disabledAbsoluteTooltip?: boolean;
- /**
- * For relative time shortens minutes to min, hour to hr etc.
- */
- shorten?: boolean;
- /**
- * Shortens the shortened relative time
- * min to m, hr to h
- */
- extraShort?: boolean;
- tooltipTitle?: React.ReactNode;
- className?: string;
- } & TimeProps;
- type State = {
- relative: string;
- };
- class TimeSince extends React.PureComponent<Props, State> {
- static defaultProps: DefaultProps = {
- suffix: 'ago',
- };
- state = {
- relative: '',
- };
- // TODO(ts) TODO(emotion): defining the props type breaks emotion's typings
- // See: https://github.com/emotion-js/emotion/pull/1514
- static getDerivedStateFromProps(props) {
- return {
- relative: getRelativeDate(
- props.date,
- props.suffix,
- props.shorten,
- props.extraShort
- ),
- };
- }
- componentDidMount() {
- this.setRelativeDateTicker();
- }
- componentWillUnmount() {
- if (this.ticker) {
- window.clearTimeout(this.ticker);
- this.ticker = null;
- }
- }
- ticker: number | null = null;
- setRelativeDateTicker = () => {
- this.ticker = window.setTimeout(() => {
- this.setState({
- relative: getRelativeDate(
- this.props.date,
- this.props.suffix,
- this.props.shorten,
- this.props.extraShort
- ),
- });
- this.setRelativeDateTicker();
- }, ONE_MINUTE_IN_MS);
- };
- render() {
- const {
- date,
- suffix: _suffix,
- disabledAbsoluteTooltip,
- className,
- tooltipTitle,
- shorten: _shorten,
- extraShort: _extraShort,
- ...props
- } = this.props;
- const dateObj = getDateObj(date);
- const user = ConfigStore.get('user');
- const options = user ? user.options : null;
- const format = options?.clock24Hours ? 'MMMM D, YYYY HH:mm z' : 'LLL z';
- const tooltip = getDynamicText({
- fixed: options?.clock24Hours
- ? 'November 3, 2020 08:57 UTC'
- : 'November 3, 2020 8:58 AM UTC',
- value: moment.tz(dateObj, options?.timezone ?? '').format(format),
- });
- return (
- <Tooltip
- disabled={disabledAbsoluteTooltip}
- title={
- <div>
- <div>{tooltipTitle}</div>
- {tooltip}
- </div>
- }
- >
- <time dateTime={dateObj.toISOString()} className={className} {...props}>
- {this.state.relative}
- </time>
- </Tooltip>
- );
- }
- }
- export default TimeSince;
- function getDateObj(date: RelaxedDateType): Date {
- if (isString(date) || isNumber(date)) {
- date = new Date(date);
- }
- return date;
- }
- export function getRelativeDate(
- currentDateTime: RelaxedDateType,
- suffix?: string,
- shorten?: boolean,
- extraShort?: boolean
- ): string {
- const date = getDateObj(currentDateTime);
- if ((shorten || extraShort) && suffix) {
- return t('%(time)s %(suffix)s', {
- time: getDuration(moment().diff(moment(date), 'seconds'), 0, shorten, extraShort),
- suffix,
- });
- } else if ((shorten || extraShort) && !suffix) {
- return getDuration(moment().diff(moment(date), 'seconds'), 0, shorten, extraShort);
- } else if (!suffix) {
- return moment(date).fromNow(true);
- } else if (suffix === 'ago') {
- return moment(date).fromNow();
- } else if (suffix === 'old') {
- return t('%(time)s old', {time: moment(date).fromNow(true)});
- } else {
- throw new Error('Unsupported time format suffix');
- }
- }
|