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 try { 54 setSearchResults( 55 produce(draft => { 56 const prev = draft[_hash] ?? {}; 57 draft[_hash] = { 58 state: 'pending', 59 result: newSearch ? {} : prev.result, 60 query: query, 61 currentPage: page, 62 }; 63 return draft; 64 }), 65 ); 66 // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。 67 const result = await plugin?.instance?.search?.(query, page); 68 if (currentQueryRef.current !== query) { 69 return; 70 } 71 setPageStatus(PageStatus.RESULT); 72 if (!result) { 73 throw new Error(); 74 } 75 setSearchResults( 76 produce(draft => { 77 const prev = draft[_hash] ?? {}; 78 if (result._isEnd === false) { 79 prev.state = 'resolved'; 80 } else { 81 prev.state = 'done'; 82 } 83 prev.result = newSearch 84 ? mergeResult(result, {}, _platform) 85 : mergeResult(prev.result ?? {}, result ?? {}, _platform); 86 draft[_hash] = { 87 state: prev.state, 88 result: prev.result, 89 query: query, 90 currentPage: page, 91 }; 92 return draft; 93 }), 94 ); 95 } catch (e) { 96 console.log('SEARCH ERROR', e); 97 setSearchResults( 98 produce(draft => { 99 const prev = draft[_hash] ?? {}; 100 prev.state = 'resolved'; 101 draft[_hash] = prev; 102 }), 103 ); 104 } 105 }); 106 }, 107 [searchResults], 108 ); 109 110 return search; 111} 112 113// todo: 去重 114const resultKeys: (keyof IPlugin.ISearchResult)[] = ['album', 'music']; 115function mergeResult( 116 obj1: Record<string, any>, 117 obj2: Record<string, any>, 118 platform: string, 119): IPlugin.ISearchResult { 120 const result: Record<string, any> = {}; 121 for (let k of resultKeys) { 122 result[k] = (obj1[k] ?? []) 123 .map((_: any) => 124 produce(_, (_: any) => { 125 _.platform = platform; 126 }), 127 ) 128 .concat( 129 (obj2[k] ?? []).map((_: any) => 130 produce(_, (_: any) => { 131 _.platform = platform; 132 }), 133 ), 134 ); 135 } 136 return result; 137} 138