From af417a28d32902cff11183d99ed9028b3f51aad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=AB=E5=A4=B4=E7=8C=AB?= Date: Tue, 29 Aug 2023 00:21:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=83=A8=E5=88=86=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/preload/index.ts | 2 + src/renderer/app.scss | 1 + src/renderer/components/MusicList/index.tsx | 3 +- .../downloaded-sheet.ts | 120 ++++++++++++++---- src/renderer/core/downloader/index.ts | 7 +- src/renderer/core/events/types/common.d.ts | 4 + .../music-sheet/internal/sheets-method.ts | 6 +- src/renderer/core/shortcut/index.ts | 4 +- src/renderer/document/bootstrap.ts | 2 + .../components/Downloaded/index.tsx | 13 ++ .../components/Downloading/index.tsx | 7 + .../main-page/views/download-view/index.scss | 13 ++ .../main-page/views/download-view/index.tsx | 28 +++- src/types/preload.d.ts | 1 + 14 files changed, 170 insertions(+), 41 deletions(-) rename src/renderer/core/{music-sheet/internal => downloader}/downloaded-sheet.ts (51%) create mode 100644 src/renderer/pages/main-page/views/download-view/components/Downloaded/index.tsx create mode 100644 src/renderer/pages/main-page/views/download-view/components/Downloading/index.tsx create mode 100644 src/renderer/pages/main-page/views/download-view/index.scss diff --git a/src/preload/index.ts b/src/preload/index.ts index bb86bfc4..49f01585 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -5,6 +5,7 @@ import ipcRendererDelegate from "./internal/ipc-renderer-delegate"; import fsDelegate from "./internal/fs-delegate"; import themepack from "./internal/themepack"; import path from 'path'; +import { rimraf } from "rimraf"; // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts @@ -14,3 +15,4 @@ contextBridge.exposeInMainWorld('ipcRenderer', ipcRendererDelegate); contextBridge.exposeInMainWorld('fs', fsDelegate); contextBridge.exposeInMainWorld('themepack', themepack); contextBridge.exposeInMainWorld('path', path); +contextBridge.exposeInMainWorld('rimraf', rimraf) diff --git a/src/renderer/app.scss b/src/renderer/app.scss index 8fc00364..2d904d20 100644 --- a/src/renderer/app.scss +++ b/src/renderer/app.scss @@ -61,6 +61,7 @@ & .tab-panel-container { height: 100%; + outline: none; } } diff --git a/src/renderer/components/MusicList/index.tsx b/src/renderer/components/MusicList/index.tsx index 3f0a17ee..f7055409 100644 --- a/src/renderer/components/MusicList/index.tsx +++ b/src/renderer/components/MusicList/index.tsx @@ -208,7 +208,7 @@ function _MusicList(props: IMusicListProps) { return () => { hotkeys.unbind('Shift', musiclistScope); } - }) + }, []) return (
@@ -241,6 +241,7 @@ function _MusicList(props: IMusicListProps) { > {virtualController.virtualItems.map((virtualItem) => { const row = virtualItem.dataItem; + // todo 拆出一个组件 return ( ([]); const downloadedSet = new Set(); +// 在初始化歌单时一起初始化 export async function setupDownloadedMusicList() { const downloaded = (await getUserPerferenceIDB("downloadedList")) ?? []; downloadedMusicListStore.setValue(downloaded); @@ -23,14 +27,14 @@ export async function setupDownloadedMusicList() { } function primaryKeyMap(media: IMedia.IMediaBase) { - return { - platform: media.platform, - id: media.id - } + return { + platform: media.platform, + id: media.id, + }; } // 添加到已下载完成的列表中 -export async function addDownloadedMusic( +export async function addDownloadedMusicToList( musicItems: IMusic.IMusicItem | IMusic.IMusicItem[] ) { const _musicItems = Array.isArray(musicItems) ? musicItems : [musicItems]; @@ -61,10 +65,14 @@ export async function addDownloadedMusic( } }); await musicSheetDB.musicStore.bulkPut(allMusic); - downloadedMusicListStore.setValue((prev) => [...prev, ...(allMusic.map(primaryKeyMap))]); + downloadedMusicListStore.setValue((prev) => [ + ...prev, + ...allMusic.map(primaryKeyMap), + ]); allMusic.forEach((it) => { downloadedSet.add(getMediaPrimaryKey(it)); }); + Evt.emit("MUSIC_DOWNLOADED", allMusic); setUserPerferenceIDB( "downloadedList", downloadedMusicListStore.getValue() @@ -76,7 +84,8 @@ export async function addDownloadedMusic( } export async function removeDownloadedMusic( - musicItems: IMusic.IMusicItem | IMusic.IMusicItem[] + musicItems: IMusic.IMusicItem | IMusic.IMusicItem[], + removeFile = false ) { const _musicItems = Array.isArray(musicItems) ? musicItems : [musicItems]; @@ -88,23 +97,37 @@ export async function removeDownloadedMusic( ); const needDelete: any[] = []; const needUpdate: any[] = []; - toBeRemovedMusicDetail.forEach((musicItem) => { - if (!musicItem) { - return; - } - musicItem[musicRefSymbol]--; - if (musicItem[musicRefSymbol] === 0) { - needDelete.push([musicItem.platform, musicItem.id]); - } else { - // 清空下载 - setInternalData( - musicItem, - "downloadData", - null - ); - needUpdate.push(musicItem); - } - }); + await Promise.all( + toBeRemovedMusicDetail.map(async (musicItem) => { + if (!musicItem) { + return; + } + // 1. 如果要删除本地文件 + if (removeFile) { + const internalPath = getInternalData( + musicItem, + "downloadData" + )?.path; + const rmResult = await window.rimraf(internalPath); + if (!rmResult) { + return; + } + } + // 只从歌单中删除,引用-1 + musicItem[musicRefSymbol]--; + if (musicItem[musicRefSymbol] === 0) { + needDelete.push([musicItem.platform, musicItem.id]); + } else { + // 清空下载 + setInternalData( + musicItem, + "downloadData", + null + ); + needUpdate.push(musicItem); + } + }) + ); await musicSheetDB.musicStore.bulkDelete(needDelete); await musicSheetDB.musicStore.bulkPut(needUpdate); @@ -113,7 +136,8 @@ export async function removeDownloadedMusic( (it) => -1 === _musicItems.findIndex((_) => isSameMedia(_, it)) ) ); - + // 触发事件 + Evt.emit("MUSIC_REMOVE_DOWNLOADED", _musicItems); _musicItems.forEach((it) => { downloadedSet.delete(getMediaPrimaryKey(it)); }); @@ -128,6 +152,46 @@ export async function removeDownloadedMusic( } } -export function isDownloadeed(musicItem: IMedia.IMediaBase) { - return downloadedSet.has(getMediaPrimaryKey(musicItem)); +export function isDownloaded(musicItem: IMedia.IMediaBase) { + return musicItem ? downloadedSet.has(getMediaPrimaryKey(musicItem)) : false; +} + +export const useDownloadedMusicList = downloadedMusicListStore.useValue; + +export function useDownloaded(musicItem: IMedia.IMediaBase) { + const [downloaded, setDownloaded] = useState(isDownloaded(musicItem)); + + useEffect(() => { + const dlCb = (musicItems: IMusic.IMusicItem | IMusic.IMusicItem[]) => { + if (Array.isArray(musicItems)) { + setDownloaded( + (prev) => + prev || + musicItems.findIndex((it) => isSameMedia(it, musicItem)) !== -1 + ); + } else { + setDownloaded((prev) => prev || isSameMedia(musicItem, musicItems)); + } + }; + + const rmCb = (musicItems: IMusic.IMusicItem | IMusic.IMusicItem[]) => { + if (Array.isArray(musicItems)) { + setDownloaded( + (prev) => + prev && + musicItems.findIndex((it) => isSameMedia(it, musicItem)) === -1 + ); + } else { + setDownloaded((prev) => prev && !isSameMedia(musicItem, musicItems)); + } + }; + + Evt.on("MUSIC_DOWNLOADED", dlCb); + Evt.on("MUSIC_REMOVE_DOWNLOADED", rmCb); + + return () => { + Evt.off("MUSIC_DOWNLOADED", dlCb); + Evt.off("MUSIC_REMOVE_DOWNLOADED", rmCb); + }; + }, [musicItem]); } diff --git a/src/renderer/core/downloader/index.ts b/src/renderer/core/downloader/index.ts index 4467d715..5530407f 100644 --- a/src/renderer/core/downloader/index.ts +++ b/src/renderer/core/downloader/index.ts @@ -8,9 +8,9 @@ import * as Comlink from "comlink"; import { callPluginDelegateMethod } from "../plugin-delegate"; import { DownloadState, localPluginName } from "@/common/constant"; import PQueue from "p-queue"; -import MusicSheet from "../music-sheet"; import { downloadingQueueStore } from "./store"; import throttle from "lodash.throttle"; +import { addDownloadedMusicToList, isDownloaded, setupDownloadedMusicList } from "./downloaded-sheet"; type ProxyMarkedFunction void> = T & Comlink.ProxyMarked; @@ -34,6 +34,7 @@ let downloaderWorker: IDownloaderWorker; async function setupDownloader() { setupDownloaderWorker(); + setupDownloadedMusicList(); } function forceUpdatePendingQueue() { @@ -69,7 +70,7 @@ async function generateDownloadMusicTask( const _musicItems = Array.isArray(musicItems) ? musicItems : [musicItems]; // 过滤掉已下载的 const _validMusicItems = _musicItems.filter( - (it) => !MusicSheet.isDownloadeed(it) && it.platform !== localPluginName + (it) => !isDownloaded(it) && it.platform !== localPluginName ); // @ts-ignore downloadingQueueStore.setValue((prev) => { @@ -161,7 +162,7 @@ async function downloadMusic( Comlink.proxy((dataState) => { onStateChange(dataState); if (dataState.state === DownloadState.DONE) { - MusicSheet.addDownloadedMusic( + addDownloadedMusicToList( setInternalData( musicItem as any, "downloadData", diff --git a/src/renderer/core/events/types/common.d.ts b/src/renderer/core/events/types/common.d.ts index fd97690b..2cfc38d9 100644 --- a/src/renderer/core/events/types/common.d.ts +++ b/src/renderer/core/events/types/common.d.ts @@ -3,5 +3,9 @@ declare namespace IEventType { interface IEvents { /** 路由导航 */ NAVIGATE: string; + // 音乐下载完成 + MUSIC_DOWNLOADED: IMedia.IMediaBase | IMedia.IMediaBase[]; + // 音乐被移除 + MUSIC_REMOVE_DOWNLOADED: IMedia.IMediaBase | IMedia.IMediaBase[]; } } \ No newline at end of file diff --git a/src/renderer/core/music-sheet/internal/sheets-method.ts b/src/renderer/core/music-sheet/internal/sheets-method.ts index 6ee85670..67dc9f74 100644 --- a/src/renderer/core/music-sheet/internal/sheets-method.ts +++ b/src/renderer/core/music-sheet/internal/sheets-method.ts @@ -12,7 +12,6 @@ import defaultSheet from "./default-sheet"; import { getMediaPrimaryKey, isSameMedia } from "@/common/media-util"; import { useEffect, useState } from "react"; import { getUserPerferenceIDB, setUserPerferenceIDB } from "@/renderer/utils/user-perference"; -import { setupDownloadedMusicList } from "./downloaded-sheet"; // 默认歌单,快速判定是否在列表中 const favoriteMusicListIds = new Set(); @@ -44,7 +43,6 @@ export async function setupSheets() { // 收藏歌单 const starredSheets = await getUserPerferenceIDB('starredMusicSheets'); starredSheetsStore.setValue(starredSheets ?? []); - await setupDownloadedMusicList(); } catch (e) { console.log(e); } @@ -467,6 +465,4 @@ export async function unstarMusicSheet(sheet: IMedia.IMediaBase) { const newSheets = starredSheetsStore.getValue().filter(item => !isSameMedia(item, sheet)); await setUserPerferenceIDB('starredMusicSheets', newSheets); starredSheetsStore.setValue(newSheets); -} - -export {addDownloadedMusic, removeDownloadedMusic, isDownloadeed} from './downloaded-sheet' \ No newline at end of file +} \ No newline at end of file diff --git a/src/renderer/core/shortcut/index.ts b/src/renderer/core/shortcut/index.ts index 1f332018..3e396482 100644 --- a/src/renderer/core/shortcut/index.ts +++ b/src/renderer/core/shortcut/index.ts @@ -30,8 +30,10 @@ const shortCutKeysEvts: Record = { const baseShortCutFunction = ( evt: keyof IEventType.IEvents, - global: boolean + global: boolean, + originalEvt: KeyboardEvent ) => { + originalEvt.preventDefault(); if (global && rendererAppConfig.getAppConfigPath("shortCut.enableGlobal")) { } else if (rendererAppConfig.getAppConfigPath("shortCut.enableLocal")) { Evt.emit(evt); diff --git a/src/renderer/document/bootstrap.ts b/src/renderer/document/bootstrap.ts index 0ee0ec4d..e455283a 100644 --- a/src/renderer/document/bootstrap.ts +++ b/src/renderer/document/bootstrap.ts @@ -13,6 +13,7 @@ import Evt from "../core/events"; import { ipcRendererInvoke } from "@/common/ipc-util/renderer"; import * as Comlink from 'comlink'; +import Downloader from "../core/downloader"; setAutoFreeze(false); @@ -29,6 +30,7 @@ export default async function () { dropHandler(); clearDefaultBehavior(); setupEvents(); + await Downloader.setupDownloader(); } diff --git a/src/renderer/pages/main-page/views/download-view/components/Downloaded/index.tsx b/src/renderer/pages/main-page/views/download-view/components/Downloaded/index.tsx new file mode 100644 index 00000000..dc9c72ec --- /dev/null +++ b/src/renderer/pages/main-page/views/download-view/components/Downloaded/index.tsx @@ -0,0 +1,13 @@ +import MusicSheet from '@/renderer/core/music-sheet' +import React from 'react' + +export default function Downloaded() { + + // const downloadedMusic = MusicSheet.addDownloadedMusic + + + + return ( +
Downloaded
+ ) +} diff --git a/src/renderer/pages/main-page/views/download-view/components/Downloading/index.tsx b/src/renderer/pages/main-page/views/download-view/components/Downloading/index.tsx new file mode 100644 index 00000000..fa48d63d --- /dev/null +++ b/src/renderer/pages/main-page/views/download-view/components/Downloading/index.tsx @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Downloading() { + return ( +
Downloading
+ ) +} diff --git a/src/renderer/pages/main-page/views/download-view/index.scss b/src/renderer/pages/main-page/views/download-view/index.scss new file mode 100644 index 00000000..8dace902 --- /dev/null +++ b/src/renderer/pages/main-page/views/download-view/index.scss @@ -0,0 +1,13 @@ +.download-view--container { + & .header { + font-weight: 600; + font-size: 1.5rem; + margin-top: 1.5rem; + margin-bottom: 1.2rem; + letter-spacing: 0.05rem; + user-select: none; + } + + + } + \ No newline at end of file diff --git a/src/renderer/pages/main-page/views/download-view/index.tsx b/src/renderer/pages/main-page/views/download-view/index.tsx index a9533d5a..31c4dd9e 100644 --- a/src/renderer/pages/main-page/views/download-view/index.tsx +++ b/src/renderer/pages/main-page/views/download-view/index.tsx @@ -1,7 +1,29 @@ -import React from 'react' +import { Tab } from "@headlessui/react"; +import "./index.scss"; +import Downloaded from "./components/Downloaded"; +import Downloading from "./components/Downloading"; export default function DownloadView() { return ( -
Download
- ) +
+ + + + 已下载 + + + 下载中 + + + + + + + + + + + +
+ ); } diff --git a/src/types/preload.d.ts b/src/types/preload.d.ts index a5a9ec60..4ee906d4 100644 --- a/src/types/preload.d.ts +++ b/src/types/preload.d.ts @@ -3,6 +3,7 @@ interface Window { themepack: typeof import('../preload/internal/themepack').default; globalData: IGlobalData, path: typeof import("node:path"); + rimraf: typeof import('rimraf').rimraf } interface IGlobalData {