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