diff --git a/web/src/app/main.tsx b/web/src/app/main.tsx index 1fb0b4c..996fec4 100644 --- a/web/src/app/main.tsx +++ b/web/src/app/main.tsx @@ -5,7 +5,7 @@ import { chatStore } from '@/features/chat' import { historyStore } from '@/features/history' import { lyricsStore } from '@/features/lyrics' import { playerStore, reminderStore, volumeStore } from '@/features/playback' -import { playlistsStore } from '@/features/playlists' +import { playlistsStore, setAccountSettingsOpener } from '@/features/playlists' import { roomEntryStore } from '@/features/rooms' import { searchStore } from '@/features/search' import { appearanceStore, performanceStore, profileStore } from '@/features/settings' @@ -28,6 +28,7 @@ volumeStore.start() brandStore.start() profileStore.start() playlistsStore.start() +setAccountSettingsOpener(() => navigationStore.open('settings')) playerStore.start() reminderStore.start() historyStore.start() diff --git a/web/src/features/history/history-page.tsx b/web/src/features/history/history-page.tsx index 245d0c6..c0dcf39 100644 --- a/web/src/features/history/history-page.tsx +++ b/web/src/features/history/history-page.tsx @@ -2,6 +2,7 @@ import type { PlayRecord } from './records' import { CirclePlus, Download, History, Search, Trash2 } from 'lucide-react' import { useState } from 'react' import { roomStore, sameTrack } from '@/core/room' +import { SaveToPlaylistButton } from '@/features/playlists' import { enqueue, sourceName } from '@/features/queue' import { bookTitle } from '@/shared/lib/book-title' import { formatClock } from '@/shared/lib/time' @@ -46,6 +47,7 @@ function HistoryRow({ record, compact, connected, playing }: { record: PlayRecor await enqueue(record.track) }} /> + (null) return (
) } diff --git a/web/src/features/playlists/account-settings.ts b/web/src/features/playlists/account-settings.ts new file mode 100644 index 0000000..0905973 --- /dev/null +++ b/web/src/features/playlists/account-settings.ts @@ -0,0 +1,10 @@ +// 游客在保存对话框里点“去账号设置”时打开账号设置。设置页在哪里、怎样打开由应用壳决定,启动时设置。 +let open = (): void => {} + +export function openAccountSettings(): void { + open() +} + +export function setAccountSettingsOpener(opener: () => void): void { + open = opener +} diff --git a/web/src/features/playlists/create-playlist-form.tsx b/web/src/features/playlists/create-playlist-form.tsx new file mode 100644 index 0000000..8acbe54 --- /dev/null +++ b/web/src/features/playlists/create-playlist-form.tsx @@ -0,0 +1,41 @@ +import type { MyPlaylist } from '@/core/realtime' +import { useState } from 'react' +import { playlistsStore } from './playlists-store' + +export function CreatePlaylistForm({ onCreated, onCancel }: { onCreated: (playlist: MyPlaylist) => void, onCancel: () => void }) { + const [name, setName] = useState('') + const [saving, setSaving] = useState(false) + return ( +
{ + if (event.key !== 'Escape' || event.nativeEvent.isComposing) + return + // 拦下对话框的 Esc:在“保存到歌单”里,Esc 只回到列表。 + event.preventDefault() + onCancel() + }} + onSubmit={async (event) => { + event.preventDefault() + if (saving) + return + setSaving(true) + const playlist = await playlistsStore.create(name) + if (playlist) + onCreated(playlist) + else + setSaving(false) + }} + > + +
+ + +
+
+ ) +} diff --git a/web/src/features/playlists/index.ts b/web/src/features/playlists/index.ts index 277bca5..f544eac 100644 --- a/web/src/features/playlists/index.ts +++ b/web/src/features/playlists/index.ts @@ -1 +1,4 @@ +export { setAccountSettingsOpener } from './account-settings' export { playlistsStore } from './playlists-store' +export { saveToPlaylistLabel } from './save-label' +export { SaveToPlaylistButton, SaveToPlaylistDialog } from './save-to-playlist' diff --git a/web/src/features/playlists/playlist-cover.tsx b/web/src/features/playlists/playlist-cover.tsx new file mode 100644 index 0000000..f967936 --- /dev/null +++ b/web/src/features/playlists/playlist-cover.tsx @@ -0,0 +1,26 @@ +import { Music2, Star } from 'lucide-react' +import { useState } from 'react' + +export function FavoritesMark({ size = 14 }: { size?: number }) { + return