import type { EntityRatingSupportLevel, SubsonicItemGenre, SubsonicOpenArtistRef, SubsonicSong } from '../api/subsonicTypes'; import { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { useNavigate } from 'react-router-dom'; import { Play, Heart, X, ChevronLeft, Download, ListPlus, HardDriveDownload, Share2, Highlighter, Loader2, Shuffle } from 'lucide-react'; import { CoverArtImage } from '../cover/CoverArtImage'; import { useAlbumCoverRef } from '../cover/useLibraryCoverRef'; import { useCoverLightboxSrc } from '../cover/lightbox'; import { useTranslation } from 'react-i18next'; import { useIsMobile } from '../hooks/useIsMobile'; import { useAlbumDetailBack } from '../hooks/useAlbumDetailBack'; import { useThemeStore } from '../store/themeStore'; import StarRating from './StarRating'; import { copyEntityShareLink } from '../utils/share/copyEntityShareLink'; import { showToast } from '../utils/ui/toast'; import { isAlbumRecentlyAdded } from '../utils/albumRecency'; import { formatLongDuration } from '../utils/format/formatDuration'; import { formatMb } from '../utils/format/formatBytes'; import { sanitizeHtml } from '../utils/sanitizeHtml'; import { OpenArtistRefInline } from './OpenArtistRefInline'; import { tooltipAttrs } from './tooltipAttrs'; import { offlineActionPolicy, type OfflineActionPolicy } from '../utils/offline/offlineActionPolicy'; import { deriveAlbumGenreTags } from '../utils/library/genreTags'; import { genreColor } from '../utils/library/genreColor'; /** True when the album artist label means "no single artist" — `getArtistInfo` * has nothing meaningful to return for these, so the Artist Bio entry is hidden. */ function isVariousArtistsLabel(name: string | undefined | null): boolean { if (!name) return false; const trimmed = name.trim().toLowerCase(); return ( trimmed === 'various artists' || trimmed === 'various' || trimmed === 'va' || trimmed === 'multiple artists' || trimmed === 'verschiedene interpreten' || trimmed === 'verschiedene' ); } function BioModal({ bio, onClose }: { bio: string; onClose: () => void }) { const { t } = useTranslation(); return createPortal(