diff --git a/app/components/custom_status/custom_status_expiry.tsx b/app/components/custom_status/custom_status_expiry.tsx index 400da88ca..10e57a34f 100644 --- a/app/components/custom_status/custom_status_expiry.tsx +++ b/app/components/custom_status/custom_status_expiry.tsx @@ -8,7 +8,7 @@ import {Text, type TextStyle} from 'react-native'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import FormattedDate from '@components/formatted_date'; +import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date'; import FormattedText from '@components/formatted_text'; import FormattedTime from '@components/formatted_time'; import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference'; @@ -43,6 +43,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); +const DATE_FORMATS = { + withinWeek: {weekday: 'long'}, + withinYear: {month: 'short', day: 'numeric'}, + afterYear: {dateStyle: 'medium'}, +} satisfies Record; + const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCompulsory, showToday, testID = '', textStyles = {}, theme, time, withinBrackets}: Props) => { const userTimezone = getUserTimezoneProps(currentUser); const timezone = userTimezone.useAutomaticTimezone ? userTimezone.automaticTimezone : userTimezone.manualTimezone; @@ -72,11 +78,11 @@ const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCo /> ); } else if (expiryMomentTime.isAfter(tomorrowEndTime)) { - let format = 'dddd'; + let format: FormattedDateFormat = DATE_FORMATS.withinWeek; if (expiryMomentTime.isAfter(plusSixDaysEndTime) && isCurrentYear) { - format = 'MMM DD'; + format = DATE_FORMATS.withinYear; } else if (!isCurrentYear) { - format = 'MMM DD, YYYY'; + format = DATE_FORMATS.afterYear; } dateComponent = ( diff --git a/app/components/files/file_info.tsx b/app/components/files/file_info.tsx index c88f04cf1..50f965fbb 100644 --- a/app/components/files/file_info.tsx +++ b/app/components/files/file_info.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {Text, TouchableOpacity, View} from 'react-native'; -import FormattedDate from '@components/formatted_date'; +import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date'; import {useTheme} from '@context/theme'; import {getFormattedFileSize} from '@utils/file'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; @@ -14,10 +14,15 @@ type FileInfoProps = { disabled?: boolean; file: FileInfo; showDate: boolean; - channelName?: string ; + channelName?: string; onPress: () => void; -} -const FORMAT = ' • MMM DD HH:MM A'; +}; +const FORMAT: FormattedDateFormat = { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', +}; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { @@ -91,13 +96,16 @@ const FileInfo = ({disabled, file, channelName, showDate, onPress}: FileInfoProp {`${getFormattedFileSize(file.size)}`} - {showDate && - - } + {showDate && file.create_at != null && ( + <> + {' • '} + + + )} diff --git a/app/components/files_search/file_options/header.tsx b/app/components/files_search/file_options/header.tsx index 0e9a45c1a..2f85eed1d 100644 --- a/app/components/files_search/file_options/header.tsx +++ b/app/components/files_search/file_options/header.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {View, Text} from 'react-native'; -import FormattedDate from '@components/formatted_date'; +import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date'; import {useTheme} from '@context/theme'; import {getFormattedFileSize} from '@utils/file'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; @@ -11,7 +11,13 @@ import {typography} from '@utils/typography'; import Icon, {ICON_SIZE} from './icon'; -const format = 'MMM DD YYYY HH:MM A'; +const FORMAT: FormattedDateFormat = { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', +}; const HEADER_MARGIN = 8; const FILE_ICON_MARGIN = 8; @@ -78,7 +84,7 @@ const Header = ({fileInfo}: Props) => { {`${size} • `} diff --git a/app/components/formatted_date/__snapshots__/index.test.tsx.snap b/app/components/formatted_date/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..75a0d8936 --- /dev/null +++ b/app/components/formatted_date/__snapshots__/index.test.tsx.snap @@ -0,0 +1,799 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match snapshot for 'bg' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26.10.2024 г. + +`; + +exports[` should match snapshot for 'bg' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26.10.2024 г., 10:01 ч. + +`; + +exports[` should match snapshot for 'bg' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26.10, 10:01 ч. + +`; + +exports[` should match snapshot for 'bg' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26.10 + +`; + +exports[` should match snapshot for 'bg' locale and '{"weekday": "long"}' format 1`] = ` + + събота + +`; + +exports[` should match snapshot for 'bg' locale and 'undefined' format 1`] = ` + + 26.10.2024 г. + +`; + +exports[` should match snapshot for 'de' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26.10.2024 + +`; + +exports[` should match snapshot for 'de' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26. Okt. 2024, 10:01 + +`; + +exports[` should match snapshot for 'de' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26. Okt., 10:01 + +`; + +exports[` should match snapshot for 'de' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26. Okt. + +`; + +exports[` should match snapshot for 'de' locale and '{"weekday": "long"}' format 1`] = ` + + Samstag + +`; + +exports[` should match snapshot for 'de' locale and 'undefined' format 1`] = ` + + 26.10.2024 + +`; + +exports[` should match snapshot for 'en' locale and '{"dateStyle": "medium"}' format 1`] = ` + + Oct 26, 2024 + +`; + +exports[` should match snapshot for 'en' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + Oct 26, 2024, 10:01 AM + +`; + +exports[` should match snapshot for 'en' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + Oct 26, 10:01 AM + +`; + +exports[` should match snapshot for 'en' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + Oct 26 + +`; + +exports[` should match snapshot for 'en' locale and '{"weekday": "long"}' format 1`] = ` + + Saturday + +`; + +exports[` should match snapshot for 'en' locale and 'undefined' format 1`] = ` + + Oct 26, 2024 + +`; + +exports[` should match snapshot for 'en-AU' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 Oct 2024 + +`; + +exports[` should match snapshot for 'en-AU' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 Oct 2024, 10:01 am + +`; + +exports[` should match snapshot for 'en-AU' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 Oct, 10:01 am + +`; + +exports[` should match snapshot for 'en-AU' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 Oct + +`; + +exports[` should match snapshot for 'en-AU' locale and '{"weekday": "long"}' format 1`] = ` + + Saturday + +`; + +exports[` should match snapshot for 'en-AU' locale and 'undefined' format 1`] = ` + + 26 Oct 2024 + +`; + +exports[` should match snapshot for 'es' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 oct 2024 + +`; + +exports[` should match snapshot for 'es' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 oct 2024, 10:01 + +`; + +exports[` should match snapshot for 'es' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 oct, 10:01 + +`; + +exports[` should match snapshot for 'es' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 oct + +`; + +exports[` should match snapshot for 'es' locale and '{"weekday": "long"}' format 1`] = ` + + sábado + +`; + +exports[` should match snapshot for 'es' locale and 'undefined' format 1`] = ` + + 26 oct 2024 + +`; + +exports[` should match snapshot for 'fa' locale and '{"dateStyle": "medium"}' format 1`] = ` + + ۵ آبان ۱۴۰۳ + +`; + +exports[` should match snapshot for 'fa' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + ۵ آبان ۱۴۰۳، ۱۰:۰۱ + +`; + +exports[` should match snapshot for 'fa' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + ۵ آبان، ۱۰:۰۱ + +`; + +exports[` should match snapshot for 'fa' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + ۵ آبان + +`; + +exports[` should match snapshot for 'fa' locale and '{"weekday": "long"}' format 1`] = ` + + شنبه + +`; + +exports[` should match snapshot for 'fa' locale and 'undefined' format 1`] = ` + + ۵ آبان ۱۴۰۳ + +`; + +exports[` should match snapshot for 'fr' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 oct. 2024 + +`; + +exports[` should match snapshot for 'fr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 oct. 2024, 10:01 + +`; + +exports[` should match snapshot for 'fr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 oct., 10:01 + +`; + +exports[` should match snapshot for 'fr' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 oct. + +`; + +exports[` should match snapshot for 'fr' locale and '{"weekday": "long"}' format 1`] = ` + + samedi + +`; + +exports[` should match snapshot for 'fr' locale and 'undefined' format 1`] = ` + + 26 oct. 2024 + +`; + +exports[` should match snapshot for 'hu' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 2024. okt. 26. + +`; + +exports[` should match snapshot for 'hu' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 2024. okt. 26. 10:01 + +`; + +exports[` should match snapshot for 'hu' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + okt. 26. 10:01 + +`; + +exports[` should match snapshot for 'hu' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + okt. 26. + +`; + +exports[` should match snapshot for 'hu' locale and '{"weekday": "long"}' format 1`] = ` + + szombat + +`; + +exports[` should match snapshot for 'hu' locale and 'undefined' format 1`] = ` + + 2024. okt. 26. + +`; + +exports[` should match snapshot for 'it' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 ott 2024 + +`; + +exports[` should match snapshot for 'it' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 ott 2024, 10:01 + +`; + +exports[` should match snapshot for 'it' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 ott, 10:01 + +`; + +exports[` should match snapshot for 'it' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 ott + +`; + +exports[` should match snapshot for 'it' locale and '{"weekday": "long"}' format 1`] = ` + + sabato + +`; + +exports[` should match snapshot for 'it' locale and 'undefined' format 1`] = ` + + 26 ott 2024 + +`; + +exports[` should match snapshot for 'ja' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 2024/10/26 + +`; + +exports[` should match snapshot for 'ja' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 2024年10月26日 10:01 + +`; + +exports[` should match snapshot for 'ja' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 10:01 + +`; + +exports[` should match snapshot for 'ja' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 + +`; + +exports[` should match snapshot for 'ja' locale and '{"weekday": "long"}' format 1`] = ` + + 土曜日 + +`; + +exports[` should match snapshot for 'ja' locale and 'undefined' format 1`] = ` + + 2024/10/26 + +`; + +exports[` should match snapshot for 'ko' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 2024. 10. 26. + +`; + +exports[` should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 2024년 10월 26일 오전 10:01 + +`; + +exports[` should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 10월 26일 오전 10:01 + +`; + +exports[` should match snapshot for 'ko' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 10월 26일 + +`; + +exports[` should match snapshot for 'ko' locale and '{"weekday": "long"}' format 1`] = ` + + 토요일 + +`; + +exports[` should match snapshot for 'ko' locale and 'undefined' format 1`] = ` + + 2024. 10. 26. + +`; + +exports[` should match snapshot for 'nl' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 okt 2024 + +`; + +exports[` should match snapshot for 'nl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 okt 2024, 10:01 + +`; + +exports[` should match snapshot for 'nl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 okt, 10:01 + +`; + +exports[` should match snapshot for 'nl' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 okt + +`; + +exports[` should match snapshot for 'nl' locale and '{"weekday": "long"}' format 1`] = ` + + zaterdag + +`; + +exports[` should match snapshot for 'nl' locale and 'undefined' format 1`] = ` + + 26 okt 2024 + +`; + +exports[` should match snapshot for 'pl' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 paź 2024 + +`; + +exports[` should match snapshot for 'pl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 paź 2024, 10:01 + +`; + +exports[` should match snapshot for 'pl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 paź, 10:01 + +`; + +exports[` should match snapshot for 'pl' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 paź + +`; + +exports[` should match snapshot for 'pl' locale and '{"weekday": "long"}' format 1`] = ` + + sobota + +`; + +exports[` should match snapshot for 'pl' locale and 'undefined' format 1`] = ` + + 26 paź 2024 + +`; + +exports[` should match snapshot for 'pt-BR' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 de out. de 2024 + +`; + +exports[` should match snapshot for 'pt-BR' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 de out. de 2024, 10:01 + +`; + +exports[` should match snapshot for 'pt-BR' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 de out., 10:01 + +`; + +exports[` should match snapshot for 'pt-BR' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 de out. + +`; + +exports[` should match snapshot for 'pt-BR' locale and '{"weekday": "long"}' format 1`] = ` + + sábado + +`; + +exports[` should match snapshot for 'pt-BR' locale and 'undefined' format 1`] = ` + + 26 de out. de 2024 + +`; + +exports[` should match snapshot for 'ro' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 oct. 2024 + +`; + +exports[` should match snapshot for 'ro' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 oct. 2024, 10:01 + +`; + +exports[` should match snapshot for 'ro' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 oct., 10:01 + +`; + +exports[` should match snapshot for 'ro' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 oct. + +`; + +exports[` should match snapshot for 'ro' locale and '{"weekday": "long"}' format 1`] = ` + + sâmbătă + +`; + +exports[` should match snapshot for 'ro' locale and 'undefined' format 1`] = ` + + 26 oct. 2024 + +`; + +exports[` should match snapshot for 'ru' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 окт. 2024 г. + +`; + +exports[` should match snapshot for 'ru' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 окт. 2024 г., 10:01 + +`; + +exports[` should match snapshot for 'ru' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 окт., 10:01 + +`; + +exports[` should match snapshot for 'ru' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 окт. + +`; + +exports[` should match snapshot for 'ru' locale and '{"weekday": "long"}' format 1`] = ` + + суббота + +`; + +exports[` should match snapshot for 'ru' locale and 'undefined' format 1`] = ` + + 26 окт. 2024 г. + +`; + +exports[` should match snapshot for 'sv' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 okt. 2024 + +`; + +exports[` should match snapshot for 'sv' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 okt. 2024 10:01 + +`; + +exports[` should match snapshot for 'sv' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 okt. 10:01 + +`; + +exports[` should match snapshot for 'sv' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 okt. + +`; + +exports[` should match snapshot for 'sv' locale and '{"weekday": "long"}' format 1`] = ` + + lördag + +`; + +exports[` should match snapshot for 'sv' locale and 'undefined' format 1`] = ` + + 26 okt. 2024 + +`; + +exports[` should match snapshot for 'tr' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 Eki 2024 + +`; + +exports[` should match snapshot for 'tr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 Eki 2024 10:01 + +`; + +exports[` should match snapshot for 'tr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 Eki 10:01 + +`; + +exports[` should match snapshot for 'tr' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 Eki + +`; + +exports[` should match snapshot for 'tr' locale and '{"weekday": "long"}' format 1`] = ` + + Cumartesi + +`; + +exports[` should match snapshot for 'tr' locale and 'undefined' format 1`] = ` + + 26 Eki 2024 + +`; + +exports[` should match snapshot for 'uk' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 жовт. 2024 р. + +`; + +exports[` should match snapshot for 'uk' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 26 жовт. 2024 р., 10:01 + +`; + +exports[` should match snapshot for 'uk' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 26 жовт., 10:01 + +`; + +exports[` should match snapshot for 'uk' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 жовт. + +`; + +exports[` should match snapshot for 'uk' locale and '{"weekday": "long"}' format 1`] = ` + + субота + +`; + +exports[` should match snapshot for 'uk' locale and 'undefined' format 1`] = ` + + 26 жовт. 2024 р. + +`; + +exports[` should match snapshot for 'vi' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 26 thg 10, 2024 + +`; + +exports[` should match snapshot for 'vi' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 10:01 26 thg 10, 2024 + +`; + +exports[` should match snapshot for 'vi' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 10:01 26 thg 10 + +`; + +exports[` should match snapshot for 'vi' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 26 thg 10 + +`; + +exports[` should match snapshot for 'vi' locale and '{"weekday": "long"}' format 1`] = ` + + Thứ Bảy + +`; + +exports[` should match snapshot for 'vi' locale and 'undefined' format 1`] = ` + + 26 thg 10, 2024 + +`; + +exports[` should match snapshot for 'zh-CN' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 2024年10月26日 + +`; + +exports[` should match snapshot for 'zh-CN' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 2024年10月26日 10:01 + +`; + +exports[` should match snapshot for 'zh-CN' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 10:01 + +`; + +exports[` should match snapshot for 'zh-CN' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 + +`; + +exports[` should match snapshot for 'zh-CN' locale and '{"weekday": "long"}' format 1`] = ` + + 星期六 + +`; + +exports[` should match snapshot for 'zh-CN' locale and 'undefined' format 1`] = ` + + 2024年10月26日 + +`; + +exports[` should match snapshot for 'zh-TW' locale and '{"dateStyle": "medium"}' format 1`] = ` + + 2024年10月26日 + +`; + +exports[` should match snapshot for 'zh-TW' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = ` + + 2024年10月26日 上午10:01 + +`; + +exports[` should match snapshot for 'zh-TW' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 上午10:01 + +`; + +exports[` should match snapshot for 'zh-TW' locale and '{"day": "numeric", "month": "short"}' format 1`] = ` + + 10月26日 + +`; + +exports[` should match snapshot for 'zh-TW' locale and '{"weekday": "long"}' format 1`] = ` + + 星期六 + +`; + +exports[` should match snapshot for 'zh-TW' locale and 'undefined' format 1`] = ` + + 2024年10月26日 + +`; + +exports[` should render with a manual user time 1`] = ` + + Oct 26, 2024 + +`; diff --git a/app/components/formatted_date/index.test.tsx b/app/components/formatted_date/index.test.tsx new file mode 100644 index 000000000..6e68039f4 --- /dev/null +++ b/app/components/formatted_date/index.test.tsx @@ -0,0 +1,79 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithIntl} from '@test/intl-test-helper'; + +import locales from '../../i18n/languages'; + +import FormattedDate, {type FormattedDateFormat} from './index'; + +const DATE = new Date('2024-10-26T10:01:04.653Z'); +const FORMATS = [ + undefined, + {weekday: 'long'}, + {dateStyle: 'medium'}, + {month: 'short', day: 'numeric'}, + { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }, + { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }, +] satisfies Array; + +const TEST_MATRIX = Object.keys(locales). + map((locale) => FORMATS.map<[string, FormattedDateFormat | undefined]>((format) => [locale, format])). + flat(1); + +describe('', () => { + it.each(TEST_MATRIX)("should match snapshot for '%s' locale and '%p' format", (locale, format) => { + const wrapper = renderWithIntl( + , + {locale}, + ); + expect(wrapper.toJSON()).toMatchSnapshot(); + }); + + it('should render with a manual user time', () => { + const wrapper = renderWithIntl( + , + ); + expect(wrapper.toJSON()).toMatchSnapshot(); + }); + + it('should render with an automatic user time', () => { + const wrapper = renderWithIntl( + , + ); + + // Just check that the component render as automatic timezone is environment dependant + expect(wrapper.toJSON()).toBeTruthy(); + }); +}); diff --git a/app/components/formatted_date/index.tsx b/app/components/formatted_date/index.tsx index ecf499bbe..6f56d8df0 100644 --- a/app/components/formatted_date/index.tsx +++ b/app/components/formatted_date/index.tsx @@ -1,34 +1,40 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import moment from 'moment'; -import mtz from 'moment-timezone'; import React from 'react'; import {useIntl} from 'react-intl'; import {Text, type TextProps} from 'react-native'; -import {getLocaleFromLanguage} from '@i18n'; +export type FormattedDateFormat = Exclude; type FormattedDateProps = TextProps & { - format?: string; + format?: FormattedDateFormat; timezone?: string | UserTimezone | null; value: number | string | Date; } -const FormattedDate = ({format = 'MMM DD, YYYY', timezone, value, ...props}: FormattedDateProps) => { +const DEFAULT_FORMAT: FormattedDateFormat = {dateStyle: 'medium'}; + +const FormattedDate = ({ + format = DEFAULT_FORMAT, + timezone, + value, + ...props +}: FormattedDateProps) => { const {locale} = useIntl(); - moment.locale(getLocaleFromLanguage(locale).toLowerCase()); - let formattedDate = mtz(value).format(format); - if (timezone) { - let zone: string; - if (typeof timezone === 'object') { - zone = timezone.useAutomaticTimezone ? timezone.automaticTimezone : timezone.manualTimezone; - } else { - zone = timezone; - } - formattedDate = mtz.tz(value, zone).format(format); + + let timeZone: string | undefined; + if (timezone && typeof timezone === 'object') { + timeZone = timezone.useAutomaticTimezone ? timezone.automaticTimezone : timezone.manualTimezone; + } else { + timeZone = timezone ?? undefined; } + const formattedDate = new Intl.DateTimeFormat(locale, { + ...format, + timeZone, + }).format(new Date(value)); + return {formattedDate}; }; diff --git a/app/components/post_list/date_separator/index.tsx b/app/components/post_list/date_separator/index.tsx index a48d6df7a..5e06e4d72 100644 --- a/app/components/post_list/date_separator/index.tsx +++ b/app/components/post_list/date_separator/index.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {type StyleProp, View, type ViewStyle} from 'react-native'; -import FormattedDate from '@components/formatted_date'; +import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date'; import FormattedText from '@components/formatted_text'; import {useTheme} from '@context/theme'; import {isSameYear, isToday, isYesterday} from '@utils/datetime'; @@ -38,6 +38,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); +const DATE_FORMATS = { + withinYear: {month: 'short', day: 'numeric'}, + afterYear: {dateStyle: 'medium'}, +} satisfies Record; + const RecentDate = (props: DateSeparatorProps) => { const {date, ...otherProps} = props; const when = new Date(date); @@ -60,7 +65,7 @@ const RecentDate = (props: DateSeparatorProps) => { ); } - const format = isSameYear(when, new Date()) ? 'MMM DD' : 'MMM DD, YYYY'; + const format: FormattedDateFormat = isSameYear(when, new Date()) ? DATE_FORMATS.withinYear : DATE_FORMATS.afterYear; return (