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