mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-17 13:48:02 +00:00
feat: updates
This commit is contained in:
parent
c6c59b2cd9
commit
7ce516877e
63 changed files with 6591 additions and 1107 deletions
|
|
@ -28,12 +28,7 @@ const ArtistInline = ({
|
|||
if (!artists) return <div></div>
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
!className?.includes('line-clamp') && 'line-clamp-1',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className={cx(!className?.includes('line-clamp') && 'line-clamp-1', className)}>
|
||||
{artists.map((artist, index) => (
|
||||
<span key={`${artist.id}-${artist.name}`}>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { prefetchAlbum } from '@/web/api/hooks/useAlbum'
|
|||
import { prefetchPlaylist } from '@/web/api/hooks/usePlaylist'
|
||||
import { memo, useCallback } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import ArtistInline from './ArtistsInLine'
|
||||
import ArtistInline from './ArtistsInline'
|
||||
|
||||
type ItemTitle = undefined | 'name'
|
||||
type ItemSubTitle = undefined | 'artist' | 'year'
|
||||
|
|
@ -56,15 +56,9 @@ const Album = ({
|
|||
onMouseOver={prefetch}
|
||||
/>
|
||||
{title && (
|
||||
<div className='line-clamp-2 mt-2 text-14 font-medium text-neutral-300'>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
{subtitle && (
|
||||
<div className='mt-1 text-14 font-medium text-neutral-700'>
|
||||
{subtitle}
|
||||
</div>
|
||||
<div className='line-clamp-2 mt-2 text-14 font-medium text-neutral-300'>{title}</div>
|
||||
)}
|
||||
{subtitle && <div className='mt-1 text-14 font-medium text-neutral-700'>{subtitle}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -107,21 +101,12 @@ const CoverRow = ({
|
|||
return (
|
||||
<div className={className}>
|
||||
{/* Title */}
|
||||
{title && (
|
||||
<h4 className='mb-6 text-14 font-bold uppercase dark:text-neutral-300'>
|
||||
{title}
|
||||
</h4>
|
||||
)}
|
||||
{title && <h4 className='mb-6 text-14 font-bold uppercase dark:text-neutral-300'>{title}</h4>}
|
||||
|
||||
{/* Items */}
|
||||
<div className='grid grid-cols-3 gap-4 lg:gap-6 xl:grid-cols-4 2xl:grid-cols-5'>
|
||||
{albums?.map(album => (
|
||||
<Album
|
||||
key={album.id}
|
||||
album={album}
|
||||
itemTitle={itemTitle}
|
||||
itemSubtitle={itemSubtitle}
|
||||
/>
|
||||
<Album key={album.id} album={album} itemTitle={itemTitle} itemSubtitle={itemSubtitle} />
|
||||
))}
|
||||
{playlists?.map(playlist => (
|
||||
<Playlist key={playlist.id} playlist={playlist} />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Main from '@/web/components/Main'
|
|||
import Player from '@/web/components/Player'
|
||||
import MenuBar from '@/web/components/MenuBar'
|
||||
import Topbar from '@/web/components/Topbar/TopbarDesktop'
|
||||
import { cx } from '@emotion/css'
|
||||
import { css, cx } from '@emotion/css'
|
||||
import player from '@/web/states/player'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import Login from './Login'
|
||||
|
|
@ -22,7 +22,10 @@ const Layout = () => {
|
|||
id='layout'
|
||||
className={cx(
|
||||
'relative grid h-screen select-none overflow-hidden bg-white dark:bg-black',
|
||||
window.env?.isElectron && !fullscreen && 'rounded-24'
|
||||
window.env?.isElectron && !fullscreen && 'rounded-24',
|
||||
css`
|
||||
min-width: 720px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<BlurBackground />
|
||||
|
|
@ -40,9 +43,7 @@ const Layout = () => {
|
|||
|
||||
{(window.env?.isWindows ||
|
||||
window.env?.isLinux ||
|
||||
window.localStorage.getItem('showWindowsTitleBar') === 'true') && (
|
||||
<TitleBar />
|
||||
)}
|
||||
window.localStorage.getItem('showWindowsTitleBar') === 'true') && <TitleBar />}
|
||||
|
||||
<ContextMenus />
|
||||
|
||||
|
|
|
|||
|
|
@ -23,25 +23,19 @@ const LayoutMobile = () => {
|
|||
<Router />
|
||||
</main>
|
||||
<div
|
||||
className={cx(
|
||||
'fixed bottom-0 left-0 right-0 z-20 pt-3 dark:bg-black',
|
||||
css`
|
||||
padding-bottom: calc(
|
||||
className={cx('fixed bottom-0 left-0 right-0 z-20 pt-3 dark:bg-black')}
|
||||
style={{
|
||||
paddingBottom: `calc(
|
||||
${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'} + 0.75rem
|
||||
);
|
||||
`
|
||||
)}
|
||||
)`,
|
||||
}}
|
||||
>
|
||||
{showPlayer && (
|
||||
<div
|
||||
className={cx(
|
||||
'absolute left-7 right-7 z-20',
|
||||
css`
|
||||
top: calc(
|
||||
-100% - 6px + ${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'}
|
||||
);
|
||||
`
|
||||
)}
|
||||
className={cx('absolute left-7 right-7 z-20')}
|
||||
style={{
|
||||
top: `calc(-100% - 6px + ${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'})`,
|
||||
}}
|
||||
>
|
||||
<Player />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,7 @@ import persistedUiStates from '@/web/states/persistedUiStates'
|
|||
import useUser from '@/web/api/hooks/useUser'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const OR = ({
|
||||
children,
|
||||
onClick,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
onClick: () => void
|
||||
}) => {
|
||||
const OR = ({ children, onClick }: { children: React.ReactNode; onClick: () => void }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
|
@ -125,10 +119,9 @@ const Login = () => {
|
|||
<motion.div
|
||||
animate={animateCard}
|
||||
className={cx(
|
||||
'relative rounded-48 bg-white/10 p-9',
|
||||
'relative h-fit rounded-48 bg-white/10 p-9',
|
||||
css`
|
||||
width: 392px;
|
||||
height: fit-content;
|
||||
`
|
||||
)}
|
||||
>
|
||||
|
|
@ -136,9 +129,7 @@ const Login = () => {
|
|||
{cardType === 'phone/email' && <LoginWithPhoneOrEmail />}
|
||||
|
||||
<OR onClick={handleSwitchCard}>
|
||||
{cardType === 'qrCode'
|
||||
? t`auth.use-phone-or-email`
|
||||
: t`auth.scan-qr-code`}
|
||||
{cardType === 'qrCode' ? t`auth.use-phone-or-email` : t`auth.scan-qr-code`}
|
||||
</OR>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import useUserLikedTracksIDs, {
|
||||
useMutationLikeATrack,
|
||||
} from '@/web/api/hooks/useUserLikedTracksIDs'
|
||||
import useUserLikedTracksIDs, { useMutationLikeATrack } from '@/web/api/hooks/useUserLikedTracksIDs'
|
||||
import player from '@/web/states/player'
|
||||
import { resizeImage } from '@/web/utils/common'
|
||||
|
||||
|
|
@ -30,8 +28,7 @@ const PlayingTrack = () => {
|
|||
[playerSnapshot.trackListSource]
|
||||
)
|
||||
|
||||
const hasListSource =
|
||||
playerSnapshot.mode !== PlayerMode.FM && trackListSource?.type
|
||||
const hasListSource = playerSnapshot.mode !== PlayerMode.FM && trackListSource?.type
|
||||
|
||||
const toTrackListSource = () => {
|
||||
if (!hasListSource) return
|
||||
|
|
@ -76,16 +73,10 @@ const LikeButton = ({ track }: { track: Track | undefined | null }) => {
|
|||
|
||||
return (
|
||||
<div className='mr-1 '>
|
||||
<IconButton
|
||||
onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}
|
||||
>
|
||||
<IconButton onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}>
|
||||
<Icon
|
||||
className='h-6 w-6 text-white'
|
||||
name={
|
||||
track?.id && userLikedSongs?.ids?.includes(track.id)
|
||||
? 'heart'
|
||||
: 'heart-outline'
|
||||
}
|
||||
name={track?.id && userLikedSongs?.ids?.includes(track.id) ? 'heart' : 'heart-outline'}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
|
|
@ -101,10 +92,7 @@ const Controls = () => {
|
|||
return (
|
||||
<div className='flex items-center justify-center gap-2 text-white'>
|
||||
{mode === PlayerMode.TrackList && (
|
||||
<IconButton
|
||||
onClick={() => track && player.prevTrack()}
|
||||
disabled={!track}
|
||||
>
|
||||
<IconButton onClick={() => track && player.prevTrack()} disabled={!track}>
|
||||
<Icon className='h-6 w-6' name='previous' />
|
||||
</IconButton>
|
||||
)}
|
||||
|
|
@ -120,11 +108,7 @@ const Controls = () => {
|
|||
>
|
||||
<Icon
|
||||
className='h-7 w-7'
|
||||
name={
|
||||
[PlayerState.Playing, PlayerState.Loading].includes(state)
|
||||
? 'pause'
|
||||
: 'play'
|
||||
}
|
||||
name={[PlayerState.Playing, PlayerState.Loading].includes(state) ? 'pause' : 'play'}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => track && player.nextTrack()} disabled={!track}>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'
|
|||
import player from '@/web/states/player'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import ArtistInline from '@/web/components/ArtistsInline'
|
||||
import ArtistInline from '../ArtistsInline'
|
||||
import persistedUiStates from '@/web/states/persistedUiStates'
|
||||
import Controls from './Controls'
|
||||
import Cover from './Cover'
|
||||
|
|
@ -34,9 +34,7 @@ const NowPlaying = () => {
|
|||
{/* Info & Controls */}
|
||||
<div className='m-3 flex flex-col items-center rounded-20 bg-white/60 p-5 font-medium backdrop-blur-3xl dark:bg-black/70'>
|
||||
{/* Track Info */}
|
||||
<div className='line-clamp-1 text-lg text-black dark:text-white'>
|
||||
{track?.name}
|
||||
</div>
|
||||
<div className='line-clamp-1 text-lg text-black dark:text-white'>{track?.name}</div>
|
||||
<ArtistInline
|
||||
artists={track?.ar || []}
|
||||
className='text-black/30 dark:text-white/30'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const Tabs = ({
|
|||
value,
|
||||
onChange,
|
||||
className,
|
||||
style,
|
||||
}: {
|
||||
tabs: {
|
||||
id: string
|
||||
|
|
@ -13,9 +14,10 @@ const Tabs = ({
|
|||
value: string
|
||||
onChange: (id: string) => void
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}) => {
|
||||
return (
|
||||
<div className={cx('no-scrollbar flex overflow-y-auto', className)}>
|
||||
<div className={cx('no-scrollbar flex overflow-y-auto', className)} style={style}>
|
||||
{tabs.map(tab => (
|
||||
<div
|
||||
key={tab.id}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,9 @@ const Background = () => {
|
|||
transition={{ ease }}
|
||||
className={cx(
|
||||
'absolute inset-0 z-0 bg-contain bg-repeat-x',
|
||||
window.env?.isElectron && 'rounded-t-24',
|
||||
css`
|
||||
background-image: url(${topbarBackground});
|
||||
`
|
||||
window.env?.isElectron && 'rounded-t-24'
|
||||
)}
|
||||
style={{ backgroundImage: `url(${topbarBackground})` }}
|
||||
></motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
|
|
|||
|
|
@ -67,14 +67,14 @@ const Info = ({
|
|||
)}
|
||||
|
||||
{/* Description */}
|
||||
{!isMobile && (
|
||||
{!isMobile && description && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-white/40'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: description || '',
|
||||
__html: description,
|
||||
}}
|
||||
></motion.div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue