xref: /MusicFree/src/pages/searchPage/hooks/useSearch.ts (revision 0b940038696677ddf4d2a91434ce49a8383fecf4)
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