MM-21961 Fix Incorrect Timestamp on Android Mobile App (#3861)

This commit is contained in:
Elias Nahum
2020-01-27 16:57:22 -03:00
committed by Miguel Alatzar
parent d25561bb83
commit ff13f3e612
5 changed files with 49 additions and 62 deletions

View File

@@ -4,19 +4,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Text} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
import moment from 'moment-timezone';
import CustomPropTypes from 'app/constants/custom_prop_types';
class FormattedTime extends React.PureComponent {
export default class FormattedTime extends React.PureComponent {
static propTypes = {
value: PropTypes.any.isRequired,
timeZone: PropTypes.string,
children: PropTypes.func,
hour12: PropTypes.bool,
style: CustomPropTypes.Style,
intl: intlShape.isRequired,
};
getFormattedTime = () => {
@@ -24,23 +22,14 @@ class FormattedTime extends React.PureComponent {
value,
timeZone,
hour12,
intl,
} = this.props;
const timezoneProps = timeZone ? {timeZone} : {};
const options = {
...timezoneProps,
hour12,
};
const formattedTime = intl.formatTime(value, options);
// `formatTime` returns unformatted date string on error like in the case of (react-intl) unsupported timezone.
// Therefore, use react-intl by default or moment-timezone for unsupported timezone.
if (formattedTime !== String(new Date(value))) {
return formattedTime;
let format = 'H:mm';
if (hour12) {
const localeFormat = moment.localeData().longDateFormat('LT');
format = localeFormat?.includes('A') ? localeFormat : 'h:mm A';
}
const format = hour12 ? 'hh:mm A' : 'HH:mm';
if (timeZone) {
return moment.tz(value, timeZone).format(format);
}
@@ -59,5 +48,3 @@ class FormattedTime extends React.PureComponent {
return <Text style={style}>{formattedTime}</Text>;
}
}
export default injectIntl(FormattedTime);

View File

@@ -4,9 +4,7 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {IntlProvider} from 'react-intl';
import IntlPolyfill from 'intl';
import 'intl/locale-data/jsonp/es';
import 'intl/locale-data/jsonp/ko';
import moment from 'moment-timezone';
import FormattedTime from './formatted_time';
@@ -17,8 +15,6 @@ describe('FormattedTime', () => {
hour12: true,
};
setupTest();
it('should render correctly', () => {
console.error = jest.fn();
@@ -40,13 +36,15 @@ describe('FormattedTime', () => {
});
it('should support localization', () => {
moment.locale('es');
let wrapper = renderWithIntl(
<FormattedTime {...baseProps}/>,
'es',
);
expect(wrapper.getByText('7:02 p. m.')).toBeTruthy();
expect(wrapper.getByText('7:02 PM')).toBeTruthy();
moment.locale('ko');
wrapper = renderWithIntl(
<FormattedTime {...baseProps}/>,
'ko',
@@ -66,6 +64,7 @@ describe('FormattedTime', () => {
});
it('should fallback to default short format for unsupported locale of react-intl ', () => {
moment.locale('es');
let wrapper = renderWithIntl(
<FormattedTime
{...baseProps}
@@ -74,7 +73,7 @@ describe('FormattedTime', () => {
'es',
);
expect(wrapper.getByText('08:47 AM')).toBeTruthy();
expect(wrapper.getByText('8:47 AM')).toBeTruthy();
wrapper = renderWithIntl(
<FormattedTime
@@ -82,16 +81,13 @@ describe('FormattedTime', () => {
timeZone='NZ-CHAT'
hour12={false}
/>,
'es',
);
expect(wrapper.getByText('08:47')).toBeTruthy();
expect(wrapper.getByText('8:47')).toBeTruthy();
});
});
function renderWithIntl(component, locale = 'en') {
return render(<IntlProvider locale={locale}>{component}</IntlProvider>);
}
function setupTest() {
global.Intl = IntlPolyfill;
}

View File

@@ -51,7 +51,7 @@ exports[`PostHeader should match snapshot when just a base post 1`] = `
John Smith
</Text>
</TouchableWithFeedbackIOS>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -123,7 +123,7 @@ exports[`PostHeader should match snapshot when just a base post in landscape mod
John Smith
</Text>
</TouchableWithFeedbackIOS>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -232,7 +232,7 @@ exports[`PostHeader should match snapshot when post is autoresponder 1`] = `
}
}
/>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -297,7 +297,7 @@ exports[`PostHeader should match snapshot when post is from system message 1`] =
}
/>
</View>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -367,7 +367,7 @@ exports[`PostHeader should match snapshot when post is same thread, so dont disp
John Smith
</Text>
</TouchableWithFeedbackIOS>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -494,7 +494,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
}
}
/>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -646,7 +646,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
}
}
/>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -758,7 +758,7 @@ exports[`PostHeader should match snapshot when post renders Commented On for new
John Smith
</Text>
</TouchableWithFeedbackIOS>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {
@@ -845,7 +845,7 @@ exports[`PostHeader should match snapshot when post should display reply button
John Smith
</Text>
</TouchableWithFeedbackIOS>
<InjectIntl(FormattedTime)
<FormattedTime
hour12={true}
style={
Object {

View File

@@ -103,6 +103,7 @@ export default class Root extends PureComponent {
return (
<IntlProvider
key={locale}
ref={this.setProviderRef}
locale={locale}
messages={getTranslations(locale)}

View File

@@ -8,92 +8,95 @@ import moment from 'moment';
import en from 'assets/i18n/en.json';
const TRANSLATIONS = {en};
export const DEFAULT_LOCALE = 'en';
addLocaleData(enLocaleData);
function loadTranslation(locale) {
try {
let translations;
let localeData;
let momentData;
switch (locale) {
case 'de':
TRANSLATIONS.de = require('assets/i18n/de.json');
translations = require('assets/i18n/de.json');
localeData = require('react-intl/locale-data/de');
momentData = require('moment/locale/de');
break;
case 'es':
TRANSLATIONS.es = require('assets/i18n/es.json');
translations = require('assets/i18n/es.json');
localeData = require('react-intl/locale-data/es');
momentData = require('moment/locale/es');
break;
case 'fr':
TRANSLATIONS.fr = require('assets/i18n/fr.json');
translations = require('assets/i18n/fr.json');
localeData = require('react-intl/locale-data/fr');
momentData = require('moment/locale/fr');
break;
case 'it':
TRANSLATIONS.it = require('assets/i18n/it.json');
translations = require('assets/i18n/it.json');
localeData = require('react-intl/locale-data/it');
momentData = require('moment/locale/it');
break;
case 'ja':
TRANSLATIONS.ja = require('assets/i18n/ja.json');
translations = require('assets/i18n/ja.json');
localeData = require('react-intl/locale-data/ja');
momentData = require('moment/locale/ja');
break;
case 'ko':
TRANSLATIONS.ko = require('assets/i18n/ko.json');
translations = require('assets/i18n/ko.json');
localeData = require('react-intl/locale-data/ko');
momentData = require('moment/locale/ko');
break;
case 'nl':
TRANSLATIONS.nl = require('assets/i18n/nl.json');
translations = require('assets/i18n/nl.json');
localeData = require('react-intl/locale-data/nl');
momentData = require('moment/locale/nl');
break;
case 'pl':
TRANSLATIONS.pl = require('assets/i18n/pl.json');
translations = require('assets/i18n/pl.json');
localeData = require('react-intl/locale-data/pl');
momentData = require('moment/locale/pl');
break;
case 'pt-BR':
TRANSLATIONS[locale] = require('assets/i18n/pt-BR.json');
translations = require('assets/i18n/pt-BR.json');
localeData = require('react-intl/locale-data/pt');
momentData = require('moment/locale/pt-br');
break;
case 'ro':
TRANSLATIONS.ro = require('assets/i18n/ro.json');
translations = require('assets/i18n/ro.json');
localeData = require('react-intl/locale-data/ro');
momentData = require('moment/locale/ro');
break;
case 'ru':
TRANSLATIONS.ru = require('assets/i18n/ru.json');
translations = require('assets/i18n/ru.json');
localeData = require('react-intl/locale-data/ru');
momentData = require('moment/locale/ru');
break;
case 'tr':
TRANSLATIONS.tr = require('assets/i18n/tr.json');
translations = require('assets/i18n/tr.json');
localeData = require('react-intl/locale-data/tr');
momentData = require('moment/locale/tr');
break;
case 'uk':
TRANSLATIONS.tr = require('assets/i18n/uk.json');
translations = require('assets/i18n/uk.json');
localeData = require('react-intl/locale-data/uk');
momentData = require('moment/locale/uk');
break;
case 'zh-CN':
TRANSLATIONS[locale] = require('assets/i18n/zh-CN.json');
translations = require('assets/i18n/zh-CN.json');
localeData = require('react-intl/locale-data/zh');
momentData = require('moment/locale/zh-cn');
break;
case 'zh-TW':
TRANSLATIONS[locale] = require('assets/i18n/zh-TW.json');
translations = require('assets/i18n/zh-TW.json');
localeData = require('react-intl/locale-data/zh');
momentData = require('moment/locale/zh-tw');
break;
default:
translations = en;
localeData = enLocaleData;
break;
}
if (localeData) {
@@ -102,9 +105,13 @@ function loadTranslation(locale) {
if (momentData) {
moment.updateLocale(locale.toLowerCase(), momentData);
} else {
resetMomentLocale();
}
return translations;
} catch (e) {
console.error('NO Translation found', e); //eslint-disable-line no-console
return en;
}
}
@@ -113,15 +120,11 @@ export function resetMomentLocale() {
}
export function getTranslations(locale) {
if (!TRANSLATIONS[locale]) {
loadTranslation(locale);
}
return TRANSLATIONS[locale] || TRANSLATIONS[DEFAULT_LOCALE];
return loadTranslation(locale);
}
export function getLocalizedMessage(locale, id) {
const translations = getTranslations(locale);
return translations[id] || TRANSLATIONS[DEFAULT_LOCALE][id];
return translations[id];
}