From 36603dc3a0ccdbbf8dbab1d43608b24e00b28864 Mon Sep 17 00:00:00 2001 From: qier222 Date: Wed, 23 Mar 2022 01:21:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=AD=8C=E6=89=8B?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/main/cache.ts | 12 ++ packages/main/database.ts | 1 + .../renderer/src/assets/icons/dislike.svg | 2 +- .../renderer/src/assets/icons/music-note.svg | 5 +- .../renderer/src/components/ArtistsInline.tsx | 16 +- packages/renderer/src/components/Button.tsx | 2 +- packages/renderer/src/components/CoverRow.tsx | 4 +- packages/renderer/src/components/Router.tsx | 9 +- packages/renderer/src/components/Sidebar.tsx | 4 +- packages/renderer/src/components/Slider.tsx | 2 +- .../renderer/src/components/TracksGrid.tsx | 43 ++++- packages/renderer/src/hooks/useArtist.ts | 18 ++- packages/renderer/src/pages/Album.tsx | 2 +- packages/renderer/src/pages/Artist.tsx | 148 ++++++++++++++++++ packages/renderer/src/pages/Login.tsx | 6 +- packages/renderer/src/utils/player.ts | 1 - 16 files changed, 247 insertions(+), 28 deletions(-) create mode 100644 packages/renderer/src/pages/Artist.tsx diff --git a/packages/main/cache.ts b/packages/main/cache.ts index e72670d..c2b5e33 100644 --- a/packages/main/cache.ts +++ b/packages/main/cache.ts @@ -44,6 +44,11 @@ export async function setCache(api: string, data: any, query: any) { db.set(ModelNames.PLAYLIST, Number(data.playlist.id), data) break } + case 'artists': { + if (!data.artist) return + db.set(ModelNames.ARTIST, Number(data.artist.id), data) + break + } case 'artist/album': { if (!data.hotAlbums) return db.set(ModelNames.ARTIST_ALBUMS, Number(data.artist.id), data) @@ -117,6 +122,13 @@ export function getCache( if (playlist?.json) return JSON.parse(playlist.json) break } + case 'artists': { + if (!query?.id) return + const artist = db.get(ModelNames.ARTIST, Number(query?.id)) as any + if (checkIsExpired && isCacheExpired(artist?.updateAt, 30)) return + if (artist?.json) return JSON.parse(artist.json) + break + } case 'artist/album': { if (!query?.id) return const artistAlbums = db.get( diff --git a/packages/main/database.ts b/packages/main/database.ts index 05a24c0..58d4d8d 100644 --- a/packages/main/database.ts +++ b/packages/main/database.ts @@ -30,6 +30,7 @@ const RegularSchemas = [ ModelNames.PLAYLIST, ModelNames.ALBUM, ModelNames.TRACK, + ModelNames.ARTIST, ].map(name => ({ primaryKey: 'id', name, diff --git a/packages/renderer/src/assets/icons/dislike.svg b/packages/renderer/src/assets/icons/dislike.svg index 394de56..967008d 100644 --- a/packages/renderer/src/assets/icons/dislike.svg +++ b/packages/renderer/src/assets/icons/dislike.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/renderer/src/assets/icons/music-note.svg b/packages/renderer/src/assets/icons/music-note.svg index bbebf35..c6a6c96 100644 --- a/packages/renderer/src/assets/icons/music-note.svg +++ b/packages/renderer/src/assets/icons/music-note.svg @@ -1 +1,4 @@ - + + + + diff --git a/packages/renderer/src/components/ArtistsInline.tsx b/packages/renderer/src/components/ArtistsInline.tsx index 38648dc..e1883e6 100644 --- a/packages/renderer/src/components/ArtistsInline.tsx +++ b/packages/renderer/src/components/ArtistsInline.tsx @@ -1,17 +1,29 @@ const ArtistInline = ({ artists, className, + disableLink, }: { artists: Artist[] className?: string + disableLink?: boolean }) => { if (!artists) return
+ const navigate = useNavigate() + const handleClick = () => { + disableLink ? null : navigate(`/artist/${artists[0].id}`) + } + return (
{artists.map((artist, index) => ( - - + + {artist.name} {index < artists.length - 1 ? ', ' : ''}  diff --git a/packages/renderer/src/components/Button.tsx b/packages/renderer/src/components/Button.tsx index 10f1205..d7a3b22 100644 --- a/packages/renderer/src/components/Button.tsx +++ b/packages/renderer/src/components/Button.tsx @@ -32,7 +32,7 @@ const Button = ({ 'bg-brand-100 dark:bg-brand-600': color === Color.Primary, 'text-brand-500 dark:text-white': iconColor === Color.Primary, 'bg-gray-100 dark:bg-gray-700': color === Color.Gray, - 'text-gray-900 dark:text-gray-400': iconColor === Color.Gray, + 'text-gray-600 dark:text-gray-400': iconColor === Color.Gray, 'animate-pulse bg-gray-100 !text-transparent dark:bg-gray-800': isSkelton, } diff --git a/packages/renderer/src/components/CoverRow.tsx b/packages/renderer/src/components/CoverRow.tsx index 1956618..024aca8 100644 --- a/packages/renderer/src/components/CoverRow.tsx +++ b/packages/renderer/src/components/CoverRow.tsx @@ -125,7 +125,7 @@ const CoverRow = ({
prefetch(item.id)} - className='grid gap-x-[24px] gap-y-7' + className='grid gap-x-6 gap-y-7' >
{/* Cover */} diff --git a/packages/renderer/src/components/Router.tsx b/packages/renderer/src/components/Router.tsx index fe3f909..9272695 100644 --- a/packages/renderer/src/components/Router.tsx +++ b/packages/renderer/src/components/Router.tsx @@ -5,6 +5,7 @@ import Album from '@/pages/Album' import Home from '@/pages/Home' import Login from '@/pages/Login' import Playlist from '@/pages/Playlist' +import Artist from '@/pages/Artist' const routes: RouteObject[] = [ { @@ -23,11 +24,15 @@ const routes: RouteObject[] = [ path: '/album/:id', element: , }, + { + path: '/artist/:id', + element: , + }, ] -const router = () => { +const Router = () => { const element = useRoutes(routes) return {element} } -export default router +export default Router diff --git a/packages/renderer/src/components/Sidebar.tsx b/packages/renderer/src/components/Sidebar.tsx index 6dc6625..c337027 100644 --- a/packages/renderer/src/components/Sidebar.tsx +++ b/packages/renderer/src/components/Sidebar.tsx @@ -7,7 +7,7 @@ import { prefetchPlaylist } from '@/hooks/usePlaylist' interface Tab { name: string - icon?: string + icon: string route: string } interface PrimaryTab extends Tab { @@ -41,7 +41,7 @@ const PrimaryTabs = () => { onClick={() => scrollToTop()} key={tab.route} to={tab.route} - className={({ isActive }: { isActive: boolean }) => + className={({ isActive }) => classNames( 'btn-hover-animation mx-3 flex cursor-default items-center rounded-lg px-3 py-2 transition-colors duration-200 after:scale-[0.97] after:bg-black/[.06] dark:after:bg-white/20', !isActive && 'text-gray-700 dark:text-white', diff --git a/packages/renderer/src/components/Slider.tsx b/packages/renderer/src/components/Slider.tsx index 5c75b25..bad8604 100644 --- a/packages/renderer/src/components/Slider.tsx +++ b/packages/renderer/src/components/Slider.tsx @@ -131,7 +131,7 @@ const Slider = ({
diff --git a/packages/renderer/src/components/TracksGrid.tsx b/packages/renderer/src/components/TracksGrid.tsx index ea72635..395be76 100644 --- a/packages/renderer/src/components/TracksGrid.tsx +++ b/packages/renderer/src/components/TracksGrid.tsx @@ -2,18 +2,25 @@ import ArtistInline from '@/components/ArtistsInline' import Skeleton from '@/components/Skeleton' import { resizeImage } from '@/utils/common' -const TrackListGrid = ({ +const Track = ({ track, isSkeleton = false, + isHighlight = false, }: { track: Track isSkeleton: boolean + isHighlight: boolean }) => { return (
@@ -35,17 +42,19 @@ const TrackListGrid = ({ {!isSkeleton && (
{track.name}
)} {isSkeleton && ( - PLACEHOLDER12345 + PLACEHOLDER12345 )} -
- {!isSkeleton && } +
+ {!isSkeleton && ( + + )} {isSkeleton && ( PLACE )} @@ -56,4 +65,22 @@ const TrackListGrid = ({ ) } -export default TrackListGrid +const TrackGrid = ({ + tracks, + isSkeleton = false, + onTrackDoubleClick, +}: { + tracks: Track[] + isSkeleton?: boolean + onTrackDoubleClick?: (trackID: number) => void +}) => { + return ( +
+ {tracks.map((track, index) => ( + + ))} +
+ ) +} + +export default TrackGrid diff --git a/packages/renderer/src/hooks/useArtist.ts b/packages/renderer/src/hooks/useArtist.ts index f3075ff..d49ef1e 100644 --- a/packages/renderer/src/hooks/useArtist.ts +++ b/packages/renderer/src/hooks/useArtist.ts @@ -1,14 +1,24 @@ import { fetchArtist } from '@/api/artist' import { ArtistApiNames } from '@/api/artist' -import type { FetchArtistParams } from '@/api/artist' +import type { FetchArtistParams, FetchArtistResponse } from '@/api/artist' -export default function useArtist(params: FetchArtistParams, noCache: boolean) { +export default function useArtist( + params: FetchArtistParams, + noCache?: boolean +) { return useQuery( [ArtistApiNames.FETCH_ARTIST, params], - () => fetchArtist(params, noCache), + () => fetchArtist(params, !!noCache), { enabled: !!params.id && params.id > 0 && !isNaN(Number(params.id)), - staleTime: 3600000, + staleTime: 5 * 60 * 1000, // 5 mins + placeholderData: (): FetchArtistResponse => + window.ipcRenderer.sendSync('getApiCacheSync', { + api: 'artists', + query: { + id: params.id, + }, + }), } ) } diff --git a/packages/renderer/src/pages/Album.tsx b/packages/renderer/src/pages/Album.tsx index 34eb818..0bc8f12 100644 --- a/packages/renderer/src/pages/Album.tsx +++ b/packages/renderer/src/pages/Album.tsx @@ -146,7 +146,7 @@ const Header = ({
Album by{' '} {album?.artist.name} diff --git a/packages/renderer/src/pages/Artist.tsx b/packages/renderer/src/pages/Artist.tsx new file mode 100644 index 0000000..3fe8ded --- /dev/null +++ b/packages/renderer/src/pages/Artist.tsx @@ -0,0 +1,148 @@ +import Button, { Color as ButtonColor } from '@/components/Button' +import SvgIcon from '@/components/SvgIcon' +import Cover from '@/components/Cover' +import useArtist from '@/hooks/useArtist' +import useArtistAlbums from '@/hooks/useArtistAlbums' +import { resizeImage } from '@/utils/common' +import dayjs from 'dayjs' +import TracksGrid from '@/components/TracksGrid' +import CoverRow, { Subtitle } from '@/components/CoverRow' +import Skeleton from '@/components/Skeleton' +import { Fragment } from 'react' + +const Artist = () => { + const params = useParams() + + const { data: artist, isLoading } = useArtist({ + id: Number(params.id) || 0, + }) + + const { data: albumsRaw, isLoading: isLoadingAlbum } = useArtistAlbums({ + id: Number(params.id) || 0, + limit: 1000, + }) + + const albums = useMemo(() => { + if (!albumsRaw?.hotAlbums) return [] + return albumsRaw.hotAlbums.filter( + album => + album.type === '专辑' && + ['混音版', '精选集', 'Remix'].includes(album.subType) === false && + album.size > 1 + ) + }, [albumsRaw?.hotAlbums]) + + const singles = useMemo(() => { + if (!albumsRaw?.hotAlbums) return [] + return albumsRaw.hotAlbums.filter( + album => + album.type !== '专辑' || + ['混音版', '精选集', 'Remix'].includes(album.subType) || + album.size === 1 + ) + }, [albumsRaw?.hotAlbums]) + + const latestAlbum = useMemo(() => { + if (!albumsRaw || !albumsRaw.hotAlbums) return + return albumsRaw.hotAlbums[0] + }, [albumsRaw]) + + const coverImage = resizeImage(artist?.artist?.img1v1Url || '', 'md') + + return ( +
+
+ {coverImage && ( + + + + + )} +
+
+ + {/* Header */} +
+
+ + + + + +
+ +
+ +
+
+ {artist?.artist.name} +
+
+
+ +
+ {/* Latest release */} +
+
+ 最新发行 +
+
+ {isLoadingAlbum ? ( + + ) : ( + + )} +
+ {latestAlbum?.name} +
+
+ {latestAlbum?.type} ·{' '} + {dayjs(latestAlbum?.publishTime || 0).year()} +
+
+
+ + {/* Popular tracks */} +
+
+ 热门歌曲 +
+
+ +
+
+
+ + {/* Albums */} +
+
专辑
+ +
+ + {/* Singles/EP */} +
+
+ 单曲和EP +
+ +
+
+ ) +} + +export default Artist diff --git a/packages/renderer/src/pages/Login.tsx b/packages/renderer/src/pages/Login.tsx index e2d61df..e4f5d71 100644 --- a/packages/renderer/src/pages/Login.tsx +++ b/packages/renderer/src/pages/Login.tsx @@ -22,7 +22,9 @@ const EmailInput = ({ }) => { return (
-
Email
+
+ Email +
setEmail(e.target.value)} @@ -96,7 +98,7 @@ const PasswordInput = ({