rbondesson 11030ae68d
Refactor DateSeparator using MVVM and move to shared-components (#32482)
* Refactor DateSeparator using MVVM and move to shared-components

* Add a few more stories, tests and screenshots

* Use the shared component and viewmodel in element-web

* Renaming custom content property an updating snapshots

* Fix lint errors and update snapshot after merge

* Change lifecycle handling for DateSeparatoreViewModel in components where manual handling is preferrable over wrapper component.

* Move context menu from viewmodel to shared components - step 1

* Create a jump to date picker component in shared components

* Add tests for coverage and fix layout issues and roving indexes

* Make element-web use the new component

* Simplify context menu and adjusting tests

* The HTMLExport now render shared components and need a I18nContext.Provider

* Updating unit tests for context menu

* Changed to {translate: _t} to let scripts pick up translations

* Fix lint issue and updating screenshots after merge

* Update snaps for element web components

* Renaming MVVM view components with suffix View.

* Fixing problem with input date calendar icon and system dark theme

* Changed the rendering of the menu and added a separate button component

* Handle input control with useRef in onKeyDown

* Updating DateSeparator snapshots on unit tests

* Updating layout after compound Menu got a className property

* Move files to new subfolder after merge

* Updated snapshot after merge

* Updating lock file

* Updates to styling from PR review

* Updates to focus/blur functionality

* Fixed tabbing and export documentation to stories

* Updated snapshots

---------

Co-authored-by: Zack <zazi21@student.bth.se>
2026-03-02 12:18:51 +00:00

60 lines
2.4 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type I18nApi } from "@element-hq/element-web-module-api";
import { _t as _tFromModule } from "./i18n";
// These are the constants we use for when to break the text
const MILLISECONDS_RECENT = 15000;
const MILLISECONDS_1_MIN = 75000;
const MINUTES_UNDER_1_HOUR = 45;
const MINUTES_1_HOUR = 75;
const HOURS_UNDER_1_DAY = 23;
const HOURS_1_DAY = 26;
/**
* Converts a timestamp into human-readable, translated, text.
* @param {number} timeMillis The time in millis to compare against.
* @returns {string} The humanized time.
*/
export function humanizeTime(timeMillis: number, i18nApi?: I18nApi): string {
const now = Date.now();
let msAgo = now - timeMillis;
const minutes = Math.abs(Math.ceil(msAgo / 60000));
const hours = Math.ceil(minutes / 60);
const days = Math.ceil(hours / 24);
const _t = i18nApi?.translate ?? _tFromModule;
if (msAgo >= 0) {
// Past
if (msAgo <= MILLISECONDS_RECENT) return _t("time|few_seconds_ago");
if (msAgo <= MILLISECONDS_1_MIN) return _t("time|about_minute_ago");
if (minutes <= MINUTES_UNDER_1_HOUR) return _t("time|n_minutes_ago", { num: minutes });
if (minutes <= MINUTES_1_HOUR) return _t("time|about_hour_ago");
if (hours <= HOURS_UNDER_1_DAY) return _t("time|n_hours_ago", { num: hours });
if (hours <= HOURS_1_DAY) return _t("time|about_day_ago");
return _t("time|n_days_ago", { num: days });
} else {
// Future
msAgo = Math.abs(msAgo);
if (msAgo <= MILLISECONDS_RECENT) return _t("time|in_few_seconds");
if (msAgo <= MILLISECONDS_1_MIN) return _t("time|in_about_minute");
if (minutes <= MINUTES_UNDER_1_HOUR) return _t("time|in_n_minutes", { num: minutes });
if (minutes <= MINUTES_1_HOUR) return _t("time|in_about_hour");
if (hours <= HOURS_UNDER_1_DAY) return _t("time|in_n_hours", { num: hours });
if (hours <= HOURS_1_DAY) return _t("time|in_about_day");
return _t("time|in_n_days", { num: days });
}
}
export function humanizeRelativeTime(i18nApi?: I18nApi): Intl.RelativeTimeFormat {
return new Intl.RelativeTimeFormat(i18nApi?.language, { style: "long", numeric: "auto" });
}