Skip to content
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

Migrate AutoUpdateTime to functional component #16968

Merged
merged 14 commits into from
Apr 11, 2023
Merged
117 changes: 49 additions & 68 deletions src/components/AutoUpdateTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
* The time auto-update logic is extracted to this component to avoid re-rendering a more complex component, e.g. DetailsPage.
*/
import {View} from 'react-native';
import React, {PureComponent} from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import DateUtils from '../libs/DateUtils';
Expand All @@ -22,85 +28,60 @@ const propTypes = {
...withLocalizePropTypes,
};

class AutoUpdateTime extends PureComponent {
constructor(props) {
super(props);
this.getCurrentUserLocalTime = this.getCurrentUserLocalTime.bind(this);
this.updateCurrentTime = this.updateCurrentTime.bind(this);
this.getTimezoneName = this.getTimezoneName.bind(this);
this.state = {
currentUserLocalTime: this.getCurrentUserLocalTime(),
};
}

componentDidMount() {
this.updateCurrentTime();
}

componentDidUpdate() {
// Make sure the interval is up to date every time the component updates
this.updateCurrentTime();
}

componentWillUnmount() {
clearTimeout(this.timer);
}

function AutoUpdateTime(props) {
/**
* @returns {moment} Returns the locale moment object
*/
getCurrentUserLocalTime() {
return DateUtils.getLocalMomentFromDatetime(
this.props.preferredLocale,
const getCurrentUserLocalTime = useCallback(() => (
DateUtils.getLocalMomentFromDatetime(
props.preferredLocale,
null,
this.props.timezone.selected,
);
}
props.timezone.selected,
)
), [props.preferredLocale, props.timezone.selected]);

/**
* @returns {string} Returns the timezone name in string, e.g.: GMT +07
*/
getTimezoneName() {
const [currentUserLocalTime, setCurrentUserLocalTime] = useState(getCurrentUserLocalTime);
const minuteRef = useRef(new Date().getMinutes());
const timezoneName = useMemo(() => {
// With non-GMT timezone, moment.zoneAbbr() will return the name of that timezone, so we can use it directly.
if (Number.isNaN(Number(this.state.currentUserLocalTime.zoneAbbr()))) {
return this.state.currentUserLocalTime.zoneAbbr();
if (Number.isNaN(Number(currentUserLocalTime.zoneAbbr()))) {
return currentUserLocalTime.zoneAbbr();
}

// With GMT timezone, moment.zoneAbbr() will return a number, so we need to display it as GMT {abbreviations} format, e.g.: GMT +07
return `GMT ${this.state.currentUserLocalTime.zoneAbbr()}`;
}
return `GMT ${currentUserLocalTime.zoneAbbr()}`;
}, [currentUserLocalTime]);

/**
* Update the user's local time at the top of every minute
*/
updateCurrentTime() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
const millisecondsUntilNextMinute = (60 - this.state.currentUserLocalTime.seconds()) * 1000;
this.timer = setTimeout(() => {
this.setState({
currentUserLocalTime: this.getCurrentUserLocalTime(),
});
}, millisecondsUntilNextMinute);
}
useEffect(() => {
// If the any of the props that getCurrentUserLocalTime depends on change, we want to update the displayed time immediately
setCurrentUserLocalTime(getCurrentUserLocalTime());

// Also, if the user leaves this page open, we want to make sure the displayed time is updated every minute when the clock changes
// To do this we create an interval to check if the minute has changed every second and update the displayed time if it has
const interval = setInterval(() => {
const currentMinute = new Date().getMinutes();
if (currentMinute !== minuteRef.current) {
setCurrentUserLocalTime(getCurrentUserLocalTime());
minuteRef.current = currentMinute;
}
}, 1000);
return () => clearInterval(interval);
}, [getCurrentUserLocalTime]);

render() {
return (
<View style={[styles.mb6, styles.detailsPageSectionContainer]}>
<Text style={[styles.textLabelSupporting, styles.mb1]} numberOfLines={1}>
{this.props.translate('detailsPage.localTime')}
</Text>
<Text numberOfLines={1}>
{this.state.currentUserLocalTime.format('LT')}
{' '}
{this.getTimezoneName()}
</Text>
</View>
);
}
return (
<View style={[styles.mb6, styles.detailsPageSectionContainer]}>
<Text style={[styles.textLabelSupporting, styles.mb1]} numberOfLines={1}>
{props.translate('detailsPage.localTime')}
</Text>
<Text numberOfLines={1}>
{currentUserLocalTime.format('LT')}
{' '}
{timezoneName}
</Text>
</View>
);
}

AutoUpdateTime.propTypes = propTypes;
AutoUpdateTime.displayName = 'AutoUpdateTime';
export default withLocalize(AutoUpdateTime);