🐛 fix: Fix market search (fix #437)

This commit is contained in:
canisminor1990
2023-11-13 22:09:14 +08:00
parent 2b59339973
commit 178b742fdc
15 changed files with 115 additions and 109 deletions
@@ -1,6 +1,6 @@
import { DraggablePanel, DraggablePanelBody, DraggablePanelContainer } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { memo, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import SafeSpacing from '@/components/SafeSpacing';
import { MARKET_SIDEBAR_WIDTH } from '@/const/layoutTokens';
@@ -31,6 +31,18 @@ const SideBar = memo(() => {
s.activateAgent,
]);
const handleExpandChange = useCallback(
(show: boolean) => {
if (!show) {
setTempId(useMarketStore.getState().currentIdentifier);
deactivateAgent();
} else if (tempId) {
activateAgent(tempId);
}
},
[deactivateAgent, activateAgent, tempId],
);
return (
<DraggablePanel
className={styles.drawer}
@@ -40,14 +52,7 @@ const SideBar = memo(() => {
expand={showAgentSidebar}
minWidth={MARKET_SIDEBAR_WIDTH}
mode={'fixed'}
onExpandChange={(show) => {
if (!show) {
setTempId(useMarketStore.getState().currentIdentifier);
deactivateAgent();
} else if (tempId) {
activateAgent(tempId);
}
}}
onExpandChange={handleExpandChange}
placement={'right'}
>
<DraggablePanelContainer
+2 -3
View File
@@ -6,18 +6,17 @@ import { FC, memo } from 'react';
import AgentCard from '@/app/market/features/AgentCard';
import ResponsiveIndex from '@/components/ResponsiveIndex';
import { AgentsMarketIndexItem } from '@/types/market';
import Index from '../index';
import Layout from './layout.desktop';
const Mobile: FC = dynamic(() => import('../(mobile)'), { ssr: false }) as FC;
export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
export default memo(() => (
<ResponsiveIndex Mobile={Mobile}>
<Layout>
<Index />
<AgentCard CardRender={SpotlightCard} defaultAgents={defaultAgents} />
<AgentCard CardRender={SpotlightCard} />
</Layout>
</ResponsiveIndex>
));
+2 -3
View File
@@ -3,15 +3,14 @@
import { memo } from 'react';
import AgentCard from '@/app/market/features/AgentCard';
import { AgentsMarketIndexItem } from '@/types/market';
import Index from '../index';
import CardRender from './features/AgentCard';
import Layout from './layout.mobile';
export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
export default memo(() => (
<Layout>
<Index />
<AgentCard CardRender={CardRender} defaultAgents={defaultAgents} mobile />
<AgentCard CardRender={CardRender} mobile />
</Layout>
));
@@ -13,13 +13,16 @@ import AgentCardBanner from './AgentCardBanner';
import { useStyles } from './style';
const { Paragraph } = Typography;
const AgentCardItem = memo<AgentsMarketIndexItem>(({ meta, identifier }) => {
const ref = useRef(null);
const isHovering = useHover(ref);
const onAgentCardClick = useMarketStore((s) => s.activateAgent);
const { avatar, title, description, tags, backgroundColor } = meta;
const { styles, theme } = useStyles();
const { isDarkMode } = useThemeMode();
const { avatar, title, description, tags, backgroundColor } = meta;
return (
<Flexbox className={styles.container} onClick={() => onAgentCardClick(identifier)}>
<AgentCardBanner meta={meta} style={{ opacity: isDarkMode ? 0.9 : 0.4 }} />
+20 -31
View File
@@ -1,71 +1,60 @@
import { SpotlightCardProps } from '@lobehub/ui';
import { FC, memo, useMemo } from 'react';
import isEqual from 'fast-deep-equal';
import { FC, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import LazyLoad from 'react-lazy-load';
import { agentMarketSelectors, useMarketStore } from '@/store/market';
import { AgentsMarketIndexItem } from '@/types/market';
import TagList from '../TagList';
import AgentCardItem from './AgentCardItem';
import Loading from './Loading';
import { useStyles } from './style';
const gridRender: SpotlightCardProps['renderItem'] = (item) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { styles } = useStyles();
return (
<LazyLoad className={styles.lazy}>
<AgentCardItem {...item} />
</LazyLoad>
);
};
export interface AgentCardProps {
CardRender: FC<SpotlightCardProps>;
defaultAgents?: AgentsMarketIndexItem[];
mobile?: boolean;
}
const AgentCard = memo<AgentCardProps>(({ defaultAgents, CardRender, mobile }) => {
const AgentCard = memo<AgentCardProps>(({ CardRender, mobile }) => {
const { t } = useTranslation('market');
const { styles } = useStyles();
const [useFetchAgentList, keywords] = useMarketStore((s) => [
s.useFetchAgentList,
s.searchKeywords,
]);
useFetchAgentList();
const { isLoading } = useFetchAgentList();
const agentList = useMarketStore(agentMarketSelectors.getAgentList, isEqual);
const { styles } = useStyles();
const clientAgentList = useMarketStore(agentMarketSelectors.getAgentList);
const agentList = clientAgentList.length === 0 && defaultAgents ? defaultAgents : clientAgentList;
const GridRender: SpotlightCardProps['renderItem'] = useCallback(
(props: any) => (
<LazyLoad className={styles.lazy}>
<AgentCardItem {...props} />
</LazyLoad>
),
[styles.lazy],
);
const agentListData = useMemo(() => {
if (!keywords) return agentList;
return agentList.filter(({ meta }) => JSON.stringify(meta).toLowerCase().includes(keywords));
}, [agentList, keywords]);
if (agentList.length === 0) return <Loading />;
if (isLoading) return <Loading />;
return (
<Flexbox gap={mobile ? 16 : 24}>
<TagList />
{keywords ? (
<CardRender
items={agentListData}
renderItem={gridRender}
items={agentList}
renderItem={GridRender}
spotlight={mobile ? undefined : false}
/>
) : (
<>
<div className={styles.subTitle}>{t('title.recentSubmits')}</div>
<CardRender items={agentListData.slice(0, 3)} renderItem={gridRender} />
<CardRender items={agentList.slice(0, 3)} renderItem={GridRender} />
<div className={styles.subTitle}>{t('title.allAgents')}</div>
<CardRender
items={agentListData.slice(3)}
renderItem={gridRender}
items={agentList.slice(3)}
renderItem={GridRender}
spotlight={mobile ? undefined : false}
/>
</>
@@ -17,7 +17,6 @@ const { Link } = Typography;
const Header = memo(() => {
const { t } = useTranslation('market');
const { styles, theme } = useStyles();
const createSession = useSessionStore((s) => s.createSession);
const switchSideBar = useGlobalStore((s) => s.switchSideBar);
const agentItem = useMarketStore(agentMarketSelectors.currentAgentItem);
@@ -22,11 +22,10 @@ const AgentModalInner = memo(() => {
s.useFetchAgent,
s.currentIdentifier,
]);
const { styles } = useStyles();
const { data, isLoading } = useFetchAgent(currentIdentifier);
const { t } = useTranslation('market');
const [tab, setTab] = useState<string>(InfoTabs.prompt);
const { data, isLoading } = useFetchAgent(currentIdentifier);
const { styles } = useStyles();
if (isLoading || !data?.meta) return <Loading />;
@@ -1,22 +1,27 @@
import { SearchBar } from '@lobehub/ui';
import { useResponsive } from 'antd-style';
import { memo, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useMarketStore } from '@/store/market';
const AgentSearchBar = memo(() => {
const { t } = useTranslation('market');
const keywords = useMarketStore((s) => s.searchKeywords);
const [keywords, setKeywords] = useMarketStore((s) => [s.searchKeywords, s.setSearchKeywords]);
const [value, setValue] = useState(keywords);
const { mobile } = useResponsive();
const handleSearch = useCallback(() => {
setKeywords(value);
}, [value, setKeywords]);
return (
<SearchBar
allowClear
enableShortKey={!mobile}
onChange={(e) => setValue(e.target.value)}
onPressEnter={() => useMarketStore.setState({ searchKeywords: value })}
onSubmit={() => useMarketStore.setState({ searchKeywords: value })}
onPressEnter={handleSearch}
onSubmit={handleSearch}
placeholder={t('search.placeholder')}
shortKey={'k'}
spotlight={!mobile}
+28
View File
@@ -0,0 +1,28 @@
import { Button } from 'antd';
import isEqual from 'fast-deep-equal';
import { startCase } from 'lodash-es';
import { memo } from 'react';
import { agentMarketSelectors, useMarketStore } from '@/store/market';
const Inner = memo(() => {
const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
const [keywords, setSearchKeywords] = useMarketStore((s) => [
s.searchKeywords,
s.setSearchKeywords,
]);
return agentTagList.map((item) => (
<Button
key={item}
onClick={() => setSearchKeywords(item)}
shape={'round'}
size={'small'}
type={keywords === item ? 'primary' : 'default'}
>
{startCase(item)}
</Button>
));
});
export default Inner;
+12 -23
View File
@@ -1,32 +1,21 @@
import { Button, Skeleton } from 'antd';
import isEqual from 'fast-deep-equal';
import { startCase } from 'lodash-es';
import { memo } from 'react';
import { Skeleton } from 'antd';
import { Suspense, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { agentMarketSelectors, useMarketStore } from '@/store/market';
import Inner from '@/app/market/features/TagList/Inner';
const TagList = memo(() => {
const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
const keywords = useMarketStore((s) => s.searchKeywords);
return (
<Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
{agentTagList?.length > 0
? agentTagList.map((item) => (
<Button
key={item}
onClick={() => useMarketStore.setState({ searchKeywords: item })}
shape={'round'}
size={'small'}
type={keywords === item ? 'primary' : 'default'}
>
{startCase(item)}
</Button>
))
: Array.from({ length: 5 })
.fill('')
.map((_, index) => <Skeleton.Button key={index} shape={'round'} size={'small'} />)}
<Suspense
fallback={Array.from({ length: 5 })
.fill('')
.map((_, index) => (
<Skeleton.Button key={index} shape={'round'} size={'small'} />
))}
>
<Inner />
</Suspense>
</Flexbox>
);
});
+9 -1
View File
@@ -12,6 +12,7 @@ import type { Store } from './store';
export interface StoreAction {
activateAgent: (identifier: string) => void;
deactivateAgent: () => void;
setSearchKeywords: (keywords: string) => void;
updateAgentMap: (key: string, value: AgentsMarketItem) => void;
useFetchAgent: (identifier: string) => SWRResponse<AgentsMarketItem>;
useFetchAgentList: () => SWRResponse<LobeChatAgentsMarketIndex>;
@@ -29,6 +30,9 @@ export const createMarketAction: StateCreator<
deactivateAgent: () => {
set({ currentIdentifier: undefined }, false, 'deactivateAgent');
},
setSearchKeywords: (keywords) => {
set({ searchKeywords: keywords });
},
updateAgentMap: (key, value) => {
const { agentMap } = get();
@@ -56,7 +60,11 @@ export const createMarketAction: StateCreator<
useFetchAgentList: () =>
useSWR<LobeChatAgentsMarketIndex>(getCurrentLanguage(), getAgentList, {
onSuccess: (agentMarketIndex) => {
set({ agentList: agentMarketIndex.agents }, false, 'useFetchAgentList');
set(
{ agentList: agentMarketIndex.agents, tagList: agentMarketIndex.tags },
false,
'useFetchAgentList',
);
},
}),
});
+2
View File
@@ -7,6 +7,7 @@ export interface StoreState {
agentMap: MarketAgentMap;
currentIdentifier: string;
searchKeywords: string;
tagList: string[];
}
export const initialState: StoreState = {
@@ -14,4 +15,5 @@ export const initialState: StoreState = {
agentMap: {},
currentIdentifier: '',
searchKeywords: '',
tagList: [],
};
+10 -8
View File
@@ -1,17 +1,19 @@
import { flatten } from 'lodash-es';
import { DEFAULT_AGENTS_MARKET_ITEM } from '@/const/market';
import { AgentsMarketItem } from '@/types/market';
import { findDuplicates } from '@/utils/findDuplicates';
import type { Store } from './store';
const getAgentList = (s: Store) => s.agentList;
const getAgentTagList = (s: Store) => {
const agentList = s.agentList;
const rawAgentTagList = flatten(agentList.map((item) => item.meta.tags)) as string[];
return findDuplicates(rawAgentTagList);
const getAgentList = (s: Store) => {
const { searchKeywords, agentList } = s;
if (!searchKeywords) return agentList;
return agentList.filter(({ meta }) => {
const checkMeta: string = [meta.tags, meta.title, meta.description, meta.avatar]
.filter(Boolean)
.join('');
return checkMeta.toLowerCase().includes(searchKeywords.toLowerCase());
});
};
const getAgentTagList = (s: Store) => s.tagList;
const getAgentItemById = (d: string) => (s: Store) => s.agentMap[d];
+1
View File
@@ -16,4 +16,5 @@ export type AgentsMarketItem = AgentsMarketIndexItem & LobeAgentSettings;
export interface LobeChatAgentsMarketIndex {
agents: AgentsMarketIndexItem[];
schemaVersion: 1;
tags: string[];
}
-22
View File
@@ -1,22 +0,0 @@
export const findDuplicates = (arr: string[]): string[] => {
const duplicates: { [key: string]: number } = {};
// 统计每个项目出现的次数
for (const item of arr) {
if (duplicates[item]) {
duplicates[item]++;
} else {
duplicates[item] = 1;
}
}
// 挑出重复出现 3 次以上的项目
const COUNT = 3;
const result = Object.keys(duplicates).filter((item) => duplicates[item] >= COUNT);
// 按重复次数从多到少排序
result.sort((a, b) => duplicates[b] - duplicates[a]);
return result;
};