mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
f7a721b7b1
Per bcorporaal's request in #252: album-oriented listeners want a cleaner artist page focused on the album grid, with the freedom to reorder/hide bio, top tracks, and similar artists. Five sections are now reorder- and toggle-able from Settings → Personalisation → "Artist page sections": Bio, Top tracks, Similar artists, Albums, Also featured on. The fixed header block (artist photo, name, action buttons) stays at the top. Implementation reuses the existing sidebar customizer pattern: - new `src/store/artistLayoutStore.ts` modelled on `sidebarStore.ts` with the same persist + onRehydrateStorage migration so future-added sections don't silently disappear from existing users' configs. - `ArtistDetail.tsx` render block refactored from a flat sequence of conditional JSX into a `renderableSectionIds.map()` driven by the store. The "first rendered section gets marginTop: 0" rule now works regardless of the configured order — both hidden-by-toggle and empty-data sections are filtered before the layout decides what's first. - `ArtistLayoutCustomizer` component in Settings, lifted from `LyricsSourcesCustomizer` (drag via `useDragSource` + `psy-drop` event, per-row toggle, reset button). - 7 new i18n keys per locale across all 8 languages. The store hook MUST stay above the loading / !artist early returns — otherwise React's hook call order mismatches between renders and the component fails silently. Lesson learned during the refactor. Co-authored-by: Psychotoxical <dev@psysonic.app> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
|
|
export type ArtistSectionId = 'bio' | 'topTracks' | 'similar' | 'albums' | 'featured';
|
|
|
|
export interface ArtistSectionConfig {
|
|
id: ArtistSectionId;
|
|
visible: boolean;
|
|
}
|
|
|
|
/**
|
|
* Default order matches the historical layout of `pages/ArtistDetail.tsx` so
|
|
* existing users see no change until they explicitly customise it.
|
|
*/
|
|
export const DEFAULT_ARTIST_SECTIONS: ArtistSectionConfig[] = [
|
|
{ id: 'bio', visible: true },
|
|
{ id: 'topTracks', visible: true },
|
|
{ id: 'similar', visible: true },
|
|
{ id: 'albums', visible: true },
|
|
{ id: 'featured', visible: true },
|
|
];
|
|
|
|
interface ArtistLayoutStore {
|
|
sections: ArtistSectionConfig[];
|
|
setSections: (sections: ArtistSectionConfig[]) => void;
|
|
toggleSection: (id: ArtistSectionId) => void;
|
|
reset: () => void;
|
|
}
|
|
|
|
export const useArtistLayoutStore = create<ArtistLayoutStore>()(
|
|
persist(
|
|
(set) => ({
|
|
sections: DEFAULT_ARTIST_SECTIONS,
|
|
|
|
setSections: (sections) => set({ sections }),
|
|
|
|
toggleSection: (id) => set((s) => ({
|
|
sections: s.sections.map(sec => sec.id === id ? { ...sec, visible: !sec.visible } : sec),
|
|
})),
|
|
|
|
reset: () => set({ sections: DEFAULT_ARTIST_SECTIONS }),
|
|
}),
|
|
{
|
|
name: 'psysonic_artist_layout',
|
|
onRehydrateStorage: () => (state) => {
|
|
if (!state) return;
|
|
// Sanitize: drop null/corrupt entries, append any new sections that
|
|
// were added in a later release so they don't silently disappear.
|
|
const knownIds = new Set(DEFAULT_ARTIST_SECTIONS.map(s => s.id));
|
|
const safe = (state.sections ?? [])
|
|
.filter((s): s is ArtistSectionConfig => s != null && typeof s.id === 'string' && knownIds.has(s.id as ArtistSectionId));
|
|
const seen = new Set(safe.map(s => s.id));
|
|
const missing = DEFAULT_ARTIST_SECTIONS.filter(s => !seen.has(s.id));
|
|
state.sections = missing.length > 0 ? [...safe, ...missing] : safe;
|
|
},
|
|
}
|
|
)
|
|
);
|