xref: /MusicFree/src/pages/searchPage/hooks/useSearch.ts (revision 316c909695af8978fd54ffb10af86d9214d66fef)
1import {devLog, errorLog, trace} from '@/utils/log';
2import {RequestStateCode} from '@/constants/commonConst';
3import produce from 'immer';
4import {useAtom, useSetAtom} from 'jotai';
5import {useCallback, useRef} from 'react';
6import {PageStatus, pageStatusAtom, searchResultsAtom} from '../store/atoms';
7import PluginManager, {Plugin} from '@/core/pluginManager';
8
9export default function useSearch() {
10    const setPageStatus = useSetAtom(pageStatusAtom);
11    const [searchResults, setSearchResults] = useAtom(searchResultsAtom);
12
13    // 当前正在搜索
14    const currentQueryRef = useRef<string>('');
15
16    /**
17     * query: 搜索词
18     * queryPage: 搜索页码
19     * type: 搜索类型
20     * pluginHash: 搜索条件
21     */
22    const search = useCallback(
23        async function (
24            query?: string,
25            queryPage?: number,
26            type?: ICommon.SupportMediaType,
27            pluginHash?: string,
28        ) {
29            /** 如果没有指定插件,就用所有插件搜索 */
30
31            let plugins: Plugin[] = [];
32            if (pluginHash) {
33                const tgtPlugin = PluginManager.getByHash(pluginHash);
34                tgtPlugin && (plugins = [tgtPlugin]);
35            } else {
36                plugins = PluginManager.getSearchablePlugins();
37            }
38            if (plugins.length === 0) {
39                setPageStatus(PageStatus.NO_PLUGIN);
40                return;
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 =
53                    type ?? plugin.instance.defaultSearchType ?? 'music';
54                // 上一份搜索结果
55                const prevPluginResult = searchResults[searchType][plugin.hash];
56                /** 上一份搜索还没返回/已经结束 */
57                if (
58                    (prevPluginResult?.state ===
59                        RequestStateCode.PENDING_REST_PAGE ||
60                        prevPluginResult?.state ===
61                            RequestStateCode.FINISHED) &&
62                    undefined === query
63                ) {
64                    return;
65                }
66
67                // 是否是一次新的搜索
68                const newSearch =
69                    query ||
70                    prevPluginResult?.page === undefined ||
71                    queryPage === 1;
72
73                // 本次搜索关键词
74                currentQueryRef.current = query =
75                    query ?? prevPluginResult?.query ?? '';
76
77                /** 搜索的页码 */
78                const page =
79                    queryPage ?? newSearch
80                        ? 1
81                        : (prevPluginResult?.page ?? 0) + 1;
82
83                trace('开始搜索', {
84                    _platform,
85                    query,
86                    page,
87                    searchType,
88                });
89
90                try {
91                    setSearchResults(
92                        produce(draft => {
93                            const prevMediaResult: any = draft[searchType];
94                            prevMediaResult[_hash] = {
95                                state: newSearch
96                                    ? RequestStateCode.PENDING_FIRST_PAGE
97                                    : RequestStateCode.PENDING_REST_PAGE,
98                                // @ts-ignore
99                                data: newSearch
100                                    ? []
101                                    : prevMediaResult[_hash]?.data ?? [],
102                                query: query,
103                                page,
104                            };
105                        }),
106                    );
107                    // !! jscore的promise有问题,改成hermes就好了,可能和JIT有关,不知道。
108                    const result = await plugin?.methods?.search?.(
109                        query,
110                        page,
111                        searchType,
112                    );
113                    /** 如果搜索结果不是本次结果 */
114                    if (currentQueryRef.current !== query) {
115                        return;
116                    }
117                    /** 切换到结果页 */
118                    setPageStatus(PageStatus.RESULT);
119                    if (!result) {
120                        throw new Error('搜索结果为空');
121                    }
122                    setSearchResults(
123                        produce(draft => {
124                            const prevMediaResult = draft[searchType];
125                            const prevPluginResult: any = prevMediaResult[
126                                _hash
127                            ] ?? {
128                                data: [],
129                            };
130                            const currResult = result.data ?? [];
131
132                            prevMediaResult[_hash] = {
133                                state:
134                                    result?.isEnd === false &&
135                                    result?.data?.length
136                                        ? RequestStateCode.PARTLY_DONE
137                                        : RequestStateCode.FINISHED,
138                                query,
139                                page,
140                                data: newSearch
141                                    ? currResult
142                                    : (prevPluginResult.data ?? []).concat(
143                                          currResult,
144                                      ),
145                            };
146                            return draft;
147                        }),
148                    );
149                } catch (e: any) {
150                    errorLog('搜索失败', e?.message);
151                    devLog(
152                        'error',
153                        '搜索失败',
154                        `Plugin: ${plugin.name} Query: ${query} Page: ${page}`,
155                        e,
156                        e?.message,
157                    );
158
159                    setPageStatus(PageStatus.RESULT);
160                    setSearchResults(
161                        produce(draft => {
162                            const prevMediaResult = draft[searchType];
163                            const prevPluginResult = prevMediaResult[_hash] ?? {
164                                data: [],
165                            };
166
167                            prevPluginResult.state =
168                                RequestStateCode.PARTLY_DONE;
169                            return draft;
170                        }),
171                    );
172                }
173            });
174        },
175        [searchResults],
176    );
177
178    return search;
179}
180