B 設備管理:device-store 補 registeredAt + register/unregister actions; deriveTriState 三態(已連接/未連接/已連接未註冊);三態 badge(warning token + icon + 文字不只色);排序/filter chips;註冊 UI(明確區分取消註冊≠移除)。 C 模型共享:/models/library cursor 無限捲動 + 搜尋/filter/排序;visibility badge 三態;公開設定 Dialog(RadioGroup + shares 管理 + public 警告 + 二次確認); profile 頁 owner/公開雙態;radio-group 新元件;owner 用 name 不洩 email。 共用檔 types/api.ts(B error codes)+ i18n en/zh(devices.* B / models.* C)。 零新 Design Token。reviewer B(0C/0M) + C(0C/0M、設計12/12 API8/8) 通過。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
1.6 KiB
TypeScript
39 lines
1.6 KiB
TypeScript
/**
|
||
* 相對時間格式化(共用 util)
|
||
*
|
||
* 從 `components/cloud/remote-device-badge.tsx` 的 `formatRelativeTime` 抽出共用,
|
||
* 讓「模型共享」的 owner 資訊列 / 共享時間沿用同一份規格與 i18n key,避免重複實作。
|
||
*
|
||
* 規格(components.md §10.3):
|
||
* - < 60 秒 → 「剛剛」(remote.lastSeen.justNow)
|
||
* - < 60 分 → 「X 分鐘前」(remote.lastSeen.minutesAgo)
|
||
* - < 24 時 → 「X 小時前」(remote.lastSeen.hoursAgo)
|
||
* - ≥ 24 時 → 絕對時間「MM/DD HH:mm」
|
||
*/
|
||
|
||
/**
|
||
* @param isoString ISO 8601 時間字串(無法解析時回空字串)
|
||
* @param nowMs 當前時間(ms)— 由 caller 傳入以便測試 deterministic
|
||
* @param t i18n 翻譯函式(需含 remote.lastSeen.* key)
|
||
*/
|
||
export function formatRelativeTime(
|
||
isoString: string,
|
||
nowMs: number,
|
||
t: (k: string) => string,
|
||
): string {
|
||
const ts = Date.parse(isoString);
|
||
if (Number.isNaN(ts)) return "";
|
||
const diffSec = Math.max(0, Math.floor((nowMs - ts) / 1000));
|
||
if (diffSec < 60) return t("remote.lastSeen.justNow");
|
||
const diffMin = Math.floor(diffSec / 60);
|
||
if (diffMin < 60) return t("remote.lastSeen.minutesAgo").replace("{n}", String(diffMin));
|
||
const diffHour = Math.floor(diffMin / 60);
|
||
if (diffHour < 24) return t("remote.lastSeen.hoursAgo").replace("{n}", String(diffHour));
|
||
const d = new Date(ts);
|
||
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
||
const dd = String(d.getDate()).padStart(2, "0");
|
||
const hh = String(d.getHours()).padStart(2, "0");
|
||
const mi = String(d.getMinutes()).padStart(2, "0");
|
||
return `${mm}/${dd} ${hh}:${mi}`;
|
||
}
|