forked from Ivasoft/mattermost-mobile
MM-21961 Fix Incorrect Timestamp on Android Mobile App (#3861)
This commit is contained in:
committed by
Miguel Alatzar
parent
d25561bb83
commit
ff13f3e612
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -103,6 +103,7 @@ export default class Root extends PureComponent {
|
||||
|
||||
return (
|
||||
<IntlProvider
|
||||
key={locale}
|
||||
ref={this.setProviderRef}
|
||||
locale={locale}
|
||||
messages={getTranslations(locale)}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user