@@ -2,10 +2,12 @@ import {
2
2
createEffect ,
3
3
createSignal ,
4
4
For ,
5
+ Match ,
5
6
on ,
6
7
onCleanup ,
7
8
Show ,
8
9
startTransition ,
10
+ Switch ,
9
11
} from 'solid-js' ;
10
12
import {
11
13
Trans ,
@@ -14,8 +16,6 @@ import {
14
16
} from '@jellybrick/solid-i18next' ;
15
17
import { Marquee } from '@suyongs/solid-utility' ;
16
18
17
- import alsong from 'alsong' ;
18
-
19
19
import SideBar from './SideBar' ;
20
20
21
21
import Card from '../components/Card' ;
@@ -34,21 +34,26 @@ import { formatTime } from '../utils/formatTime';
34
34
import { LangResource } from '../../common/intl' ;
35
35
import { useLyricProvider } from '../hooks/useLyricProvider' ;
36
36
import { LyricMetadata } from '../../common/provider' ;
37
+ import Selector from '../components/Select' ;
37
38
38
39
const LyricsMapEditor = ( ) => {
39
40
usePluginsCSS ( ) ;
40
41
41
42
const lyricProvider = useLyricProvider ( ) ;
42
43
const {
44
+ lyricData,
45
+ id : playingId ,
43
46
title : playingTitle ,
44
47
artist : playingArtist ,
45
48
duration,
46
49
status,
47
- id,
48
50
} = usePlayingInfo ( ) ;
49
51
52
+ const [ searchMode , setSearchMode ] = createSignal < 'default' | 'id' > ( 'default' ) ;
53
+
50
54
const [ title , setTitle ] = createSignal ( playingTitle ( ) ) ;
51
55
const [ artist , setArtist ] = createSignal ( playingArtist ( ) ) ;
56
+ const [ id , setId ] = createSignal ( '' ) ;
52
57
53
58
const [ loading , setLoading ] = createSignal ( false ) ;
54
59
const [ searchList , setSearchList ] = createSignal < LyricMetadata [ ] > ( [ ] ) ;
@@ -71,64 +76,63 @@ const LyricsMapEditor = () => {
71
76
observer . disconnect ( ) ;
72
77
} ) ;
73
78
74
- const onSearch = async ( ) => {
79
+ const onSearch = async ( nextPage = false ) => {
75
80
setLoading ( true ) ;
76
81
82
+ if ( ! nextPage ) {
83
+ setPage ( 0 ) ;
84
+ }
85
+
77
86
await usePluginOverride (
78
87
'search-lyrics' ,
79
- async ( artist , title , options ) => {
80
- const result = await lyricProvider ( )
81
- . searchLyrics ( {
82
- artist,
83
- title,
84
- playtime : options ?. playtime ,
85
- page : options ?. page ,
86
- } )
87
- . catch ( ( e ) => {
88
- console . error ( e ) ;
89
- return [ ] ;
90
- } ) ;
88
+ async ( mode , artist , title , id , options ) => {
89
+ const list : LyricMetadata [ ] = [ ] ;
91
90
92
- setSearchList ( result ) ;
93
- setPage ( 0 ) ;
94
- setHasNext ( true ) ;
95
- } ,
96
- artist ( ) ,
97
- title ( ) ,
98
- { playtime : duration ( ) , page : 0 } ,
99
- ) ;
100
-
101
- setLoading ( false ) ;
102
- } ;
91
+ if ( mode === 'default' ) {
92
+ list . push (
93
+ ...( await lyricProvider ( )
94
+ . searchLyrics ( {
95
+ artist,
96
+ title,
97
+ playtime : options ?. playtime ,
98
+ page : options ?. page ,
99
+ } )
100
+ . catch ( ( e ) => {
101
+ console . error ( e ) ;
102
+ return [ ] ;
103
+ } ) ) ,
104
+ ) ;
105
+ }
106
+ if ( mode === 'id' ) {
107
+ const result = await lyricProvider ( )
108
+ . getLyricById ( id )
109
+ . catch ( ( e ) => {
110
+ console . error ( e ) ;
111
+ return null ;
112
+ } ) ;
103
113
104
- const onSearchNextPage = async ( ) => {
105
- setLoading ( true ) ;
114
+ if ( result ) list . push ( result ) ;
115
+ }
106
116
107
- await usePluginOverride (
108
- 'search-lyrics' ,
109
- async ( artist , title , options ) => {
110
- const result = await lyricProvider ( )
111
- . searchLyrics ( {
112
- artist,
113
- title,
114
- playtime : options ?. playtime ,
115
- page : options ?. page ,
116
- } )
117
- . catch ( ( e ) => {
118
- console . error ( e ) ;
119
- return [ ] ;
120
- } ) ;
117
+ if ( nextPage ) {
118
+ const isDuplicated = searchList ( ) . at ( - 1 ) ?. id === list . at ( - 1 ) ?. id ;
121
119
122
- if ( result . length === 0 ) {
123
- setHasNext ( false ) ;
124
- return ;
120
+ if ( ! isDuplicated ) {
121
+ setPage ( page ( ) + 1 ) ;
122
+ setSearchList ( [ ...searchList ( ) , ...list ] ) ;
123
+ } else {
124
+ setHasNext ( false ) ;
125
+ }
126
+ } else {
127
+ setPage ( 0 ) ;
128
+ setSearchList ( list ) ;
129
+ setHasNext ( true ) ;
125
130
}
126
-
127
- setSearchList ( [ ...searchList ( ) , ...result ] ) ;
128
- setPage ( page ( ) + 1 ) ;
129
131
} ,
132
+ searchMode ( ) ,
130
133
artist ( ) ,
131
134
title ( ) ,
135
+ id ( ) ,
132
136
{ playtime : duration ( ) , page : page ( ) } ,
133
137
) ;
134
138
@@ -137,7 +141,7 @@ const LyricsMapEditor = () => {
137
141
138
142
const onSelect = async ( metadata : LyricMetadata ) => {
139
143
const newMapper = {
140
- [ id ( ) ] : {
144
+ [ playingId ( ) ] : {
141
145
mode : {
142
146
type : 'provider' as const ,
143
147
id : metadata . id ,
@@ -151,7 +155,7 @@ const LyricsMapEditor = () => {
151
155
152
156
const observer = new IntersectionObserver ( ( entries ) => {
153
157
if ( ! loading ( ) && entries [ 0 ] . intersectionRatio > 0 ) {
154
- onSearchNextPage ( ) ;
158
+ onSearch ( true ) ;
155
159
}
156
160
} ) ;
157
161
@@ -179,18 +183,43 @@ const LyricsMapEditor = () => {
179
183
onSearch ( ) ;
180
184
} }
181
185
>
182
- < input
183
- class = { 'input' }
184
- placeholder = { t ( 'lyrics.artist' ) }
185
- value = { artist ( ) }
186
- onInput = { ( event ) => setArtist ( event . target . value ) }
187
- />
188
- < input
189
- class = { 'input flex-1' }
190
- placeholder = { t ( 'lyrics.title' ) }
191
- value = { title ( ) }
192
- onInput = { ( event ) => setTitle ( event . target . value ) }
186
+ < Selector
187
+ mode = { 'select' }
188
+ placeholder = { t ( 'lyrics.search-mode' ) }
189
+ class = { 'select min-w-[90px] w-16 basis-1/5' }
190
+ options = { [ 'default' , 'id' ] as const }
191
+ value = { searchMode ( ) }
192
+ onChange = { setSearchMode }
193
+ format = { ( str ) => t ( `lyrics.search-mode.${ str } ` ) }
193
194
/>
195
+ < Switch >
196
+ < Match when = { searchMode ( ) === 'default' } >
197
+ < >
198
+ < input
199
+ class = { 'input w-16 basis-1/5' }
200
+ placeholder = { t ( 'lyrics.artist' ) }
201
+ value = { artist ( ) }
202
+ onInput = { ( event ) => setArtist ( event . target . value ) }
203
+ />
204
+ < input
205
+ class = { 'input w-16 basis-1/5 flex-1' }
206
+ placeholder = { t ( 'lyrics.title' ) }
207
+ value = { title ( ) }
208
+ onInput = { ( event ) => setTitle ( event . target . value ) }
209
+ />
210
+ </ >
211
+ </ Match >
212
+ < Match when = { searchMode ( ) === 'id' } >
213
+ < >
214
+ < input
215
+ class = { 'input flex-1 w-16 basis-1/5' }
216
+ placeholder = { t ( 'lyrics.id' ) }
217
+ value = { id ( ) }
218
+ onInput = { ( event ) => setId ( event . target . value ) }
219
+ />
220
+ </ >
221
+ </ Match >
222
+ </ Switch >
194
223
< button type = { 'submit' } class = { 'btn-text btn-icon !min-w-0' } >
195
224
< svg
196
225
width = "16"
@@ -222,7 +251,9 @@ const LyricsMapEditor = () => {
222
251
< For each = { searchList ( ) } >
223
252
{ ( item ) => (
224
253
< Card
225
- class = { 'flex flex-row justify-start items-center gap-1' }
254
+ class = { `flex flex-row justify-start items-center gap-1
255
+ ${ lyricData ( ) ?. id === item . id ? '!bg-primary-100 dark:!bg-primary-800 hover:!bg-primary-200 hover:dark:!bg-primary-700' : '' }
256
+ ` }
226
257
onClick = { ( ) => onSelect ( item ) }
227
258
>
228
259
< div
@@ -274,19 +305,35 @@ const LyricsMapEditor = () => {
274
305
</ div >
275
306
</ Show >
276
307
</ div >
277
- < svg
278
- width = "16"
279
- height = "16"
280
- fill = "none"
281
- viewBox = "0 0 24 24"
282
- xmlns = "http://www.w3.org/2000/svg"
283
- class = { 'self-center flex-shrink-0' }
308
+ < Show
309
+ when = { lyricData ( ) ?. id !== item . id }
310
+ fallback = {
311
+ < svg
312
+ class = { 'w-6 h-6 fill-none self-center flex-shrink-0' }
313
+ xmlns = "http://www.w3.org/2000/svg"
314
+ viewBox = "0 -960 960 960"
315
+ >
316
+ < path
317
+ d = "M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"
318
+ class = { 'fill-green-500' }
319
+ />
320
+ </ svg >
321
+ }
284
322
>
285
- < path
286
- d = "M8.293 4.293a1 1 0 0 0 0 1.414L14.586 12l-6.293 6.293a1 1 0 1 0 1.414 1.414l7-7a1 1 0 0 0 0-1.414l-7-7a1 1 0 0 0-1.414 0Z"
287
- class = { 'fill-black dark:fill-white' }
288
- />
289
- </ svg >
323
+ < svg
324
+ width = "16"
325
+ height = "16"
326
+ fill = "none"
327
+ viewBox = "0 0 24 24"
328
+ xmlns = "http://www.w3.org/2000/svg"
329
+ class = { 'w-6 h-6 fill-none self-center flex-shrink-0' }
330
+ >
331
+ < path
332
+ d = "M8.293 4.293a1 1 0 0 0 0 1.414L14.586 12l-6.293 6.293a1 1 0 1 0 1.414 1.414l7-7a1 1 0 0 0 0-1.414l-7-7a1 1 0 0 0-1.414 0Z"
333
+ class = { 'fill-black dark:fill-white' }
334
+ />
335
+ </ svg >
336
+ </ Show >
290
337
</ Card >
291
338
) }
292
339
</ For >
0 commit comments