mirror of
https://github.com/unraid/api.git
synced 2026-01-01 14:10:10 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Chores** - Updated configuration versions and connection status settings for improved consistency. - Restructured build and tooling processes with refined script organization, including new commands for type checking and cleanup. - Updated dependency management to support enhanced styling and state management. - Removed legacy scripts related to environment handling and CSS utilities. - **Refactor** - Streamlined module imports to align with an updated theme management structure. - **Bug Fixes** - Enhanced error handling in various components to provide more informative error messages during operations. - Improved error reporting in the update cancellation process. - Improved error reporting in date difference calculations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
233 lines
7.3 KiB
TypeScript
233 lines
7.3 KiB
TypeScript
import dayjs, { extend } from 'dayjs';
|
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
import type { ComposerTranslation } from 'vue-i18n';
|
|
|
|
import type { DateFormatOption, ServerDateTimeFormat, TimeFormatOption } from '~/types/server';
|
|
|
|
/** @see https://day.js.org/docs/en/display/format#localized-formats */
|
|
extend(localizedFormat);
|
|
|
|
export interface TimeStringsObject {
|
|
years: number;
|
|
months: number;
|
|
days: number;
|
|
hours: number;
|
|
minutes: number;
|
|
seconds: number;
|
|
firstDateWasLater: boolean;
|
|
displaySeconds?: boolean;
|
|
}
|
|
|
|
const dateFormatOptions: DateFormatOption[] = [
|
|
{ format: '%c', display: 'ddd, D MMMM YYYY' }, // aka "system settings" in the date time settings of the webgui
|
|
{ format: '%A, %Y %B %e', display: 'ddd, YYYY MMMM D' },
|
|
{ format: '%A, %e %B %Y', display: 'ddd, D MMMM YYYY' },
|
|
{ format: '%A, %B %e, %Y', display: 'ddd, MMMM D, YYYY' },
|
|
{ format: '%A, %m/%d/%Y', display: 'ddd, MM/DD/YYYY' },
|
|
{ format: '%A, %d-%m-%Y', display: 'ddd, DD-MM-YYYY' },
|
|
{ format: '%A, %d.%m.%Y', display: 'ddd, DD.MM.YYYY' },
|
|
{ format: '%A, %Y-%m-%d', display: 'ddd, YYYY-MM-DD' },
|
|
];
|
|
|
|
const timeFormatOptions: TimeFormatOption[] = [
|
|
{ format: '%I:%M %p', display: 'hh:mma' },
|
|
{ format: '%R', display: 'HH:mm' },
|
|
];
|
|
|
|
/**
|
|
* the provided ref may not have a value until we get a response from the refreshServerState action
|
|
* So we need to watch for this value to be able to format it based on the user's date time preferences.
|
|
* @example below is how to use this composable
|
|
* const formattedRegExp = ref<any>();
|
|
* const setFormattedRegExp = () => { // ran in watch on regExp and onBeforeMount
|
|
* if (!regExp.value) { return; }
|
|
* const { outputDateTimeFormatted } = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
|
* formattedRegExp.value = outputDateTimeFormatted.value;
|
|
* };
|
|
* watch(regExp, (_newV) => {
|
|
* setFormattedRegExp();
|
|
* });
|
|
* onBeforeMount(() => {
|
|
* setFormattedRegExp();
|
|
* });
|
|
*
|
|
* @param format provided by Unraid server's state.php and set in the server store
|
|
* @param t translations
|
|
* @param hideMinutesSeconds true will hide minutes and seconds from the output
|
|
* @param providedDateTime optional provided date time to use instead of Date.now()
|
|
* @param diffCountUp true will count up from the provided date time instead of down
|
|
*/
|
|
const useDateTimeHelper = (
|
|
format: ServerDateTimeFormat | undefined,
|
|
t: ComposerTranslation,
|
|
hideMinutesSeconds?: boolean,
|
|
providedDateTime?: number | undefined,
|
|
diffCountUp?: boolean,
|
|
) => {
|
|
const buildStringFromValues = (payload: TimeStringsObject) => {
|
|
const {
|
|
years,
|
|
months,
|
|
days,
|
|
hours,
|
|
minutes,
|
|
seconds,
|
|
firstDateWasLater,
|
|
displaySeconds,
|
|
} = payload;
|
|
const result = [];
|
|
|
|
if (years) { result.push(t('year', years)); }
|
|
if (months) { result.push(t('month', months)); }
|
|
if (days) { result.push(t('day', days)); }
|
|
if (hours) { result.push(t('hour', hours)); }
|
|
if (minutes) { result.push(t('minute', minutes)); }
|
|
if (seconds && ((!years && !months && !days && !hours && !minutes) || displaySeconds)) { result.push(t('second', seconds)); }
|
|
if (firstDateWasLater) { result.push(t('ago')); }
|
|
return result.join(' ');
|
|
};
|
|
|
|
// use the format from the server to determine the format to use
|
|
const findMatchingFormat = (
|
|
selectedFormat: string,
|
|
formats: DateFormatOption[] | TimeFormatOption[],
|
|
): DateFormatOption | TimeFormatOption | undefined =>
|
|
formats.find(formatOption => formatOption.format === selectedFormat);
|
|
|
|
const dateFormat = findMatchingFormat(format?.date ?? dateFormatOptions[0].format, dateFormatOptions);
|
|
console.debug('[dateFormat]', dateFormat);
|
|
|
|
let displayFormat = `${dateFormat?.display}`;
|
|
console.debug('[displayFormat]', displayFormat);
|
|
if (!hideMinutesSeconds) {
|
|
const timeFormat = findMatchingFormat(format?.time ?? timeFormatOptions[0].format, timeFormatOptions);
|
|
displayFormat = `${displayFormat} ${timeFormat?.display}`;
|
|
console.debug('[displayFormat] with time', displayFormat);
|
|
}
|
|
|
|
const formatDate = (date: number): string =>
|
|
dayjs(date).format(displayFormat);
|
|
console.debug('[formatDate]', formatDate(Date.now()));
|
|
|
|
/**
|
|
* Original meat and potatos from:
|
|
* @version: 1.0.1
|
|
* @author: huangjinlin
|
|
* @repo: https://github.com/huangjinlin/dayjs-precise-range
|
|
*/
|
|
const buildValueObject = (
|
|
yDiff: number,
|
|
mDiff: number,
|
|
dDiff: number,
|
|
hourDiff: number,
|
|
minDiff: number,
|
|
secDiff: number,
|
|
firstDateWasLater: boolean
|
|
): TimeStringsObject => ({
|
|
years: yDiff,
|
|
months: mDiff,
|
|
days: dDiff,
|
|
hours: hourDiff,
|
|
minutes: minDiff,
|
|
seconds: secDiff,
|
|
firstDateWasLater,
|
|
});
|
|
|
|
const preciseDateDiff = (d1: dayjs.Dayjs, d2: dayjs.Dayjs): TimeStringsObject => {
|
|
let m1 = dayjs(d1);
|
|
let m2 = dayjs(d2);
|
|
let firstDateWasLater;
|
|
|
|
if (m1.isSame(m2)) {
|
|
return buildValueObject(0, 0, 0, 0, 0, 0, false);
|
|
}
|
|
if (m1.isAfter(m2)) {
|
|
const tmp = m1;
|
|
m1 = m2;
|
|
m2 = tmp;
|
|
firstDateWasLater = true;
|
|
} else {
|
|
firstDateWasLater = false;
|
|
}
|
|
|
|
let yDiff = m2.year() - m1.year();
|
|
let mDiff = m2.month() - m1.month();
|
|
let dDiff = m2.date() - m1.date();
|
|
let hourDiff = m2.hour() - m1.hour();
|
|
let minDiff = m2.minute() - m1.minute();
|
|
let secDiff = m2.second() - m1.second();
|
|
|
|
if (secDiff < 0) {
|
|
secDiff = 60 + secDiff;
|
|
minDiff -= 1;
|
|
}
|
|
if (minDiff < 0) {
|
|
minDiff = 60 + minDiff;
|
|
hourDiff -= 1;
|
|
}
|
|
if (hourDiff < 0) {
|
|
hourDiff = 24 + hourDiff;
|
|
dDiff -= 1;
|
|
}
|
|
if (dDiff < 0) {
|
|
const daysInLastFullMonth = dayjs(`${m2.year()}-${m2.month() + 1}`).subtract(1, 'M').daysInMonth();
|
|
if (daysInLastFullMonth < m1.date()) { // 31/01 -> 2/03
|
|
dDiff = daysInLastFullMonth + dDiff + (m1.date() - daysInLastFullMonth);
|
|
} else {
|
|
dDiff = daysInLastFullMonth + dDiff;
|
|
}
|
|
mDiff -= 1;
|
|
}
|
|
if (mDiff < 0) {
|
|
mDiff = 12 + mDiff;
|
|
yDiff -= 1;
|
|
}
|
|
|
|
return buildValueObject(yDiff, mDiff, dDiff, hourDiff, minDiff, secDiff, firstDateWasLater);
|
|
};
|
|
|
|
const readableDifference = (a = '', b = '') => {
|
|
try {
|
|
const x = a ? dayjs(parseInt(a, 10)) : dayjs();
|
|
const y = b ? dayjs(parseInt(b, 10)) : dayjs();
|
|
return preciseDateDiff(x, y);
|
|
} catch (error) {
|
|
throw new Error(`Couldn't calculate date difference with error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
}
|
|
};
|
|
|
|
const dateDiff = (time: string, countUp: boolean) => countUp ? readableDifference(time, '') : readableDifference('', time);
|
|
|
|
// provide outputs for components
|
|
const outputDateTimeReadableDiff = ref<string>('');
|
|
const outputDateTimeFormatted = computed(() => formatDate(providedDateTime ?? Date.now()));
|
|
|
|
const runDiff = () => {
|
|
outputDateTimeReadableDiff.value = buildStringFromValues(dateDiff((providedDateTime ?? Date.now()).toString(), diffCountUp ?? false));
|
|
};
|
|
|
|
let interval: string | number | NodeJS.Timeout | undefined;
|
|
onBeforeMount(() => {
|
|
if (providedDateTime) {
|
|
runDiff();
|
|
interval = setInterval(() => {
|
|
runDiff();
|
|
}, 1000);
|
|
}
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
if (interval) {
|
|
clearInterval(interval);
|
|
}
|
|
});
|
|
|
|
return {
|
|
formatDate,
|
|
outputDateTimeReadableDiff,
|
|
outputDateTimeFormatted,
|
|
};
|
|
};
|
|
|
|
export default useDateTimeHelper;
|