diff --git a/app/components/formatted_date.js b/app/components/formatted_date.js index 370e51222..8641688b1 100644 --- a/app/components/formatted_date.js +++ b/app/components/formatted_date.js @@ -3,35 +3,33 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {injectIntl, intlShape} from 'react-intl'; +import moment from 'moment-timezone'; import {Text} from 'react-native'; -class FormattedDate extends React.PureComponent { +export default class FormattedDate extends React.PureComponent { static propTypes = { - intl: intlShape.isRequired, - value: PropTypes.any.isRequired, format: PropTypes.string, - children: PropTypes.func, + timeZone: PropTypes.string, + value: PropTypes.any.isRequired, + }; + + static defaultProps = { + format: 'ddd, MMM DD, YYYY', }; render() { const { - intl, + format, + timeZone, value, - children, ...props } = this.props; - Reflect.deleteProperty(props, 'format'); - - const formattedDate = intl.formatDate(value, this.props); - - if (typeof children === 'function') { - return children(formattedDate); + let formattedDate = moment(value).format(format); + if (timeZone) { + formattedDate = moment.tz(value, timeZone).format(format); } return {formattedDate}; } } - -export default injectIntl(FormattedDate); diff --git a/app/components/formatted_time.js b/app/components/formatted_time.js index 9f9ea57dd..64cc06569 100644 --- a/app/components/formatted_time.js +++ b/app/components/formatted_time.js @@ -3,7 +3,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {intlShape} from 'react-intl'; import {Text} from 'react-native'; import moment from 'moment-timezone'; @@ -15,30 +14,19 @@ export default class FormattedTime extends React.PureComponent { hour12: PropTypes.bool, }; - static contextTypes = { - intl: intlShape.isRequired, - }; - getFormattedTime = () => { - const {intl} = this.context; - const { value, timeZone, hour12, } = this.props; + const format = hour12 ? 'hh:mm A' : 'HH:mm'; if (timeZone) { - const format = hour12 ? 'hh:mm A' : 'HH:mm'; return moment.tz(value, timeZone).format(format); } - // If no timezone is defined fallback to the previous implementation - return intl.formatDate(new Date(value), { - hour: 'numeric', - minute: 'numeric', - hour12, - }); + return moment(value).format(format); }; render() { diff --git a/app/components/post_header/post_header.js b/app/components/post_header/post_header.js index f9a62f9a9..fee5d3406 100644 --- a/app/components/post_header/post_header.js +++ b/app/components/post_header/post_header.js @@ -218,7 +218,10 @@ export default class PostHeader extends PureComponent { dateComponent = ( - + - - - diff --git a/app/i18n/index.js b/app/i18n/index.js index 7cc58dd93..5271638ed 100644 --- a/app/i18n/index.js +++ b/app/i18n/index.js @@ -4,6 +4,7 @@ import 'intl'; import {addLocaleData} from 'react-intl'; import enLocaleData from 'react-intl/locale-data/en'; +import moment from 'moment'; import en from 'assets/i18n/en.json'; @@ -16,72 +17,92 @@ addLocaleData(enLocaleData); function loadTranslation(locale) { try { let localeData; + let momentData; switch (locale) { case 'de': TRANSLATIONS.de = 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'); localeData = require('react-intl/locale-data/es'); + momentData = require('moment/locale/es'); break; case 'fr': TRANSLATIONS.fr = 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'); localeData = require('react-intl/locale-data/it'); + momentData = require('moment/locale/it'); break; case 'ja': TRANSLATIONS.ja = 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'); localeData = require('react-intl/locale-data/ko'); + momentData = require('moment/locale/ko'); break; case 'nl': TRANSLATIONS.nl = 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'); localeData = require('react-intl/locale-data/pl'); + momentData = require('moment/locale/pl'); break; case 'pt-BR': TRANSLATIONS[locale] = 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'); localeData = require('react-intl/locale-data/ro'); + momentData = require('moment/locale/ro'); break; case 'ru': TRANSLATIONS.ru = 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'); localeData = require('react-intl/locale-data/tr'); + momentData = require('moment/locale/tr'); break; case 'uk': TRANSLATIONS.tr = 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'); 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'); localeData = require('react-intl/locale-data/zh'); + momentData = require('moment/locale/zh-tw'); break; } if (localeData) { addLocaleData(localeData); } + + if (momentData) { + moment.updateLocale(locale, momentData); + } } catch (e) { console.error('NO Translation found', e); //eslint-disable-line no-console } diff --git a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap index cb023a6ae..3bb6582f1 100644 --- a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap +++ b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap @@ -1,13 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`channel_info_header should match snapshot 1`] = ` - + - @@ -416,13 +408,7 @@ exports[`channel_info_header should match snapshot 1`] = ` `; exports[`channel_info_header should match snapshot when DM and hasGuests 1`] = ` - + - @@ -859,13 +843,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests 1`] = ` `; exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = ` - + - @@ -1302,13 +1278,7 @@ exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = ` `; exports[`channel_info_header should match snapshot when is group constrained 1`] = ` - + - @@ -1738,13 +1706,7 @@ exports[`channel_info_header should match snapshot when is group constrained 1`] `; exports[`channel_info_header should match snapshot when public channel and hasGuests 1`] = ` - + - diff --git a/app/screens/channel_info/channel_info_header.js b/app/screens/channel_info/channel_info_header.js index 7a52023c5..053495368 100644 --- a/app/screens/channel_info/channel_info_header.js +++ b/app/screens/channel_info/channel_info_header.js @@ -41,6 +41,7 @@ export default class ChannelInfoHeader extends React.PureComponent { isBot: PropTypes.bool.isRequired, hasGuests: PropTypes.bool.isRequired, isGroupConstrained: PropTypes.bool, + timeZone: PropTypes.string, }; static contextTypes = { @@ -135,6 +136,7 @@ export default class ChannelInfoHeader extends React.PureComponent { isArchived, isBot, isGroupConstrained, + timeZone, } = this.props; const style = getStyleSheet(theme); @@ -145,7 +147,7 @@ export default class ChannelInfoHeader extends React.PureComponent { style.detail; return ( - + } @@ -287,8 +288,5 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { row: { paddingHorizontal: 15, }, - safeAreaView: { - backgroundColor: 'white', - }, }; }); diff --git a/app/screens/channel_info/index.js b/app/screens/channel_info/index.js index f849083b6..81be9d9d9 100644 --- a/app/screens/channel_info/index.js +++ b/app/screens/channel_info/index.js @@ -29,7 +29,8 @@ import {getCurrentUserId, getUser, getStatusForUserId, getCurrentUserRoles} from import {areChannelMentionsIgnored, getUserIdFromChannelName, isChannelMuted, showDeleteOption, showManagementOptions} from 'mattermost-redux/utils/channel_utils'; import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {isGuest} from 'app/utils/users'; +import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; +import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import { popTopScreen, @@ -47,6 +48,8 @@ import { setChannelDisplayName, } from 'app/actions/views/channel'; import {isLandscape} from 'app/selectors/device'; +import {isGuest} from 'app/utils/users'; + import ChannelInfo from './channel_info'; function mapStateToProps(state) { @@ -93,6 +96,12 @@ function mapStateToProps(state) { const canEditChannel = !channelIsReadOnly && showManagementOptions(state, config, license, currentChannel, isAdmin, isSystemAdmin, isChannelAdmin); const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true'; + const enableTimezone = isTimezoneEnabled(state); + let timeZone = null; + if (enableTimezone) { + timeZone = getUserCurrentTimezone(currentUser.timezone); + } + return { canDeleteChannel: showDeleteOption(state, config, license, currentChannel, isAdmin, isSystemAdmin, isChannelAdmin), viewArchivedChannels, @@ -112,6 +121,7 @@ function mapStateToProps(state) { canManageUsers, isBot, isLandscape: isLandscape(state), + timeZone, }; }