mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-22 03:42:55 -05:00
- lib/usePoll: one poll primitive that never overlaps itself and pauses while the window is hidden or a transfer to that console runs; Hardware (power/storage, PS5 clock, SMP meta), Notifications and the kernel log use it. - Engine log bridge appends a whole tick in one store update; the log store keeps an error counter so Sidebar/More stop re-filtering 500 lines. - Schedules fire whatever came due since the last tick (15 min catch-up), and record later ones as missed instead of silently skipping the day. - Screenshots / Video clips: Delete for the selection (confirm, engine delete like Files, thumbnail too), Retry on list errors, previews in the shared Modal. - formatDate: dates follow the app language. - Kernel log renders only the rows on screen (fixed-height windowing). - Logs: Open logs folder shows its error. Settings and Connection load lazily; the vendor-react chunk matches exact package names. - Modal reads onClose through a ref so an inline arrow doesn't re-grab focus on every render. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ub1mez9eaVtTF8pMEFXvA9
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
import { useLangStore } from "../state/lang";
|
|
|
|
/**
|
|
* Dates and times in the app's language, not the operating system's.
|
|
*
|
|
* A bare `toLocaleString()` formats with the system locale, so someone who
|
|
* runs the app in Japanese on an English Windows saw Japanese labels next to
|
|
* "10/9/2026, 3:04:05 PM". Every date on screen goes through here instead.
|
|
*
|
|
* Explicit fields rather than `dateStyle`/`timeStyle`: the oldest WebViews we
|
|
* ship to (safari13 target) don't know those options.
|
|
*/
|
|
export type DateFormat = "datetime" | "date" | "time" | "time-short";
|
|
|
|
const OPTIONS: Record<DateFormat, Intl.DateTimeFormatOptions> = {
|
|
datetime: { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" },
|
|
date: { year: "numeric", month: "short", day: "numeric" },
|
|
time: { hour: "numeric", minute: "2-digit", second: "2-digit" },
|
|
"time-short": { hour: "numeric", minute: "2-digit" },
|
|
};
|
|
|
|
const cache = new Map<string, Intl.DateTimeFormat>();
|
|
|
|
function formatter(lang: string, format: DateFormat): Intl.DateTimeFormat {
|
|
const key = `${lang}|${format}`;
|
|
let f = cache.get(key);
|
|
if (!f) {
|
|
let locale: string | undefined = lang;
|
|
try {
|
|
if (Intl.DateTimeFormat.supportedLocalesOf([lang]).length === 0) locale = undefined;
|
|
} catch {
|
|
locale = undefined;
|
|
}
|
|
f = new Intl.DateTimeFormat(locale, OPTIONS[format]);
|
|
cache.set(key, f);
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/** Format a Date or epoch milliseconds. Invalid input gives "". */
|
|
export function formatDate(
|
|
value: Date | number,
|
|
format: DateFormat = "datetime",
|
|
lang: string = useLangStore.getState().lang,
|
|
): string {
|
|
const d = typeof value === "number" ? new Date(value) : value;
|
|
if (Number.isNaN(d.getTime())) return "";
|
|
return formatter(lang, format).format(d);
|
|
}
|
|
|
|
/** `formatDate` bound to the current language; re-renders on a switch. */
|
|
export function useFormatDate(): (value: Date | number, format?: DateFormat) => string {
|
|
const lang = useLangStore((s) => s.lang);
|
|
return (value, format = "datetime") => formatDate(value, format, lang);
|
|
}
|