feat: updates

This commit is contained in:
qier222 2022-06-09 20:16:05 +08:00
parent d4d8dd817b
commit 4c90db789b
No known key found for this signature in database
GPG Key ID: 9C85007ED905F14D
14 changed files with 144 additions and 82 deletions

View File

@ -38,19 +38,21 @@ const ArtistRow = ({
<div className={className}>
{/* Title */}
{title && (
<h4 className='mb-6 text-12 font-medium uppercase dark:text-neutral-300 lg:text-14 lg:font-bold'>
<h4
className={cx(
'text-12 font-medium uppercase dark:text-neutral-300 lg:text-14',
'mx-2.5 mb-6 lg:mx-0 lg:font-bold'
)}
>
{title}
</h4>
)}
{/* Artists */}
{artists && (
<div className='no-scrollbar -ml-2.5 flex w-screen snap-x overflow-x-scroll lg:ml-auto lg:grid lg:w-auto lg:grid-cols-5 lg:gap-10'>
<div className='no-scrollbar flex snap-x overflow-x-scroll lg:grid lg:w-auto lg:grid-cols-5 lg:gap-10'>
{artists.map(artist => (
<div
className='mr-5 snap-start first-of-type:ml-2.5 last-of-type:mr-2.5 lg:mr-0'
key={artist.id}
>
<div className='snap-start px-2.5 lg:px-0' key={artist.id}>
<Artist artist={artist} key={artist.id} />
</div>
))}
@ -59,12 +61,9 @@ const ArtistRow = ({
{/* Placeholder */}
{placeholderRow && !artists && (
<div className='no-scrollbar -ml-2.5 flex w-screen overflow-x-scroll lg:ml-auto lg:grid lg:w-auto lg:grid-cols-5 lg:gap-10'>
<div className='no-scrollbar flex snap-x overflow-x-scroll lg:grid lg:w-auto lg:grid-cols-5 lg:gap-10'>
{[...new Array(placeholderRow * 5).keys()].map(i => (
<div
className='mr-5 first-of-type:ml-2.5 last-of-type:mr-2.5 lg:mr-0'
key={i}
>
<div className='snap-start px-2.5 lg:px-0' key={i}>
<div
className='aspect-square w-full rounded-full bg-white dark:bg-neutral-800'
style={{

View File

@ -1,9 +1,9 @@
import useBreakpoint from '@/web/hooks/useBreakpoint'
import { ReactQueryDevtools } from 'react-query/devtools'
import useIsMobile from '@/web/hooks/useIsMobile'
const Devtool = () => {
const breakpoint = useBreakpoint()
const isMobile = ['sm', 'md'].includes(breakpoint)
const isMobile = useIsMobile()
return (
<ReactQueryDevtools
initialIsOpen={false}
@ -12,7 +12,7 @@ const Devtool = () => {
position: 'fixed',
bottom: isMobile ? 'auto' : 0,
right: 0,
top: isMobile ? 0 : 1,
top: isMobile ? 0 : 'auto',
left: 'auto',
},
}}

View File

@ -14,7 +14,7 @@ const LayoutMobile = () => {
return (
<div id='layout' className='select-none bg-white pb-32 pt-3 dark:bg-black'>
<main className='min-h-screen overflow-y-auto overflow-x-hidden px-2.5 pb-16'>
<main className='min-h-screen overflow-y-auto overflow-x-hidden pb-16'>
<TopbarMobile />
<Router />
</main>
@ -47,6 +47,19 @@ const LayoutMobile = () => {
<MenuBar />
</div>
{/* Notch background */}
{isIOS && isSafari && isPWA && (
<div
className={cx(
'fixed left-0 right-0 bg-black/30 backdrop-blur-sm',
css`
top: -50px;
height: 50px;
`
)}
></div>
)}
</div>
)
}

View File

@ -67,7 +67,7 @@ const PlayLikedSongsCard = () => {
return (
<div
className={cx(
'flex flex-col justify-between rounded-24 p-8 dark:bg-night-600',
'mx-2.5 flex flex-col justify-between rounded-24 p-8 dark:bg-white/10 lg:mx-0',
css`
height: 322px;
`

View File

@ -4,6 +4,7 @@ const Tabs = ({
tabs,
value,
onChange,
className,
}: {
tabs: {
id: string
@ -11,9 +12,10 @@ const Tabs = ({
}[]
value: string
onChange: (id: string) => void
className?: string
}) => {
return (
<div className='no-scrollbar flex overflow-y-auto'>
<div className={cx('no-scrollbar flex overflow-y-auto', className)}>
{tabs.map(tab => (
<div
key={tab.id}

View File

@ -4,7 +4,7 @@ import SettingsButton from './SettingsButton'
const TopbarMobile = () => {
return (
<div className='mb-5 mt-7 flex'>
<div className='mb-5 mt-7 flex px-2.5'>
<div className='flex-grow'>
<SearchBox />
</div>

View File

@ -5,6 +5,7 @@ import { player } from '@/web/store'
import { useSnapshot } from 'valtio'
import Wave from './Wave'
import Icon from '@/web/components/Icon'
import useIsMobile from '@/web/hooks/useIsMobile'
const TrackList = ({
tracks,
@ -20,9 +21,14 @@ const TrackList = ({
() => playerSnapshot.track,
[playerSnapshot.track]
)
const isMobile = useIsMobile()
const handleClick = (e: React.MouseEvent<HTMLElement>, trackID: number) => {
if (e.detail === 2) onPlay?.(trackID)
if (isMobile) {
onPlay?.(trackID)
} else {
if (e.detail === 2) onPlay?.(trackID)
}
}
const playing = useMemo(
@ -38,7 +44,12 @@ const TrackList = ({
onClick={e => handleClick(e, track.id)}
className='group relative flex items-center py-2 text-16 font-medium text-neutral-200 transition duration-300 ease-in-out'
>
<div className='mr-6'>{String(track.no).padStart(2, '0')}</div>
{/* Track no */}
<div className='mr-3 lg:mr-6'>
{String(track.no).padStart(2, '0')}
</div>
{/* Track name */}
<div className='flex flex-grow items-center'>
{track.name}
{playingTrack?.id === track.id && (
@ -47,7 +58,9 @@ const TrackList = ({
</div>
)}
</div>
<div className='mr-12 flex opacity-0 transition-opacity group-hover:opacity-100'>
{/* Desktop context menu */}
<div className='mr-12 hidden opacity-0 transition-opacity group-hover:opacity-100 lg:flex'>
<div className='mr-3 flex h-10 w-10 items-center justify-center rounded-full bg-brand-600 text-white/80'>
{/* <Icon name='play' className='h-7 w-7' /> */}
</div>
@ -58,7 +71,14 @@ const TrackList = ({
{/* <Icon name='play' className='h-7 w-7' /> */}
</div>
</div>
<div className='text-right'>
{/* Mobile menu */}
<div className='lg:hidden'>
<div className='h-10 w-10 rounded-full bg-night-900'></div>
</div>
{/* Track duration */}
<div className='hidden text-right lg:block'>
{formatDuration(track.dt, 'en', 'hh:mm:ss')}
</div>
</div>

View File

@ -4,6 +4,7 @@ import Icon from '@/web/components/Icon'
import dayjs from 'dayjs'
import { useMemo } from 'react'
import Image from './Image'
import useIsMobile from '@/web/hooks/useIsMobile'
const TrackListHeader = ({
album,
@ -18,18 +19,22 @@ const TrackListHeader = ({
const duration = album?.songs?.reduce((acc, cur) => acc + cur.dt, 0) || 0
return formatDuration(duration, 'en', 'hh[hr] mm[min]')
}, [album?.songs])
const isMobile = useIsMobile()
const cover = album?.picUrl || playlist?.coverImgUrl || ''
return (
<div
className={cx(
'grid grid-rows-1 gap-10',
css`
grid-template-columns: 318px auto;
`
'mx-2.5 rounded-48 p-8 dark:bg-white/10',
'lg:mx-0 lg:grid lg:grid-rows-1 lg:gap-10 lg:rounded-none lg:p-0 lg:dark:bg-transparent',
!isMobile &&
css`
grid-template-columns: 318px auto;
`
)}
>
{/* Cover */}
<Image
className='z-10 aspect-square w-full rounded-24'
src={resizeImage(cover, 'lg')}
@ -37,57 +42,77 @@ const TrackListHeader = ({
/>
{/* Blur bg */}
<img
className={cx(
'absolute z-0 object-cover opacity-70',
css`
top: -400px;
left: -370px;
width: 1572px;
height: 528px;
filter: blur(256px) saturate(1.2);
`
)}
src={resizeImage(cover, 'sm')}
/>
{!isMobile && (
<img
className={cx(
'absolute z-0 object-cover opacity-70',
css`
top: -400px;
left: -370px;
width: 1572px;
height: 528px;
filter: blur(256px) saturate(1.2);
`
)}
src={resizeImage(cover, 'sm')}
/>
)}
<div className='flex flex-col justify-between'>
<div>
<div className='text-36 font-medium dark:text-neutral-100'>
{/* Name */}
<div className='mt-2.5 text-28 font-semibold dark:text-night-50 lg:mt-0 lg:text-36 lg:font-medium'>
{album?.name || playlist?.name}
</div>
<div className='mt-6 text-24 font-medium dark:text-neutral-600'>
{/* Creator */}
<div className='mt-2.5 text-24 font-medium dark:text-night-400 lg:mt-6'>
{album?.artist.name || playlist?.creator.nickname}
</div>
<div className='mt-1 flex items-center text-14 font-bold dark:text-neutral-600'>
{/* Extra info */}
<div className='mt-1 flex items-center text-12 font-medium dark:text-night-400 lg:text-14 lg:font-bold'>
{/* Album info */}
{!!album && (
<>
{album?.mark === 1056768 && (
<Icon name='explicit' className='mb-px mr-1 h-3.5 w-3.5 ' />
<Icon
name='explicit'
className='mb-px mr-1 h-3 w-3 lg:h-3.5 lg:w-3.5 '
/>
)}{' '}
{dayjs(album?.publishTime || 0).year()} · {album?.songs.length}{' '}
Tracks, {albumDuration}
tracks, {albumDuration}
</>
)}
{/* Playlist info */}
{!!playlist && (
<>
Updated at {formatDate(playlist?.updateTime || 0, 'en')} ·{' '}
{playlist.trackCount} Tracks
{playlist.trackCount} tracks
</>
)}
</div>
<div className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-neutral-600'>
{album?.description || playlist?.description}
</div>
{/* Description */}
{!isMobile && (
<div className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-night-400 '>
{album?.description || playlist?.description}
</div>
)}
</div>
<div className='z-10 flex'>
{/* Actions */}
<div className='mt-11 flex items-end justify-between lg:z-10 lg:mt-4 lg:justify-start'>
<div className='flex items-end'>
<button className='mr-2.5 h-14 w-14 rounded-full dark:bg-white/10 lg:mr-6 lg:h-[72px] lg:w-[72px]'></button>
<button className='h-14 w-14 rounded-full dark:bg-white/10 lg:mr-6 lg:h-[72px] lg:w-[72px]'></button>
</div>
<button
onClick={() => onPlay()}
className='h-[72px] w-[170px] rounded-full dark:bg-brand-700'
className='h-14 w-[125px] rounded-full dark:bg-brand-700 lg:h-[72px] lg:w-[170px]'
></button>
<button className='ml-6 h-[72px] w-[72px] rounded-full dark:bg-white/10'></button>
<button className='ml-6 h-[72px] w-[72px] rounded-full dark:bg-white/10'></button>
</div>
</div>
</div>

View File

@ -1,10 +1,10 @@
import { createBreakpoint } from 'react-use'
const useBreakpoint = createBreakpoint({
sm: 767,
md: 1023,
lg: 1279,
xl: 1535,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
'2xl': 1536,
}) as () => 'sm' | 'md' | 'lg' | 'xl' | '2xl'

View File

@ -4,8 +4,10 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/public/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' www.googletagmanager.com;" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<title>YesPlayMusic</title>
</head>

View File

@ -25,7 +25,9 @@ const MoreByArtist = ({ album }: { album?: Album }) => {
album =>
['专辑', 'EP/Single', 'EP'].includes(album.type) && album.size > 1
)
const singles = allReleases.filter(album => album.type === 'Single')
const singles = allReleases.filter(
album => album.type === 'Single' || album.size === 1
)
const qualifiedAlbums = [...filteredAlbums, ...singles]
@ -63,17 +65,10 @@ const MoreByArtist = ({ album }: { album?: Album }) => {
return (
<div>
{/* Dividing line */}
<div
className={cx(
'h-px bg-white/20',
css`
margin: 30px 0;
`
)}
></div>
<div className={cx('mx-2.5 my-7.5 h-px bg-white/10 lg:mx-0')}></div>
{/* Title */}
<div className='mb-5 text-14 font-bold text-neutral-300'>
<div className='mx-2.5 mb-5 text-14 font-bold text-neutral-300 lg:mx-0'>
MORE BY{' '}
<NavLink
to={`/artist/${album?.artist.id}`}
@ -83,7 +78,7 @@ const MoreByArtist = ({ album }: { album?: Album }) => {
</NavLink>
</div>
<CoverRow albums={filteredAlbums} />
<CoverRow albums={filteredAlbums} className='mx-2.5 lg:mx-0' />
</div>
)
}
@ -120,7 +115,7 @@ const Album = () => {
<TrackListHeader album={album?.album} onPlay={onPlay} />
<TrackList
tracks={tracks?.songs || album?.songs || album?.album.songs}
className='z-10 mt-10'
className='z-10 mx-2.5 mt-3 lg:mx-0 lg:mt-10'
onPlay={onPlay}
/>
<MoreByArtist album={album?.album} />

View File

@ -78,13 +78,14 @@ const My = () => {
tabs={tabs}
value={selectedTab}
onChange={(id: string) => setSelectedTab(id)}
className='px-2.5 lg:px-0'
/>
<CoverRow
playlists={
selectedTab === 'playlists' ? playlists?.playlist : undefined
}
albums={selectedTab === 'albums' ? albums?.data : undefined}
className='mt-6'
className='mt-6 px-2.5 lg:px-0'
/>
</div>
</div>

View File

@ -138,4 +138,5 @@ a {
html {
background-color: black;
min-height: calc(100% + env(safe-area-inset-top));
}

View File

@ -28,18 +28,7 @@ module.exports = {
900: '#7EB000',
},
day: {
100: '#FCFCFC',
200: '#F8F8F8',
300: '#F4F4F4',
400: '#F0F0F0',
500: '#EDEDED',
600: '#E9E9E9',
700: '#E5E5E5',
800: '#E2E2E2',
900: '#DEDEDE',
},
night: {
50: '#545454',
50: '#B6B6B6',
100: '#535353',
200: '#505050',
300: '#484848',
@ -48,6 +37,17 @@ module.exports = {
600: '#0E0E0E',
700: '#060606',
800: '#020202',
},
night: {
50: '#BFBFBF',
100: '#A8A8A8',
200: '#7B7B7B',
300: '#606060',
400: '#585858',
500: '#4A4A4A',
600: '#464646',
700: '#3F3F3F',
800: '#373737',
900: '#313131',
},
neutral: {
@ -82,10 +82,14 @@ module.exports = {
12: '12px',
20: '20px',
24: '24px',
48: '48px',
},
fontFamily: {
mono: ['Roboto Mono', 'ui-monospace'],
},
margin: {
7.5: '1.875rem',
},
},
},
variants: {},