mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
fix(statistics): keep player stats tab visible without local index (#851)
* fix(statistics): keep player stats tab visible without local index Show the tab always and explain that player statistics require the local library index, with a link to library settings, instead of hiding the tab. * docs(release): CHANGELOG and credits for player stats tab UX (PR #851) * docs(credits): drop minor library index and player stats tab fixes Per team policy, small UX fixes (PR #850, #851) stay in CHANGELOG only.
This commit is contained in:
@@ -9,7 +9,9 @@ import {
|
||||
type PlaySessionYearSummary,
|
||||
} from '../../api/library';
|
||||
import { usePlayerStatsLiveRefresh } from '../../hooks/usePlayerStatsLiveRefresh';
|
||||
import { usePlayerStatsRecordingEnabled } from '../../hooks/usePlayerStatsRecordingEnabled';
|
||||
import PlayerStatsHeatmap from './PlayerStatsHeatmap';
|
||||
import PlayerStatsIndexRequiredNotice from './PlayerStatsIndexRequiredNotice';
|
||||
import PlayerStatsPartialIndexNotice from './PlayerStatsPartialIndexNotice';
|
||||
import PlayerStatsRecentDays from './PlayerStatsRecentDays';
|
||||
import { formatPlayerStatsListeningTotal } from '../../utils/format/formatHumanDuration';
|
||||
@@ -18,6 +20,7 @@ const currentCalendarYear = () => new Date().getFullYear();
|
||||
|
||||
export default function PlayerStatisticsPanel() {
|
||||
const { t } = useTranslation();
|
||||
const recordingEnabled = usePlayerStatsRecordingEnabled();
|
||||
const [year, setYear] = useState(currentCalendarYear);
|
||||
const [yearBounds, setYearBounds] = useState<PlaySessionYearBounds | null>(null);
|
||||
const [summary, setSummary] = useState<PlaySessionYearSummary | null>(null);
|
||||
@@ -28,6 +31,13 @@ export default function PlayerStatisticsPanel() {
|
||||
const [liveRefreshKey, setLiveRefreshKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!recordingEnabled) {
|
||||
setLoading(false);
|
||||
setSummary(null);
|
||||
setDayCounts(new Map());
|
||||
setSelectedDate(null);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
setSelectedDate(null);
|
||||
@@ -52,9 +62,10 @@ export default function PlayerStatisticsPanel() {
|
||||
}
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, [year]);
|
||||
}, [year, recordingEnabled]);
|
||||
|
||||
const refreshLive = useCallback(async () => {
|
||||
if (!recordingEnabled) return;
|
||||
try {
|
||||
const [s, heat] = await Promise.all([
|
||||
libraryGetPlayerStatsYearSummary(year),
|
||||
@@ -66,10 +77,18 @@ export default function PlayerStatisticsPanel() {
|
||||
} catch {
|
||||
/* ignore transient read errors during live refresh */
|
||||
}
|
||||
}, [year]);
|
||||
}, [year, recordingEnabled]);
|
||||
|
||||
usePlayerStatsLiveRefresh(refreshLive);
|
||||
|
||||
if (!recordingEnabled) {
|
||||
return (
|
||||
<div className="stats-page">
|
||||
<PlayerStatsIndexRequiredNotice />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const empty = !loading && (summary?.trackPlayCount ?? 0) === 0;
|
||||
const calYear = currentCalendarYear();
|
||||
const maxNavYear = yearBounds?.maxYear != null
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Info } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export default function PlayerStatsIndexRequiredNotice() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="settings-hint settings-hint-info player-stats-partial-index-notice" role="status">
|
||||
<Info size={16} aria-hidden style={{ flexShrink: 0, marginTop: 2 }} />
|
||||
<span>
|
||||
{t('statistics.playerIndexRequired')}
|
||||
{' '}
|
||||
<button
|
||||
type="button"
|
||||
className="player-stats-partial-index-link"
|
||||
onClick={() => navigate('/settings', { state: { tab: 'library' } })}
|
||||
>
|
||||
{t('statistics.playerPartialIndexSettings')}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useLibraryIndexStore } from '../../store/libraryIndexStore';
|
||||
|
||||
export default function StatisticsTabBar() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const servers = useAuthStore(s => s.servers);
|
||||
const masterEnabled = useLibraryIndexStore(s => s.masterEnabled);
|
||||
const syncExcludedByServer = useLibraryIndexStore(s => s.syncExcludedByServer);
|
||||
|
||||
const showPlayerTab =
|
||||
masterEnabled && servers.some(s => syncExcludedByServer[s.id] !== true);
|
||||
if (!showPlayerTab) return null;
|
||||
|
||||
const isPlayer = location.pathname === '/player-stats';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user