1import {pluginManager, usePlugins} from '@/common/pluginManager'; 2import produce from 'immer'; 3import {useAtom, useSetAtom} from 'jotai'; 4import {useCallback, useRef} from 'react'; 5import {PageStatus, pageStatusAtom, searchResultsAtom} from '../store/atoms'; 6 7export default function useSearch() { 8 const setPageStatus = useSetAtom(pageStatusAtom); 9 const [searchResults, setSearchResults] = useAtom(searchResultsAtom); 10 11 // 当前正在搜索 12 const currentQueryRef = useRef<string>(''); 13 14 const search = useCallback( 15 async function ( 16 query?: string, 17 platformHash = 'all', 18 queryPage = undefined, 19 ) { 20 const installedPlugins = pluginManager.getValidPlugins(); 21 const plugins = 22 platformHash === 'all' 23 ? installedPlugins 24 : [installedPlugins.find(_ => _.hash === platformHash)]; 25 // 使用选中插件搜素 26 plugins.forEach(async plugin => { 27 const _platform = plugin?.instance.platform; 28 const _hash = plugin?.hash; 29 if (!plugin || !_platform || !_hash) { 30 // 没有插件,此时直接进入结果页 31 setPageStatus(PageStatus.RESULT); 32 return; 33 } 34 const _prevResult = searchResults[_hash] ?? {}; 35 if ( 36 (_prevResult.state === 'pending' || _prevResult.state === 'done') && 37 undefined === query 38 ) { 39 return; 40 } 41 42 // 是否是一次新的搜索 43 const newSearch = 44 query || _prevResult?.currentPage === undefined || queryPage === 1; 45 46 // 搜索关键词 47 currentQueryRef.current = query = query ?? _prevResult?.query ?? ''; 48 49 /** 搜索的页码 */ 50 const page = 51 queryPage ?? newSearch ? 1 : (_prevResult.currentPage ?? 0) + 1; 52 53 54 try { 55 setSearchResults(prevState => 56 produce(prevState, draft => { 57 const prev = draft[_hash] ?? {}; 58 prev.query = query; 59 prev.state = 'pending'; 60 prev.result = newSearch ? {} : prev.result; 61 draft[_hash] = prev; 62 }), 63 ); 64 // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。 65 const result = await plugin?.instance?.search?.(query, page); 66 if (currentQueryRef.current !== query) { 67 return; 68 } 69 setPageStatus(PageStatus.RESULT); 70 if (!result) { 71 throw new Error(); 72 } 73 setSearchResults( 74 produce(draft => { 75 const prev = draft[_hash] ?? {}; 76 if (result._isEnd === false) { 77 prev.state = 'resolved'; 78 } else { 79 prev.state = 'done'; 80 } 81 prev.result = newSearch 82 ? mergeResult(result, {}, _platform) 83 : mergeResult(prev.result ?? {}, result ?? {}, _platform); 84 draft[_hash] = { 85 state: prev.state, 86 result: prev.result, 87 query: query, 88 currentPage: page, 89 }; 90 return draft; 91 }), 92 ); 93 } catch (e) { 94 console.log('SEARCH ERROR', e); 95 setSearchResults( 96 produce(draft => { 97 const prev = draft[_hash] ?? {}; 98 prev.state = 'resolved'; 99 draft[_hash] = prev; 100 }), 101 ); 102 } 103 }); 104 }, 105 [searchResults], 106 ); 107 108 return search; 109} 110 111// todo: 去重 112const resultKeys: (keyof IPlugin.ISearchResult)[] = ['album', 'music']; 113function mergeResult( 114 obj1: Record<string, any>, 115 obj2: Record<string, any>, 116 platform: string, 117): IPlugin.ISearchResult { 118 const result: Record<string, any> = {}; 119 for (let k of resultKeys) { 120 result[k] = (obj1[k] ?? []) 121 .map((_: any) => 122 produce(_, (_: any) => { 123 _.platform = platform; 124 }), 125 ) 126 .concat( 127 (obj2[k] ?? []).map((_: any) => 128 produce(_, (_: any) => { 129 _.platform = platform; 130 }), 131 ), 132 ); 133 } 134 return result; 135} 136