mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
23f7ba02d6
* feat(player-stats): local listening history tab with heatmap and summaries Record play sessions in library.sqlite when the library index is enabled, add Rust read APIs and Tauri commands for year/day aggregates, and ship the Player stats UI with session clustering, event-driven live refresh, and a notice when some servers are excluded from indexing. * test(player-stats): split play_session repo and expand test coverage Move the repository into play_session/ (completion, cluster, integration tests), add remap/purge/FK coverage in Rust, and cover ingestion gates plus live-refresh hooks on the frontend per spec v0.3. * docs(release): CHANGELOG and credits for player stats (PR #849) * fix(player-stats): satisfy tsc and clippy CI gates Use InternetRadioStation field names in the radio skip test and replace manual month/day range checks with RangeInclusive::contains.
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
export const PLAYER_STATS_RECENT_DAYS_LIMIT = 30;
|
|
|
|
export function localTodayIso(): string {
|
|
const d = new Date();
|
|
const y = d.getFullYear();
|
|
const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
return `${y}-${m}-${day}`;
|
|
}
|
|
|
|
function parseLocalDate(dateIso: string): Date {
|
|
const [y, m, d] = dateIso.split('-').map(Number);
|
|
return new Date(y, m - 1, d);
|
|
}
|
|
|
|
function startOfLocalDay(date: Date): Date {
|
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
}
|
|
|
|
/** Human-readable day header for recent-days accordion. */
|
|
export function formatPlayerStatsDayLabel(
|
|
dateIso: string,
|
|
t: (key: string, opts?: Record<string, unknown>) => string,
|
|
locale?: string,
|
|
): string {
|
|
const date = parseLocalDate(dateIso);
|
|
const today = startOfLocalDay(new Date());
|
|
const day = startOfLocalDay(date);
|
|
const diffDays = Math.round((today.getTime() - day.getTime()) / 86_400_000);
|
|
|
|
if (diffDays === 0) return t('statistics.playerDayToday');
|
|
if (diffDays === 1) return t('statistics.playerDayYesterday');
|
|
|
|
const fmt = new Intl.DateTimeFormat(locale, {
|
|
weekday: 'long',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: date.getFullYear() !== today.getFullYear() ? 'numeric' : undefined,
|
|
});
|
|
return fmt.format(date);
|
|
}
|