mirror of
https://github.com/maotoumao/MusicFreeDesktop.git
synced 2026-06-01 12:23:18 +08:00
feat: recently play&lang
This commit is contained in:
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,3 +1,7 @@
|
||||
{
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
"editor.formatOnSave": true,
|
||||
"files.associations": {
|
||||
"*.html": "html",
|
||||
"map": "cpp"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "musicfree-desktop",
|
||||
"productName": "MusicFree",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"description": "一个插件化的音乐播放器",
|
||||
"main": ".webpack/main",
|
||||
"scripts": {
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"add": "Add",
|
||||
"save": "Save",
|
||||
"clear": "Clear",
|
||||
"open": "Open"
|
||||
"open": "Open",
|
||||
"status": "Status"
|
||||
},
|
||||
|
||||
"media": {
|
||||
@@ -156,7 +157,10 @@
|
||||
"create_local_sheet": "Create Playlist",
|
||||
"starred_sheets": "Favorites",
|
||||
|
||||
"delete_sheet": "Delete Playlist"
|
||||
"delete_sheet": "Delete",
|
||||
"rename_sheet": "Rename",
|
||||
"unstar_sheet": "Unstar",
|
||||
"recently_play": "Recently Play"
|
||||
},
|
||||
"app_header": {
|
||||
"nav_back": "Back",
|
||||
@@ -165,7 +169,9 @@
|
||||
"search_history": "Search History",
|
||||
"settings": "Settings",
|
||||
"minimize": "Minimize",
|
||||
"exit": "Exit"
|
||||
"minimode": "Minimode",
|
||||
"exit": "Exit",
|
||||
"theme": "Theme"
|
||||
},
|
||||
"music_bar": {
|
||||
"open_music_detail_page": "Open Song Details",
|
||||
@@ -193,7 +199,10 @@
|
||||
"link_media_lyric": "Link Lyric",
|
||||
"media_lyric_linked": "Lyric Linked: ",
|
||||
"unlink_media_lyric": "Unlink Lyric",
|
||||
"toast_media_lyric_unlinked": "Lyric Unlinked"
|
||||
"toast_media_lyric_unlinked": "Lyric Unlinked",
|
||||
"translation": "Translation",
|
||||
"show_translation": "Show Translation",
|
||||
"hide_translation": "Hide Translation"
|
||||
},
|
||||
"bottom_loading_state": {
|
||||
"reached_end": "~~~ End ~~~",
|
||||
@@ -232,7 +241,8 @@
|
||||
},
|
||||
"music_sheet_like_view": {
|
||||
"play_all": "Play All",
|
||||
"add_to_sheet": "Add to Playlist"
|
||||
"add_to_sheet": "Add to Playlist",
|
||||
"star": "Star"
|
||||
},
|
||||
"settings": {
|
||||
"choose_path": "Choose Path",
|
||||
@@ -277,7 +287,9 @@
|
||||
"double_click_music_list": "When double-clicking the music list",
|
||||
"add_music_to_playlist": "Add the selected song to the playback queue",
|
||||
"replace_playlist_with_musiclist": "Replace playback queue with current music list",
|
||||
"audio_output_device": "Audio output device"
|
||||
"audio_output_device": "Audio output device",
|
||||
"when_device_removed": "When device is removed",
|
||||
"continue_playing": "Continue playing"
|
||||
},
|
||||
"download": {
|
||||
"download_folder": "Download Directory",
|
||||
@@ -300,18 +312,6 @@
|
||||
"auto_update_plugin": "Automatically update plugins on startup",
|
||||
"not_check_plugin_version": "Do not check plugin version when installing"
|
||||
},
|
||||
"theme": {
|
||||
"example_theme_hint": "Here are some example themes:",
|
||||
"example_theme_subscription_hint": "You can also follow the WeChat public account: <highlight>A Cat-headed Cat</highlight>, and reply with <highlight>MusicFree Theme Pack</highlight> to get the download link (updated periodically)",
|
||||
"install_theme": "Install Theme Pack",
|
||||
"uninstall_theme": "Uninstall Theme Pack",
|
||||
"musicfree_theme": "MusicFree Theme",
|
||||
"all_files": "All Files",
|
||||
"install_theme_success": "Installed theme {{name}} successfully~",
|
||||
"install_theme_fail": "Failed to install theme: {{reason}}",
|
||||
"uninstall_theme_success": "Uninstalled theme {{name}} successfully~",
|
||||
"uninstall_theme_fail": "Failed to uninstall theme: {{reason}}"
|
||||
},
|
||||
"short_cut": {
|
||||
"enable_local": "Enable in-app shortcuts",
|
||||
"enable_global": "Enable global shortcuts",
|
||||
@@ -373,5 +373,23 @@
|
||||
"unlock_desktop_lyric": "Unlock Desktop Lyrics",
|
||||
"lock_desktop_lyric": "Lock Desktop Lyrics",
|
||||
"no_playing_music": "No Music Playing"
|
||||
},
|
||||
"theme": {
|
||||
"tab_local": "Local Theme",
|
||||
"tab_remote": "Theme Marketplace",
|
||||
"download_and_use": "Download and Use",
|
||||
"use_theme": "Use Theme",
|
||||
"install_theme": "Install Theme",
|
||||
"update_theme": "Update Theme",
|
||||
"uninstall_theme": "Uninstall Theme",
|
||||
"musicfree_theme": "MusicFree Theme",
|
||||
"all_files": "All Files",
|
||||
"install_theme_success": "Successfully installed theme {{name}}~",
|
||||
"install_theme_fail": "Failed to install theme: {{reason}}",
|
||||
"uninstall_theme_success": "Successfully uninstalled theme {{name}}~",
|
||||
"uninstall_theme_fail": "Failed to uninstall theme: {{reason}}",
|
||||
"how_to_submit_new_theme": "💡How to submit a new theme: The themes in the theme marketplace are synchronized with the <Github>MusicFreeThemePacks</Github> repository. If you need to submit a new theme, please make a pull request directly.",
|
||||
"load_remote_theme_error": "An error occurred...",
|
||||
"invalid_theme": "Invalid theme: {{reason}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"add": "Añadir",
|
||||
"save": "Guardar",
|
||||
"clear": "Limpiar",
|
||||
"open": "Abrir"
|
||||
"open": "Abrir",
|
||||
"status": "Estado"
|
||||
},
|
||||
|
||||
"media": {
|
||||
@@ -156,7 +157,10 @@
|
||||
"create_local_sheet": "Crear lista de canciones",
|
||||
"starred_sheets": "Mis favoritos",
|
||||
|
||||
"delete_sheet": "Eliminar lista de canciones"
|
||||
"delete_sheet": "Eliminar lista de canciones",
|
||||
"rename_sheet": "Cambiar nombre",
|
||||
"unstar_sheet": "Desmarcar",
|
||||
"recently_play": "Reproducido recientemente"
|
||||
},
|
||||
"app_header": {
|
||||
"nav_back": "Atrás",
|
||||
@@ -165,7 +169,9 @@
|
||||
"search_history": "Historial de búsqueda",
|
||||
"settings": "Configuración",
|
||||
"minimize": "Minimizar",
|
||||
"exit": "Salir"
|
||||
"minimode": "Modo minimalista",
|
||||
"exit": "Salir",
|
||||
"theme": "tema"
|
||||
},
|
||||
"music_bar": {
|
||||
"open_music_detail_page": "Abrir detalles de la canción",
|
||||
@@ -193,7 +199,10 @@
|
||||
"link_media_lyric": "Vincular letra",
|
||||
"media_lyric_linked": "Letra vinculada: ",
|
||||
"unlink_media_lyric": "Desvincular letra",
|
||||
"toast_media_lyric_unlinked": "Letra desvinculada"
|
||||
"toast_media_lyric_unlinked": "Letra desvinculada",
|
||||
"translation": "Traducción",
|
||||
"show_translation": "Mostrar traducción",
|
||||
"hide_translation": "Ocultar traducción"
|
||||
},
|
||||
"bottom_loading_state": {
|
||||
"reached_end": "~~~ Fin ~~~",
|
||||
@@ -232,7 +241,8 @@
|
||||
},
|
||||
"music_sheet_like_view": {
|
||||
"play_all": "Reproducir todo",
|
||||
"add_to_sheet": "Añadir a la lista de canciones"
|
||||
"add_to_sheet": "Añadir a la lista de canciones",
|
||||
"star": "favorito"
|
||||
},
|
||||
"settings": {
|
||||
"choose_path": "Elegir ruta",
|
||||
@@ -277,7 +287,9 @@
|
||||
"double_click_music_list": "Al hacer doble clic en la lista de canciones",
|
||||
"add_music_to_playlist": "Añadir canción a la lista de reproducción",
|
||||
"replace_playlist_with_musiclist": "Reemplazar la lista de reproducción con la lista de canciones actual",
|
||||
"audio_output_device": "Dispositivo de salida de audio"
|
||||
"audio_output_device": "Dispositivo de salida de audio",
|
||||
"when_device_removed": "Cuando se elimina el dispositivo de audio",
|
||||
"continue_playing": "Continuar reproduciendo"
|
||||
},
|
||||
"download": {
|
||||
"download_folder": "Carpeta de descargas",
|
||||
@@ -300,18 +312,6 @@
|
||||
"auto_update_plugin": "Actualizar complementos automáticamente al iniciar la aplicación",
|
||||
"not_check_plugin_version": "No verificar la versión al instalar complementos"
|
||||
},
|
||||
"theme": {
|
||||
"example_theme_hint": "Aquí hay algunos temas de ejemplo:",
|
||||
"example_theme_subscription_hint": "También puedes seguir nuestro WeChat: <highlight>Una GatoGato</highlight>, responde con <highlight>Paquete de temas MusicFree</highlight> para obtener el enlace de descarga (actualizado periódicamente)",
|
||||
"install_theme": "Instalar paquete de temas",
|
||||
"uninstall_theme": "Desinstalar paquete de temas",
|
||||
"musicfree_theme": "Tema MusicFree",
|
||||
"all_files": "Todos los archivos",
|
||||
"install_theme_success": "Tema {{name}} instalado con éxito~",
|
||||
"install_theme_fail": "Error al instalar el tema: {{reason}}",
|
||||
"uninstall_theme_success": "Tema {{name}} desinstalado con éxito~",
|
||||
"uninstall_theme_fail": "Error al desinstalar el tema: {{reason}}"
|
||||
},
|
||||
"short_cut": {
|
||||
"enable_local": "Habilitar atajos en la aplicación",
|
||||
"enable_global": "Habilitar atajos globales",
|
||||
@@ -373,5 +373,23 @@
|
||||
"unlock_desktop_lyric": "Desbloquear letra en pantalla",
|
||||
"lock_desktop_lyric": "Bloquear letra en pantalla",
|
||||
"no_playing_music": "No hay música reproduciéndose actualmente"
|
||||
},
|
||||
"theme": {
|
||||
"tab_local": "Tema local",
|
||||
"tab_remote": "Mercado de temas",
|
||||
"download_and_use": "Descargar y usar",
|
||||
"use_theme": "Usar tema",
|
||||
"install_theme": "Instalar tema",
|
||||
"update_theme": "Actualizar tema",
|
||||
"uninstall_theme": "Desinstalar tema",
|
||||
"musicfree_theme": "Tema de MusicFree",
|
||||
"all_files": "Todos los archivos",
|
||||
"install_theme_success": "Tema {{name}} instalado con éxito~",
|
||||
"install_theme_fail": "Error al instalar el tema: {{reason}}",
|
||||
"uninstall_theme_success": "Tema {{name}} desinstalado con éxito~",
|
||||
"uninstall_theme_fail": "Error al desinstalar el tema: {{reason}}",
|
||||
"how_to_submit_new_theme": "💡Cómo enviar un nuevo tema: Los temas en el mercado de temas están sincronizados con el repositorio <Github>MusicFreeThemePacks</Github>. Si necesita enviar un nuevo tema, envíe una solicitud de extracción directamente.",
|
||||
"load_remote_theme_error": "Ha ocurrido un error...",
|
||||
"invalid_theme": "Tema inválido: {{reason}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,8 @@
|
||||
|
||||
"delete_sheet": "删除歌单",
|
||||
"rename_sheet": "重命名歌单",
|
||||
"unstar_sheet": "取消收藏"
|
||||
"unstar_sheet": "取消收藏",
|
||||
"recently_play": "最近播放"
|
||||
},
|
||||
"app_header": {
|
||||
"nav_back": "后退",
|
||||
@@ -311,7 +312,6 @@
|
||||
"auto_update_plugin": "打开软件时自动更新插件",
|
||||
"not_check_plugin_version": "安装插件时不校验版本"
|
||||
},
|
||||
|
||||
"short_cut": {
|
||||
"enable_local": "启用软件内快捷键",
|
||||
"enable_global": "启用全局快捷键",
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"add": "新增",
|
||||
"save": "保存",
|
||||
"clear": "清除",
|
||||
"open": "打開"
|
||||
"open": "打開",
|
||||
"status": "狀態"
|
||||
},
|
||||
|
||||
"media": {
|
||||
@@ -156,7 +157,10 @@
|
||||
"create_local_sheet": "創建歌單",
|
||||
"starred_sheets": "我的收藏",
|
||||
|
||||
"delete_sheet": "刪除歌單"
|
||||
"delete_sheet": "刪除歌單",
|
||||
"rename_sheet": "重歌單",
|
||||
"unstar_sheet": "取消收藏",
|
||||
"recently_play": "最近播放"
|
||||
},
|
||||
"app_header": {
|
||||
"nav_back": "返回",
|
||||
@@ -165,7 +169,9 @@
|
||||
"search_history": "搜尋歷史",
|
||||
"settings": "設置",
|
||||
"minimize": "最小化",
|
||||
"exit": "退出"
|
||||
"minimode": "迷你模式",
|
||||
"exit": "退出",
|
||||
"theme": "主題"
|
||||
},
|
||||
"music_bar": {
|
||||
"open_music_detail_page": "打開歌曲詳情",
|
||||
@@ -193,7 +199,10 @@
|
||||
"link_media_lyric": "鏈接歌詞",
|
||||
"media_lyric_linked": "已鏈接歌詞:",
|
||||
"unlink_media_lyric": "取消鏈接歌詞",
|
||||
"toast_media_lyric_unlinked": "已取消鏈接歌詞"
|
||||
"toast_media_lyric_unlinked": "已取消鏈接歌詞",
|
||||
"translation": "翻譯",
|
||||
"show_translation": "顯示翻譯",
|
||||
"hide_translation": "隱藏翻譯"
|
||||
},
|
||||
"bottom_loading_state": {
|
||||
"reached_end": "~~~ 沒有更多了 ~~~",
|
||||
@@ -232,7 +241,8 @@
|
||||
},
|
||||
"music_sheet_like_view": {
|
||||
"play_all": "播放全部",
|
||||
"add_to_sheet": "添加到歌單"
|
||||
"add_to_sheet": "添加到歌單",
|
||||
"star": "收藏"
|
||||
},
|
||||
"settings": {
|
||||
"choose_path": "選擇路徑",
|
||||
@@ -277,7 +287,9 @@
|
||||
"double_click_music_list": "雙擊歌曲列表時",
|
||||
"add_music_to_playlist": "添加歌曲到播放清單",
|
||||
"replace_playlist_with_musiclist": "用當前歌單替換播放清單",
|
||||
"audio_output_device": "音頻輸出設備"
|
||||
"audio_output_device": "音頻輸出設備",
|
||||
"when_device_removed": "當音頻裝置被移除時",
|
||||
"continue_playing": "繼續播放"
|
||||
},
|
||||
"download": {
|
||||
"download_folder": "下載文件夾",
|
||||
@@ -300,18 +312,6 @@
|
||||
"auto_update_plugin": "啟動應用時自動更新插件",
|
||||
"not_check_plugin_version": "安裝插件時不檢查版本"
|
||||
},
|
||||
"theme": {
|
||||
"example_theme_hint": "這裡有一些示例主題:",
|
||||
"example_theme_subscription_hint": "你也可以關注我們的微信:<highlight>一隻貓貓</highlight>,回復 <highlight>MusicFree 主題包</highlight> 獲取下載連結(定期更新)",
|
||||
"install_theme": "安裝主題包",
|
||||
"uninstall_theme": "卸載主題包",
|
||||
"musicfree_theme": "MusicFree 主題",
|
||||
"all_files": "所有文件",
|
||||
"install_theme_success": "主題 {{name}} 安裝成功~",
|
||||
"install_theme_fail": "安裝主題失敗:{{reason}}",
|
||||
"uninstall_theme_success": "主題 {{name}} 卸載成功~",
|
||||
"uninstall_theme_fail": "卸載主題失敗:{{reason}}"
|
||||
},
|
||||
"short_cut": {
|
||||
"enable_local": "啟用應用內快捷鍵",
|
||||
"enable_global": "啟用全局快捷鍵",
|
||||
@@ -373,5 +373,23 @@
|
||||
"unlock_desktop_lyric": "解鎖桌面歌詞",
|
||||
"lock_desktop_lyric": "鎖定桌面歌詞",
|
||||
"no_playing_music": "目前無正在播放的音樂"
|
||||
},
|
||||
"theme": {
|
||||
"tab_local": "本地主題",
|
||||
"tab_remote": "主題市場",
|
||||
"download_and_use": "下載並使用",
|
||||
"use_theme": "使用主題",
|
||||
"install_theme": "安裝主題",
|
||||
"update_theme": "更新主題",
|
||||
"uninstall_theme": "卸載主題",
|
||||
"musicfree_theme": "MusicFree 主題",
|
||||
"all_files": "全部檔案",
|
||||
"install_theme_success": "安裝主題{{name}}成功~",
|
||||
"install_theme_fail": "安裝主題失敗: {{reason}}",
|
||||
"uninstall_theme_success": "卸載主題{{name}}成功~",
|
||||
"uninstall_theme_fail": "卸載主題失敗: {{reason}}",
|
||||
"how_to_submit_new_theme": "💡如何提交新主題: 主題市場中的主題與 <Github>MusicFreeThemePacks</Github> 倉庫同步,如果需要提交新主題請直接提PR。",
|
||||
"load_remote_theme_error": "出錯啦...",
|
||||
"invalid_theme": "主題無效: {{reason}}"
|
||||
}
|
||||
}
|
||||
|
||||
3
src/assets/icons/clock.svg
Normal file
3
src/assets/icons/clock.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 247 B |
@@ -74,6 +74,8 @@ interface IMusicListProps {
|
||||
enableDrag?: boolean;
|
||||
/** 拖拽结束 */
|
||||
onDragEnd?: (newMusicList: IMusic.IMusicItem[]) => void;
|
||||
/** context */
|
||||
contextMenu?: IContextMenuItem[];
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<IMusic.IMusicItem>();
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
padding-left: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
|
||||
&:not([data-type="primaryButton"]) {
|
||||
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
||||
}
|
||||
|
||||
& svg {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -26,11 +26,14 @@
|
||||
flex: 1;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
margin-left: 8px;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ import { useTranslation } from "react-i18next";
|
||||
interface IProps {
|
||||
musicSheet: IMusic.IMusicSheetItem;
|
||||
musicList: IMusic.IMusicItem[];
|
||||
hidePlatform?: boolean;
|
||||
}
|
||||
|
||||
export default function Header(props: IProps) {
|
||||
const { musicSheet, musicList } = props;
|
||||
const { musicSheet, musicList, hidePlatform } = props;
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const {t} = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="music-sheetlike-view--header-container" ref={containerRef}>
|
||||
@@ -28,28 +29,39 @@ export default function Header(props: IProps) {
|
||||
></img>
|
||||
<div className="sheet-info">
|
||||
<div className="title-container">
|
||||
<Condition condition={musicSheet?.platform}>
|
||||
{musicSheet?.platform && !hidePlatform ? (
|
||||
<Tag>{musicSheet?.platform}</Tag>
|
||||
</Condition>
|
||||
<div className="title">{musicSheet?.title ?? t("media.unknown_title")}</div>
|
||||
) : null}
|
||||
|
||||
<div className="title">
|
||||
{musicSheet?.title ?? t("media.unknown_title")}
|
||||
</div>
|
||||
</div>
|
||||
<Condition condition={musicSheet?.createAt || musicSheet?.artist}>
|
||||
<div className="info-container">
|
||||
<Condition condition={musicSheet?.createAt}>
|
||||
<span>
|
||||
{t("media.media_create_at")}: {dayjs(musicSheet?.createAt).format("YYYY-MM-DD")}
|
||||
{t("media.media_create_at")}:{" "}
|
||||
{dayjs(musicSheet?.createAt).format("YYYY-MM-DD")}
|
||||
</span>
|
||||
</Condition>
|
||||
<Condition condition={musicSheet?.artist}>
|
||||
<span>{t("media.media_type_artist")}: {musicSheet?.artist}</span>
|
||||
<span>
|
||||
{t("media.media_type_artist")}: {musicSheet?.artist}
|
||||
</span>
|
||||
</Condition>
|
||||
</div>
|
||||
</Condition>
|
||||
<div className="info-container">
|
||||
<Condition condition={musicSheet?.playCount}>
|
||||
<span>{t("media.media_play_count")}: {musicSheet?.playCount}</span>
|
||||
<span>
|
||||
{t("media.media_play_count")}: {musicSheet?.playCount}
|
||||
</span>
|
||||
</Condition>
|
||||
<span>{t("media.media_music_count")}: {musicSheet?.worksNum ?? musicList?.length ?? 0}</span>
|
||||
<span>
|
||||
{t("media.media_music_count")}:{" "}
|
||||
{musicSheet?.worksNum ?? musicList?.length ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Condition condition={musicSheet?.description}>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { RequestStateCode } from "@/common/constant";
|
||||
import Body from "./components/Body";
|
||||
import Header from "./components/Header";
|
||||
|
||||
import "./index.scss";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { initValue, offsetHeightStore } from "./store";
|
||||
import "./index.scss";
|
||||
|
||||
interface IMusicSheetlikeViewProps {
|
||||
scrollElement?: HTMLElement;
|
||||
@@ -13,6 +12,8 @@ interface IMusicSheetlikeViewProps {
|
||||
state?: RequestStateCode;
|
||||
onLoadMore?: () => void;
|
||||
options?: ReactNode;
|
||||
/** 是否展示来源tag */
|
||||
hidePlatform?: boolean;
|
||||
}
|
||||
|
||||
export default function MusicSheetlikeView(props: IMusicSheetlikeViewProps) {
|
||||
@@ -22,6 +23,7 @@ export default function MusicSheetlikeView(props: IMusicSheetlikeViewProps) {
|
||||
state = RequestStateCode.IDLE,
|
||||
onLoadMore,
|
||||
options,
|
||||
hidePlatform,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,7 +34,11 @@ export default function MusicSheetlikeView(props: IMusicSheetlikeViewProps) {
|
||||
|
||||
return (
|
||||
<div className="music-sheetlike-view--container">
|
||||
<Header musicSheet={musicSheet} musicList={musicList ?? []}></Header>
|
||||
<Header
|
||||
hidePlatform={hidePlatform}
|
||||
musicSheet={musicSheet}
|
||||
musicList={musicList ?? []}
|
||||
></Header>
|
||||
<Body
|
||||
musicList={musicList ?? []}
|
||||
musicSheet={musicSheet}
|
||||
|
||||
@@ -12,6 +12,7 @@ export type SvgAssetIconNames =
|
||||
| "chevron-down"
|
||||
| "chevron-left"
|
||||
| "chevron-right"
|
||||
| "clock"
|
||||
| "code-bracket-square"
|
||||
| "cog-8-tooth"
|
||||
| "dashboard-speed"
|
||||
|
||||
80
src/renderer/core/recently-playlist/index.ts
Normal file
80
src/renderer/core/recently-playlist/index.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { isSameMedia } from "@/common/media-util";
|
||||
import Store from "@/common/store";
|
||||
import {
|
||||
getUserPreferenceIDB,
|
||||
setUserPreferenceIDB,
|
||||
} from "@/renderer/utils/user-perference";
|
||||
import { Immer } from "immer";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const recentlyPlayListStore = new Store<IMusic.IMusicItem[]>([]);
|
||||
|
||||
const immer = new Immer({
|
||||
autoFreeze: false,
|
||||
});
|
||||
|
||||
const HARD_LIMIT = 500;
|
||||
|
||||
async function fetchRecentlyPlaylist() {
|
||||
return (await getUserPreferenceIDB("recentlyPlayList")) || [];
|
||||
}
|
||||
|
||||
async function setRecentlyPlaylist(musicItems: IMusic.IMusicItem[]) {
|
||||
recentlyPlayListStore.setValue(musicItems);
|
||||
return await setUserPreferenceIDB("recentlyPlayList", musicItems);
|
||||
}
|
||||
|
||||
export async function setupRecentlyPlaylist() {
|
||||
const playList = await fetchRecentlyPlaylist();
|
||||
recentlyPlayListStore.setValue(playList);
|
||||
}
|
||||
|
||||
export async function addToRecentlyPlaylist(musicItem: IMusic.IMusicItem) {
|
||||
const playList = recentlyPlayListStore.getValue();
|
||||
const existId = playList.findIndex((it) => isSameMedia(musicItem, it));
|
||||
let newPlayList = playList;
|
||||
|
||||
if (existId !== -1) {
|
||||
newPlayList = immer.produce(playList, (draft) => {
|
||||
draft.splice(existId, 1);
|
||||
});
|
||||
}
|
||||
newPlayList = [musicItem].concat(newPlayList).slice(0, HARD_LIMIT);
|
||||
setRecentlyPlaylist(newPlayList);
|
||||
}
|
||||
|
||||
export async function removeRecentlyPlayList(musicItem: IMusic.IMusicItem) {
|
||||
const playList = recentlyPlayListStore.getValue();
|
||||
const existId = playList.findIndex((it) => isSameMedia(musicItem, it));
|
||||
let newPlayList = playList;
|
||||
|
||||
if (existId !== -1) {
|
||||
newPlayList = immer.produce(playList, (draft) => {
|
||||
draft.splice(existId, 1);
|
||||
});
|
||||
setRecentlyPlaylist(newPlayList);
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearRecentlyPlaylist() {
|
||||
setRecentlyPlaylist([]);
|
||||
}
|
||||
|
||||
export function useRecentlyPlaylistSheet() {
|
||||
const recentlyPlayList = recentlyPlayListStore.useValue();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const musicSheet: IMusic.IMusicSheetItem = useMemo(() => {
|
||||
return {
|
||||
id: "recently-play",
|
||||
title: t("side_bar.recently_play"),
|
||||
platform: "recently-play",
|
||||
playCount: recentlyPlayList?.length || 0,
|
||||
artwork: recentlyPlayList?.[0]?.artwork,
|
||||
musicList: recentlyPlayList || [],
|
||||
};
|
||||
}, [recentlyPlayList, t]);
|
||||
|
||||
return musicSheet;
|
||||
}
|
||||
@@ -23,6 +23,11 @@ import {
|
||||
setupPlayerSyncHandler,
|
||||
} from "../core/command-handler";
|
||||
import ThemePack from "@/shared/themepack/renderer";
|
||||
import {
|
||||
addToRecentlyPlaylist,
|
||||
setupRecentlyPlaylist,
|
||||
} from "../core/recently-playlist";
|
||||
import { TrackPlayerEvent } from "../core/track-player/enum";
|
||||
|
||||
setAutoFreeze(false);
|
||||
|
||||
@@ -43,6 +48,7 @@ export default async function () {
|
||||
setupDeviceChange();
|
||||
localMusic.setupLocalMusic();
|
||||
await Downloader.setupDownloader();
|
||||
setupRecentlyPlaylist();
|
||||
|
||||
// 自动更新插件
|
||||
if (getAppConfigPath("plugin.autoUpdatePlugin")) {
|
||||
@@ -152,6 +158,11 @@ function setupEvents() {
|
||||
MusicSheet.frontend.addMusicToFavorite(realItem);
|
||||
}
|
||||
});
|
||||
|
||||
// 最近播放
|
||||
trackPlayer.on(TrackPlayerEvent.MusicChanged, (musicItem) => {
|
||||
addToRecentlyPlaylist(musicItem);
|
||||
});
|
||||
}
|
||||
|
||||
async function setupDeviceChange() {
|
||||
|
||||
@@ -87,6 +87,7 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮样式
|
||||
div[role="button"] {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
@@ -114,6 +115,7 @@ div[role="button"] {
|
||||
border: 1px solid currentColor;
|
||||
width: fit-content;
|
||||
line-height: 1em;
|
||||
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
||||
}
|
||||
|
||||
&[data-type="dangerButton"] {
|
||||
|
||||
@@ -8,7 +8,6 @@ import MainPage from "../pages/main-page";
|
||||
import { ContextMenuComponent } from "../components/ContextMenu";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
|
||||
|
||||
import "rc-slider/assets/index.css";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import "./index.css"; // 全局样式
|
||||
@@ -48,7 +47,6 @@ function Root() {
|
||||
}
|
||||
|
||||
function BootstrapComponent(): null {
|
||||
|
||||
useBootstrap();
|
||||
|
||||
return null;
|
||||
|
||||
@@ -36,6 +36,11 @@ export default function () {
|
||||
title: t("side_bar.plugin_management"),
|
||||
route: "plugin-manager-view",
|
||||
},
|
||||
{
|
||||
iconName: "clock",
|
||||
title: t("side_bar.recently_play"),
|
||||
route: "recently_play",
|
||||
},
|
||||
] as const;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
.page-container {
|
||||
flex: auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
position: relative;
|
||||
flex: auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page-container-full-width {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.page-container-fw {
|
||||
@extend .page-container;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@@ -13,58 +13,55 @@ import SettingView from "./views/setting-view";
|
||||
import LocalMusicView from "./views/local-music-view";
|
||||
import Empty from "@/renderer/components/Empty";
|
||||
import DownloadView from "./views/download-view";
|
||||
import ThemeView from "./views/theme-view";
|
||||
import RecentlyPlayView from "./views/recently-play-view";
|
||||
|
||||
import "./index.scss";
|
||||
import ThemeView from "./views/theme-view";
|
||||
|
||||
export default function MainPage() {
|
||||
return (
|
||||
<>
|
||||
<SideBar></SideBar>
|
||||
<div className="page-container" id="page-container">
|
||||
<Routes>
|
||||
<Route
|
||||
path="search/:query"
|
||||
element={<SearchView></SearchView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="plugin-manager-view"
|
||||
element={<PluginManagerView></PluginManagerView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="musicsheet/:platform/:id"
|
||||
element={<MusicSheetView></MusicSheetView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="album/:platform/:id"
|
||||
element={<AlbumView></AlbumView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="artist/:platform/:id"
|
||||
element={<ArtistView></ArtistView>}
|
||||
></Route>
|
||||
<Route path="toplist" element={<ToplistView></ToplistView>}></Route>
|
||||
<Route
|
||||
path="toplist-detail/:platform"
|
||||
element={<TopListDetailView></TopListDetailView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="recommend-sheets"
|
||||
element={<RecommendSheetsView></RecommendSheetsView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="local-music"
|
||||
element={<LocalMusicView></LocalMusicView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="download"
|
||||
element={<DownloadView></DownloadView>}
|
||||
></Route>
|
||||
<Route path="setting" element={<SettingView></SettingView>}></Route>
|
||||
<Route path="theme" element={<ThemeView></ThemeView>}></Route>
|
||||
<Route path="*" element={<Empty></Empty>}></Route>
|
||||
</Routes>
|
||||
</div>
|
||||
<Routes>
|
||||
<Route path="search/:query" element={<SearchView></SearchView>}></Route>
|
||||
<Route
|
||||
path="plugin-manager-view"
|
||||
element={<PluginManagerView></PluginManagerView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="musicsheet/:platform/:id"
|
||||
element={<MusicSheetView></MusicSheetView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="album/:platform/:id"
|
||||
element={<AlbumView></AlbumView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="artist/:platform/:id"
|
||||
element={<ArtistView></ArtistView>}
|
||||
></Route>
|
||||
<Route path="toplist" element={<ToplistView></ToplistView>}></Route>
|
||||
<Route
|
||||
path="toplist-detail/:platform"
|
||||
element={<TopListDetailView></TopListDetailView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="recommend-sheets"
|
||||
element={<RecommendSheetsView></RecommendSheetsView>}
|
||||
></Route>
|
||||
<Route
|
||||
path="local-music"
|
||||
element={<LocalMusicView></LocalMusicView>}
|
||||
></Route>
|
||||
<Route path="download" element={<DownloadView></DownloadView>}></Route>
|
||||
<Route path="setting" element={<SettingView></SettingView>}></Route>
|
||||
<Route path="theme" element={<ThemeView></ThemeView>}></Route>
|
||||
<Route
|
||||
path="recently_play"
|
||||
element={<RecentlyPlayView></RecentlyPlayView>}
|
||||
></Route>
|
||||
<Route path="*" element={<Empty></Empty>}></Route>
|
||||
</Routes>
|
||||
<MusicDetail></MusicDetail>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -20,11 +20,13 @@ export default function AlbumView() {
|
||||
useAlbumDetail(originalAlbumItem);
|
||||
|
||||
return (
|
||||
<MusicSheetlikeView
|
||||
musicSheet={albumItem}
|
||||
musicList={musicList}
|
||||
onLoadMore={getAlbumDetail}
|
||||
state={requestState}
|
||||
></MusicSheetlikeView>
|
||||
<div id="page-container" className="page-container">
|
||||
<MusicSheetlikeView
|
||||
musicSheet={albumItem}
|
||||
musicList={musicList}
|
||||
onLoadMore={getAlbumDetail}
|
||||
state={requestState}
|
||||
></MusicSheetlikeView>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@ interface IBodyProps {
|
||||
|
||||
const supportedMediaType = ["music", "album"];
|
||||
export default function Body(props: IBodyProps) {
|
||||
const {artistItem} = props;
|
||||
const { artistItem } = props;
|
||||
const [currentMediaType, setCurrentMediaType] = useState("music");
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="artist-view--body-container">
|
||||
@@ -28,21 +26,21 @@ export default function Body(props: IBodyProps) {
|
||||
<Tab.List className="tab-list-container">
|
||||
{supportedMediaType.map((type) => (
|
||||
<Tab key={type} as="div" className="tab-list-item">
|
||||
{t(type)}
|
||||
{t(`media.media_type_${type}`)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className={"tab-panels-container"}>
|
||||
{supportedMediaType.map((type) => (
|
||||
<Tab.Panel className="tab-panel-container" key={type}>
|
||||
<SwitchCase.Switch switch={type}>
|
||||
<SwitchCase.Case case={"music"}>
|
||||
<MusicResult artistItem={artistItem}></MusicResult>
|
||||
</SwitchCase.Case>
|
||||
<SwitchCase.Case case={"album"}>
|
||||
<AlbumResult artistItem={artistItem}></AlbumResult>
|
||||
</SwitchCase.Case>
|
||||
</SwitchCase.Switch>
|
||||
<SwitchCase.Switch switch={type}>
|
||||
<SwitchCase.Case case={"music"}>
|
||||
<MusicResult artistItem={artistItem}></MusicResult>
|
||||
</SwitchCase.Case>
|
||||
<SwitchCase.Case case={"album"}>
|
||||
<AlbumResult artistItem={artistItem}></AlbumResult>
|
||||
</SwitchCase.Case>
|
||||
</SwitchCase.Switch>
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
|
||||
@@ -11,7 +11,7 @@ interface IProps {
|
||||
|
||||
export default function Header(props: IProps) {
|
||||
const { artistItem } = props;
|
||||
const {t} = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="artist-view--header-container">
|
||||
@@ -23,7 +23,9 @@ export default function Header(props: IProps) {
|
||||
<div className="artist-info">
|
||||
<div className="title-container">
|
||||
<Tag>{artistItem?.platform}</Tag>
|
||||
<div className="title">{artistItem?.name ?? t("media.unknown_artist")}</div>
|
||||
<div className="title">
|
||||
{artistItem?.name ?? t("media.unknown_artist")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Condition condition={artistItem?.description}>
|
||||
@@ -35,7 +37,7 @@ export default function Header(props: IProps) {
|
||||
dataset.fold = dataset.fold === "true" ? "false" : "true";
|
||||
}}
|
||||
>
|
||||
简介:{artistItem?.description}
|
||||
{artistItem?.description}
|
||||
</div>
|
||||
</Condition>
|
||||
</div>
|
||||
|
||||
@@ -22,12 +22,12 @@ export default function ArtistView() {
|
||||
return () => {
|
||||
queryResultStore.setValue(initQueryResult);
|
||||
};
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="artist-view--container">
|
||||
<div id="page-container" className="page-container artist-view--container">
|
||||
<Header artistItem={artistItem}></Header>
|
||||
<Body artistItem = {artistItem}></Body>
|
||||
<Body artistItem={artistItem}></Body>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ export default function DownloadView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="download-view--container">
|
||||
<div
|
||||
id="page-container"
|
||||
className="page-container download-view--container"
|
||||
>
|
||||
<Tab.Group>
|
||||
<Tab.List className="tab-list-container">
|
||||
<Tab as="div" className="tab-list-item">
|
||||
|
||||
@@ -74,7 +74,8 @@ export default function LocalMusicView() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="local-music-view--container"
|
||||
id="page-container"
|
||||
className="page-container local-music-view--container"
|
||||
data-full-page={displayView !== DisplayView.LIST}
|
||||
>
|
||||
<div className="header">{t("local_music_page.local_music")}</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import "./index.scss";
|
||||
import Condition from "@/renderer/components/Condition";
|
||||
import { localPluginName } from "@/common/constant";
|
||||
import LocalSheet from "./local-sheet";
|
||||
import RemoteSheet from "./remote-sheet";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
/**
|
||||
* path: /main/musicsheet/platform/id
|
||||
*
|
||||
@@ -17,11 +17,12 @@ export default function MusicSheetView() {
|
||||
const { platform } = useParams() ?? {};
|
||||
|
||||
return (
|
||||
<Condition
|
||||
condition={platform === localPluginName}
|
||||
falsy={<RemoteSheet></RemoteSheet>}
|
||||
>
|
||||
<LocalSheet></LocalSheet>
|
||||
</Condition>
|
||||
<div id="page-container" className="page-container">
|
||||
{platform === localPluginName ? (
|
||||
<LocalSheet></LocalSheet>
|
||||
) : (
|
||||
<RemoteSheet></RemoteSheet>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function LocalSheet() {
|
||||
|
||||
return (
|
||||
<MusicSheetlikeView
|
||||
hidePlatform
|
||||
musicSheet={_musicSheet}
|
||||
state={loading}
|
||||
musicList={musicSheet?.musicList ?? []}
|
||||
|
||||
@@ -11,7 +11,10 @@ export default function PluginManagerView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="plugin-manager-view-container">
|
||||
<div
|
||||
id="page-container"
|
||||
className="page-container plugin-manager-view-container"
|
||||
>
|
||||
<div className="header">
|
||||
{t("plugin_management_page.plugin_management")}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import MusicSheetlikeView from "@/renderer/components/MusicSheetlikeView";
|
||||
import SvgAsset from "@/renderer/components/SvgAsset";
|
||||
import {
|
||||
clearRecentlyPlaylist,
|
||||
useRecentlyPlaylistSheet,
|
||||
} from "@/renderer/core/recently-playlist";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function RecentlyPlayView() {
|
||||
const recentlyPlaylistSheet = useRecentlyPlaylistSheet();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const options = (
|
||||
<>
|
||||
<div
|
||||
role="button"
|
||||
className="option-button"
|
||||
data-type="normalButton"
|
||||
data-disabled={!recentlyPlaylistSheet.playCount}
|
||||
onClick={() => {
|
||||
clearRecentlyPlaylist();
|
||||
}}
|
||||
>
|
||||
<SvgAsset iconName={"trash"}></SvgAsset>
|
||||
<span>{t("common.clear")}</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="page-container" className="page-container">
|
||||
<MusicSheetlikeView
|
||||
hidePlatform
|
||||
musicSheet={recentlyPlaylistSheet}
|
||||
musicList={recentlyPlaylistSheet.musicList}
|
||||
options={options}
|
||||
></MusicSheetlikeView>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -10,41 +10,43 @@ export default function RecommendSheetsView() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Condition
|
||||
condition={availablePlugins.length}
|
||||
falsy={<NoPlugin supportMethod="热门歌单" height={"100%"}></NoPlugin>}
|
||||
>
|
||||
<Tab.Group
|
||||
defaultIndex={history.state?.usr?.pluginIndex}
|
||||
onChange={(index) => {
|
||||
const usr = history.state.usr ?? {};
|
||||
|
||||
navigate("", {
|
||||
replace: true,
|
||||
state: {
|
||||
...usr,
|
||||
pluginHash: availablePlugins[index].hash,
|
||||
pluginIndex: index,
|
||||
tag: null
|
||||
},
|
||||
});
|
||||
}}
|
||||
<div id="page-container" className="page-container">
|
||||
<Condition
|
||||
condition={availablePlugins.length}
|
||||
falsy={<NoPlugin supportMethod="热门歌单" height={"100%"}></NoPlugin>}
|
||||
>
|
||||
<Tab.List className="tab-list-container">
|
||||
{availablePlugins.map((plugin) => (
|
||||
<Tab key={plugin.hash} as="div" className="tab-list-item">
|
||||
{plugin.platform}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className={"tab-panels-container"}>
|
||||
{availablePlugins.map((plugin) => (
|
||||
<Tab.Panel className="tab-panel-container" key={plugin.hash}>
|
||||
<Body plugin={plugin}></Body>
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</Condition>
|
||||
<Tab.Group
|
||||
defaultIndex={history.state?.usr?.pluginIndex}
|
||||
onChange={(index) => {
|
||||
const usr = history.state.usr ?? {};
|
||||
|
||||
navigate("", {
|
||||
replace: true,
|
||||
state: {
|
||||
...usr,
|
||||
pluginHash: availablePlugins[index].hash,
|
||||
pluginIndex: index,
|
||||
tag: null,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Tab.List className="tab-list-container">
|
||||
{availablePlugins.map((plugin) => (
|
||||
<Tab key={plugin.hash} as="div" className="tab-list-item">
|
||||
{plugin.platform}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className={"tab-panels-container"}>
|
||||
{availablePlugins.map((plugin) => (
|
||||
<Tab.Panel className="tab-panel-container" key={plugin.hash}>
|
||||
<Body plugin={plugin}></Body>
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</Condition>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function SearchView() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="search-view-container">
|
||||
<div id="page-container" className="page-container search-view-container">
|
||||
<div className="search-header">
|
||||
<span className="highlight">「{decodeURIComponent(query)}」</span>
|
||||
{t("search_result_page.search_result_title")}
|
||||
|
||||
@@ -18,9 +18,6 @@ export default function SettingView() {
|
||||
const intersectionRatioRef = useRef<Map<string, number>>(new Map());
|
||||
|
||||
useEffect(() => {
|
||||
document
|
||||
.getElementById("page-container")
|
||||
?.classList?.add("page-container-full-width");
|
||||
intersectionObserverRef.current = new IntersectionObserver(
|
||||
(targets) => {
|
||||
const ratio = intersectionRatioRef.current;
|
||||
@@ -62,7 +59,10 @@ export default function SettingView() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="setting-view--container">
|
||||
<div
|
||||
id="page-container"
|
||||
className="page-container-fw setting-view--container"
|
||||
>
|
||||
<div className="setting-view--header">
|
||||
<div className="tab-list-container">
|
||||
{routers.map((setting) => (
|
||||
|
||||
@@ -10,22 +10,24 @@ export default function ThemeView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Tab.Group>
|
||||
<Tab.List className="tab-list-container">
|
||||
{routes.map((it) => (
|
||||
<Tab key={it} as="div" className="tab-list-item">
|
||||
{t(`theme.tab_${it}`)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className={"tab-panels-container"}>
|
||||
<Tab.Panel>
|
||||
<LocalThemes></LocalThemes>
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<RemoteThemes></RemoteThemes>
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
<div id="page-container" className="page-container">
|
||||
<Tab.Group>
|
||||
<Tab.List className="tab-list-container">
|
||||
{routes.map((it) => (
|
||||
<Tab key={it} as="div" className="tab-list-item">
|
||||
{t(`theme.tab_${it}`)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className={"tab-panels-container"}>
|
||||
<Tab.Panel>
|
||||
<LocalThemes></LocalThemes>
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<RemoteThemes></RemoteThemes>
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ export default function TopListDetailView() {
|
||||
);
|
||||
|
||||
return (
|
||||
<MusicSheetlikeView
|
||||
musicSheet={topListDetail}
|
||||
musicList={topListDetail?.musicList ?? []}
|
||||
state={state}
|
||||
onLoadMore={loadMore}
|
||||
/>
|
||||
<div id="page-container" className="page-container">
|
||||
<MusicSheetlikeView
|
||||
musicSheet={topListDetail}
|
||||
musicList={topListDetail?.musicList ?? []}
|
||||
state={state}
|
||||
onLoadMore={loadMore}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,17 +14,21 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
|
||||
export default function ToplistView() {
|
||||
const availablePlugins = getSortedSupportedPlugin("getTopLists");
|
||||
const navigate = useNavigate();
|
||||
const {t} = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="toplist-view--container">
|
||||
<div id="page-container" className="page-container toplist-view--container">
|
||||
<Condition
|
||||
condition={availablePlugins.length}
|
||||
falsy={<NoPlugin supportMethod={t("plugin.method_get_top_lists")} height={"100%"}></NoPlugin>}
|
||||
falsy={
|
||||
<NoPlugin
|
||||
supportMethod={t("plugin.method_get_top_lists")}
|
||||
height={"100%"}
|
||||
></NoPlugin>
|
||||
}
|
||||
>
|
||||
<Tab.Group
|
||||
defaultIndex={history.state?.usr?.pluginIndex}
|
||||
|
||||
2
src/types/user-perference.d.ts
vendored
2
src/types/user-perference.d.ts
vendored
@@ -24,6 +24,8 @@ declare namespace IUserPreference {
|
||||
interface IDBType {
|
||||
/** 当前播放队列 */
|
||||
playList: IMusic.IMusicItem[];
|
||||
/** 最近播放队列 */
|
||||
recentlyPlayList: IMusic.IMusicItem[];
|
||||
/** 已下载列表 */
|
||||
downloadedList: IMedia.IMediaBase[];
|
||||
/** 本地音乐监听列表 */
|
||||
|
||||
Reference in New Issue
Block a user