mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
feat(player-stats): local listening history tab with heatmap and summaries (#849)
* 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.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user