1import {Plugin, pluginManager} from '@/common/pluginManager'; 2import produce from 'immer'; 3import {useAtom, useSetAtom} from 'jotai'; 4import {useCallback, useRef} from 'react'; 5import { 6 PageStatus, 7 pageStatusAtom, 8 searchResultsAtom, 9 SearchStateCode, 10} from '../store/atoms'; 11 12export default function useSearch() { 13 const setPageStatus = useSetAtom(pageStatusAtom); 14 const [searchResults, setSearchResults] = useAtom(searchResultsAtom); 15 16 // 当前正在搜索 17 const currentQueryRef = useRef<string>(''); 18 19 /** 20 * query: 搜索词 21 * queryPage: 搜索页码 22 * type: 搜索类型 23 * pluginHash: 搜索条件 24 */ 25 const search = useCallback( 26 async function ( 27 query?: string, 28 queryPage?: number, 29 type?: ICommon.SupportMediaType, 30 pluginHash?: string, 31 ) { 32 /** 如果没有指定插件,就用所有插件搜索 */ 33 34 let plugins: Plugin[] = []; 35 if (pluginHash) { 36 const tgtPlugin = pluginManager.getPluginByHash(pluginHash); 37 tgtPlugin && (plugins = [tgtPlugin]); 38 } else { 39 plugins = pluginManager.getValidPlugins(); 40 } 41 42 // 使用选中插件搜素 43 plugins.forEach(async plugin => { 44 const _platform = plugin.instance.platform; 45 const _hash = plugin.hash; 46 if (!_platform || !_hash) { 47 // 插件无效,此时直接进入结果页 48 setPageStatus(PageStatus.RESULT); 49 return; 50 } 51 52 const searchType = type ?? plugin.instance.defaultSearchType ?? 'music'; 53 // 上一份搜索结果 54 const prevPluginResult = searchResults[searchType][plugin.hash]; 55 /** 上一份搜索还没返回/已经结束 */ 56 if ( 57 (prevPluginResult?.state === SearchStateCode.PENDING || 58 prevPluginResult?.state === SearchStateCode.FINISHED) && 59 undefined === query 60 ) { 61 return; 62 } 63 64 // 是否是一次新的搜索 65 const newSearch = 66 query || prevPluginResult?.page === undefined || queryPage === 1; 67 68 // 本次搜索关键词 69 currentQueryRef.current = query = 70 query ?? prevPluginResult?.query ?? ''; 71 72 /** 搜索的页码 */ 73 const page = 74 queryPage ?? newSearch ? 1 : (prevPluginResult?.page ?? 0) + 1; 75 76 try { 77 setSearchResults( 78 produce(draft => { 79 const prevMediaResult: any = draft[searchType]; 80 prevMediaResult[_hash] = { 81 state: newSearch 82 ? SearchStateCode.PENDING_FP 83 : SearchStateCode.PENDING, 84 // @ts-ignore 85 data: newSearch ? [] : prevMediaResult[_hash]?.data ?? [], 86 query: query, 87 page, 88 }; 89 return draft; 90 }), 91 ); 92 // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。 93 const result = await plugin?.instance?.search?.( 94 query, 95 page, 96 searchType, 97 ); 98 /** 如果搜索结果不是本次结果 */ 99 if (currentQueryRef.current !== query) { 100 return; 101 } 102 /** 切换到结果页 */ 103 setPageStatus(PageStatus.RESULT); 104 if (!result) { 105 throw new Error(); 106 } 107 setSearchResults( 108 produce(draft => { 109 const prevMediaResult = draft[searchType]; 110 const prevPluginResult: any = prevMediaResult[_hash] ?? { 111 data: [], 112 }; 113 const tagedResult = makeTag(result.data ?? [], _platform); 114 115 prevMediaResult[_hash] = { 116 state: 117 (result?.isEnd === false && result?.data?.length) 118 ? SearchStateCode.PARTLY_DONE 119 : SearchStateCode.FINISHED, 120 query, 121 page, 122 data: newSearch 123 ? tagedResult 124 : (prevPluginResult.data ?? []).concat(tagedResult), 125 }; 126 return draft; 127 }), 128 ); 129 } catch (e) { 130 console.log('SEARCH ERROR', e); 131 setPageStatus(PageStatus.RESULT); 132 setSearchResults( 133 produce(draft => { 134 const prevMediaResult = draft[searchType]; 135 const prevPluginResult = prevMediaResult[_hash] ?? {data: []}; 136 137 prevPluginResult.state = SearchStateCode.PARTLY_DONE; 138 return draft; 139 }), 140 ); 141 } 142 }); 143 }, 144 [searchResults], 145 ); 146 147 return search; 148} 149 150function makeTag<X extends Record<string, any>[] = any[]>( 151 objArray: X, 152 tag: string, 153): X { 154 objArray.forEach(_ => { 155 _.platform = tag; 156 }); 157 return objArray; 158} 159