nicolium: migrate relative timestamp component to functional
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { injectIntl, defineMessages, IntlShape, FormatDateOptions } from 'react-intl';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { defineMessages, useIntl, FormatDateOptions } from 'react-intl';
|
||||
|
||||
import Text, { IText } from './ui/text';
|
||||
|
||||
@ -78,12 +78,83 @@ const getUnitDelay = (units: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const timeAgoString = (intl: IntlShape, date: Date, now: number, year: number) => {
|
||||
interface IRelativeTimestamp extends IText {
|
||||
timestamp: string;
|
||||
year?: number;
|
||||
futureDate?: boolean;
|
||||
}
|
||||
|
||||
/** Displays a timestamp compared to the current time, eg "1m" for one minute ago. */
|
||||
const RelativeTimestamp: React.FC<IRelativeTimestamp> = ({
|
||||
timestamp,
|
||||
year = new Date().getFullYear(),
|
||||
futureDate,
|
||||
theme = 'inherit',
|
||||
...props
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [now, setNow] = useState(Date.now);
|
||||
const timerRef = useRef<NodeJS.Timeout>(undefined);
|
||||
|
||||
const scheduleNextUpdate = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
|
||||
const delta = new Date(timestamp).getTime() - 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);
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
setNow(Date.now());
|
||||
}, delay);
|
||||
}, [timestamp, now]);
|
||||
|
||||
useEffect(() => {
|
||||
setNow(Date.now());
|
||||
}, [timestamp]);
|
||||
|
||||
useEffect(() => {
|
||||
scheduleNextUpdate();
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, [scheduleNextUpdate]);
|
||||
|
||||
const date = new Date(timestamp);
|
||||
const delta = now - date.getTime();
|
||||
|
||||
let relativeTime;
|
||||
let relativeTime: string;
|
||||
if (futureDate) {
|
||||
const futureDelta = date.getTime() - now;
|
||||
|
||||
if (delta < 10 * SECOND) {
|
||||
if (futureDelta < 10 * SECOND) {
|
||||
relativeTime = intl.formatMessage(messages.momentsRemaining);
|
||||
} else if (futureDelta < MINUTE) {
|
||||
relativeTime = intl.formatMessage(messages.secondsRemaining, {
|
||||
number: Math.floor(futureDelta / SECOND),
|
||||
});
|
||||
} else if (futureDelta < HOUR) {
|
||||
relativeTime = intl.formatMessage(messages.minutesRemaining, {
|
||||
number: Math.floor(futureDelta / MINUTE),
|
||||
});
|
||||
} else if (futureDelta < DAY) {
|
||||
relativeTime = intl.formatMessage(messages.hoursRemaining, {
|
||||
number: Math.floor(futureDelta / HOUR),
|
||||
});
|
||||
} else {
|
||||
relativeTime = intl.formatMessage(messages.daysRemaining, {
|
||||
number: Math.floor(futureDelta / DAY),
|
||||
});
|
||||
}
|
||||
} else if (delta < 10 * SECOND) {
|
||||
relativeTime = intl.formatMessage(messages.justNow);
|
||||
} else if (delta < 7 * DAY) {
|
||||
if (delta < MINUTE) {
|
||||
@ -101,130 +172,11 @@ const timeAgoString = (intl: IntlShape, date: Date, now: number, year: number) =
|
||||
relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
|
||||
}
|
||||
|
||||
return relativeTime;
|
||||
return (
|
||||
<Text {...props} theme={theme} tag='time' title={intl.formatDate(date, dateFormatOptions)}>
|
||||
{relativeTime}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const timeRemainingString = (intl: IntlShape, date: Date, now: number) => {
|
||||
const delta = date.getTime() - now;
|
||||
|
||||
let relativeTime;
|
||||
|
||||
if (delta < 10 * SECOND) {
|
||||
relativeTime = intl.formatMessage(messages.momentsRemaining);
|
||||
} else if (delta < MINUTE) {
|
||||
relativeTime = intl.formatMessage(messages.secondsRemaining, {
|
||||
number: Math.floor(delta / SECOND),
|
||||
});
|
||||
} else if (delta < HOUR) {
|
||||
relativeTime = intl.formatMessage(messages.minutesRemaining, {
|
||||
number: Math.floor(delta / MINUTE),
|
||||
});
|
||||
} else if (delta < DAY) {
|
||||
relativeTime = intl.formatMessage(messages.hoursRemaining, {
|
||||
number: Math.floor(delta / HOUR),
|
||||
});
|
||||
} else {
|
||||
relativeTime = intl.formatMessage(messages.daysRemaining, { number: Math.floor(delta / DAY) });
|
||||
}
|
||||
|
||||
return relativeTime;
|
||||
};
|
||||
|
||||
interface RelativeTimestampProps extends IText {
|
||||
intl: IntlShape;
|
||||
timestamp: string;
|
||||
year?: number;
|
||||
futureDate?: boolean;
|
||||
}
|
||||
|
||||
interface RelativeTimestampState {
|
||||
now: number;
|
||||
}
|
||||
|
||||
/** Displays a timestamp compared to the current time, eg "1m" for one minute ago. */
|
||||
const RelativeTimestamp = injectIntl(
|
||||
class RelativeTimestamp extends React.Component<RelativeTimestampProps, RelativeTimestampState> {
|
||||
_timer: NodeJS.Timeout | undefined;
|
||||
|
||||
state = {
|
||||
now: Date.now(),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
year: new Date().getFullYear(),
|
||||
theme: 'inherit' as const,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: RelativeTimestampProps, nextState: RelativeTimestampState) {
|
||||
// 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.state.now !== nextState.now
|
||||
);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(prevProps: RelativeTimestampProps) {
|
||||
if (this.props.timestamp !== prevProps.timestamp) {
|
||||
this.setState({ now: Date.now() });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._scheduleNextUpdate();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillUpdate() {
|
||||
this._scheduleNextUpdate();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
}
|
||||
|
||||
_scheduleNextUpdate() {
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
|
||||
const { timestamp } = this.props;
|
||||
const delta = new Date(timestamp).getTime() - this.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: Date.now() });
|
||||
}, delay);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { timestamp, intl, year, futureDate, theme, ...textProps } = this.props;
|
||||
|
||||
const date = new Date(timestamp);
|
||||
const relativeTime = futureDate
|
||||
? timeRemainingString(intl, date, this.state.now)
|
||||
: timeAgoString(intl, date, this.state.now, year!);
|
||||
|
||||
return (
|
||||
<Text
|
||||
{...textProps}
|
||||
theme={theme}
|
||||
tag='time'
|
||||
title={intl.formatDate(date, dateFormatOptions)}
|
||||
>
|
||||
{relativeTime}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export { dateFormatOptions, RelativeTimestamp as default };
|
||||
|
||||
Reference in New Issue
Block a user