Skip to content

Commit

Permalink
Merge pull request #16968 from Expensify/AutoUpdateTimeHook
Browse files Browse the repository at this point in the history
Migrate AutoUpdateTime to functional component
  • Loading branch information
marcaaron authored Apr 11, 2023
2 parents d74cd27 + 7fa46af commit 99292fc
Showing 1 changed file with 49 additions and 68 deletions.
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);

0 comments on commit 99292fc

Please sign in to comment.