You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/util/i18n/formater/expirationTimer.ts

128 lines
4.3 KiB
TypeScript

import { Duration, formatDuration, intervalToDuration } from 'date-fns';
import { DURATION_SECONDS } from '../../../session/constants';
import { getForcedEnglishTimeLocale } from '../timeLocaleMap';
/**
* We decided against localizing the abbreviated durations like 1h, 1m, 1s as most apps don't.
* This function just replaces any long form of "seconds?" to "s", "minutes?" to "m", etc.
*
* Note:
* We don't replace to 'months' as it would be the same as 'minutes', so this function shouldn't be used for a string containing months or longer units in it.
*
* Date-fns also doesn't support the 'narrow' syntax for formatDistanceStrict and even if it did, minutes are abbreviated as 'min' in english.
*
* @param unlocalized the string containing the units to abbreviate
* @returns the string with abbreviated units
*/
const unlocalizedDurationToAbbreviated = (unlocalized: string): string => {
return unlocalized
.replace(/ weeks?/g, 'w')
.replace(/ days?/g, 'd')
.replace(/ hours?/g, 'h')
.replace(/ minutes?/g, 'm')
.replace(/ seconds?/g, 's');
};
/**
* date-fns `intervalToDuration` takes a duration in ms.
* This is a simple wrapper to avoid duplicating this (and not forget about it).
*
* Note:
* - date-fns intervalToDuration returns doesn't return 2w for 14d and such, so this forces it to be used.
* - this will throw if the duration is > 4 weeks
*
* @param seconds the seconds to get the durations from
* @returns a date-fns `Duration` type with the fields set
*/
const secondsToDuration = (seconds: number): Duration => {
if (seconds > 3600 * 24 * 28) {
throw new Error('secondsToDuration cannot handle more than 4 weeks for now');
}
const duration = intervalToDuration({ start: 0, end: new Date(seconds * 1000) });
if (!duration) {
throw new Error('intervalToDuration failed to convert duration');
}
if (duration.days) {
duration.weeks = Math.floor(duration.days / 7);
duration.days %= 7;
}
return duration;
};
/**
* Format an expiring/disappearing message timer to its abbreviated form.
* Note: we don't localize this, and cannot have a value > 4 weeks
*
* @param timerSeconds the timer to format, in seconds
* @returns '1h' for a duration of 3600s.
*/
export const formatAbbreviatedExpireTimer = (timerSeconds: number) => {
// Note: we keep this function in this file even if it is not localizing anything
// so we have access to timeLocaleMap.en.
if (timerSeconds > DURATION_SECONDS.WEEKS * 4) {
throw new Error('formatAbbreviatedExpireTimer is not design to handle >4 weeks durations ');
}
const duration = secondsToDuration(timerSeconds);
const unlocalized = formatDuration(duration, {
locale: getForcedEnglishTimeLocale(), // we want this forced to english
});
return unlocalizedDurationToAbbreviated(unlocalized);
};
/**
* Format an expiring/disappearing message timer to its abbreviated form.
* Note: we don't localize this, and cannot have a value > 4 weeks
*
* @param timerSeconds the timer to format, in seconds
* @returns '1h' for a duration of 3600s.
*/
export const formatAbbreviatedExpireDoubleTimer = (timerSeconds: number) => {
// Note: we keep this function in this file even if it is not localizing anything
// so we have access to timeLocaleMap.en.
if (timerSeconds > DURATION_SECONDS.WEEKS * 4) {
throw new Error(
'formatAbbreviatedExpireDoubleTimer is not design to handle >4 weeks durations '
);
}
if (timerSeconds <= 0) {
return ['0s'];
}
const duration = secondsToDuration(timerSeconds);
const format: Array<keyof Duration> = [];
if (duration.months || duration.years) {
throw new Error("we don't support years or months to be !== 0");
}
if (duration.weeks && format.length < 2) {
format.push('weeks');
}
if (duration.days && format.length < 2) {
format.push('days');
}
if (duration.hours && format.length < 2) {
format.push('hours');
}
if (duration.minutes && format.length < 2) {
format.push('minutes');
}
if (duration.seconds && format.length < 2) {
format.push('seconds');
}
const unlocalized = formatDuration(duration, {
locale: getForcedEnglishTimeLocale(), // we want this forced to english
delimiter: '#',
format,
});
return unlocalizedDurationToAbbreviated(unlocalized).split('#');
};