xref: /MusicFree/src/pages/searchPage/hooks/useSearch.ts (revision ad2ad8ff0c73a593f8d871aeafe032c5ad4c688c)
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              draft[_hash] = prev;
49            }),
50          );
51          // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。
52          const result = await plugin?.instance?.search?.(query, page);
53          setPageStatus(PageStatus.RESULT);
54          if (!result) {
55            throw new Error();
56          }
57          setSearchResults(
58            produce(draft => {
59              const prev = draft[_hash] ?? {};
60              if (result._isEnd === false) {
61                prev.state = 'resolved';
62              } else {
63                prev.state = 'done';
64              }
65              prev.result = newSearch
66                ? mergeResult(result, {}, _platform)
67                : mergeResult(prev.result ?? {}, result ?? {}, _platform);
68              draft[_hash] = {
69                state: prev.state,
70                result: prev.result,
71                query: query,
72                currentPage: page,
73              };
74              return draft;
75            }),
76          );
77        } catch (e) {
78          console.log('SEARCH ERROR', e);
79          setSearchResults(
80            produce(draft => {
81              const prev = draft[_hash] ?? {};
82              prev.state = 'resolved';
83              draft[_hash] = prev;
84            }),
85          );
86        }
87      });
88    },
89    [searchResults],
90  );
91
92  return search;
93}
94
95// todo: 去重
96const resultKeys: (keyof IPlugin.ISearchResult)[] = ['album', 'music'];
97function mergeResult(
98  obj1: Record<string, any>,
99  obj2: Record<string, any>,
100  platform: string,
101): IPlugin.ISearchResult {
102  const result: Record<string, any> = {};
103  for (let k of resultKeys) {
104    result[k] = (obj1[k] ?? [])
105      .map((_: any) =>
106        produce(_, (_: any) => {
107          _.platform = platform;
108        }),
109      )
110      .concat(
111        (obj2[k] ?? []).map((_: any) =>
112          produce(_, (_: any) => {
113            _.platform = platform;
114          }),
115        ),
116      );
117  }
118  return result;
119}
120