xref: /MusicFree/src/pages/searchPage/hooks/useSearch.ts (revision 41811320cf3c14ef271576d2c7eea9fc56b62f36)
1import {pluginManager, usePlugins} from '@/common/pluginManager';
2import produce from 'immer';
3import {useAtom, useSetAtom} from 'jotai';
4import {useCallback} 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  const search = useCallback(
12    async function (
13      query?: string,
14      platformHash = 'all',
15      queryPage = undefined,
16    ) {
17      // 如果没有搜索结果缓存,那就是没有搜过
18      const installedPlugins = pluginManager.getValidPlugins();
19      const plugins =
20        platformHash === 'all'
21          ? installedPlugins
22          : [installedPlugins.find(_ => _.hash === platformHash)];
23      plugins.forEach(async plugin => {
24        const _platform = plugin?.instance.platform;
25        const _hash = plugin?.hash;
26        if (!plugin || !_platform || !_hash) {
27          // 没有插件,此时直接进入结果页
28          setPageStatus(PageStatus.RESULT);
29          return;
30        }
31        const _prevResult = searchResults[_hash] ?? {};
32        if (_prevResult.state === 'pending' || _prevResult.state === 'done') {
33          return;
34        }
35
36        const newSearch =
37          query || _prevResult?.currentPage === undefined || queryPage === 1;
38        query = query ?? _prevResult?.query ?? '';
39        const page =
40          queryPage ?? newSearch ? 1 : (_prevResult.currentPage ?? 0) + 1;
41
42        try {
43          setSearchResults(prevState =>
44            produce(prevState, draft => {
45              const prev = draft[_hash] ?? {};
46              prev.query = query;
47              prev.state = 'pending';
48              prev.result = newSearch ? {} : prev.result
49              draft[_hash] = prev;
50            }),
51          );
52          // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。
53          const result = await plugin?.instance?.search?.(query, page);
54          setPageStatus(PageStatus.RESULT);
55          if (!result) {
56            throw new Error();
57          }
58          setSearchResults(
59            produce(draft => {
60              const prev = draft[_hash] ?? {};
61              if (result._isEnd === false) {
62                prev.state = 'resolved';
63              } else {
64                prev.state = 'done';
65              }
66              prev.result = newSearch
67                ? mergeResult(result, {}, _platform)
68                : mergeResult(prev.result ?? {}, result ?? {}, _platform);
69              draft[_hash] = {
70                state: prev.state,
71                result: prev.result,
72                query: query,
73                currentPage: page,
74              };
75              return draft;
76            }),
77          );
78        } catch (e) {
79          console.log('SEARCH ERROR', e);
80          setSearchResults(
81            produce(draft => {
82              const prev = draft[_hash] ?? {};
83              prev.state = 'resolved';
84              draft[_hash] = prev;
85            }),
86          );
87        }
88      });
89    },
90    [searchResults],
91  );
92
93  return search;
94}
95
96// todo: 去重
97const resultKeys: (keyof IPlugin.ISearchResult)[] = ['album', 'music'];
98function mergeResult(
99  obj1: Record<string, any>,
100  obj2: Record<string, any>,
101  platform: string,
102): IPlugin.ISearchResult {
103  const result: Record<string, any> = {};
104  for (let k of resultKeys) {
105    result[k] = (obj1[k] ?? [])
106      .map((_: any) =>
107        produce(_, (_: any) => {
108          _.platform = platform;
109        }),
110      )
111      .concat(
112        (obj2[k] ?? []).map((_: any) =>
113          produce(_, (_: any) => {
114            _.platform = platform;
115          }),
116        ),
117      );
118  }
119  return result;
120}
121