Skip to content

Commit

Permalink
✨ feat(wip): Add Welcome Guide
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Apr 23, 2024
1 parent 2d52303 commit 7c27767
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 5 deletions.
105 changes: 105 additions & 0 deletions src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx
@@ -0,0 +1,105 @@
'use client';

import { ActionIcon, Avatar, Grid } from '@lobehub/ui';
import { Skeleton, Typography } from 'antd';
import { createStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { RefreshCw } from 'lucide-react';
import Link from 'next/link';
import { memo, useMemo, useState } from 'react';
import { Flexbox } from 'react-layout-kit';

import { agentMarketSelectors, useMarketStore } from '@/store/market';

const { Paragraph } = Typography;

const useStyles = createStyles(({ css, token }) => ({
card: css`
position: relative;
height: 100%;
padding: 16px;
color: ${token.colorText};
background: ${token.colorFillTertiary};
border-radius: ${token.borderRadius}px;
&:hover {
background: ${token.colorFillSecondary};
}
`,
cardDesc: css`
margin-block: 0 !important;
color: ${token.colorTextDescription};
`,
cardTitle: css`
margin-block: 0 !important;
font-size: 16px;
font-weight: bold;
`,
icon: css`
color: ${token.colorTextSecondary};
`,
title: css`
color: ${token.colorTextDescription};
`,
}));

const AgentsSuggest = memo(() => {
const [sliceStart, setSliceStart] = useState(0);
const useFetchAgentList = useMarketStore((s) => s.useFetchAgentList);
const { isLoading } = useFetchAgentList();
const agentList = useMarketStore((s) => agentMarketSelectors.getAgentList(s), isEqual);
const { styles } = useStyles();

const loadingCards = Array.from({ length: 4 }).map((_, index) => (
<Flexbox className={styles.card} key={index}>
<Skeleton active avatar paragraph={{ rows: 2 }} title={false} />
</Flexbox>
));

const cards = useMemo(
() =>
agentList.slice(sliceStart, sliceStart + 4).map((agent) => (
<Link href={`/market?agent=${agent.identifier}`} key={agent.identifier}>
<Flexbox className={styles.card} gap={8} horizontal>
<Avatar avatar={agent.meta.avatar} style={{ flex: 'none' }} />
<Flexbox gap={8}>
<Paragraph className={styles.cardTitle} ellipsis={{ rows: 1 }}>
{agent.meta.title}
</Paragraph>
<Paragraph className={styles.cardDesc} ellipsis={{ rows: 2 }}>
{agent.meta.description}
</Paragraph>
</Flexbox>
</Flexbox>
</Link>
)),
[agentList, sliceStart],
);

const handleRefresh = () => {
if (!agentList) return;
setSliceStart(Math.floor((Math.random() * agentList.length) / 2));
};

return (
<Flexbox gap={8} width={'100%'}>
<Flexbox align={'center'} horizontal justify={'space-between'}>
<div className={styles.title}>新增助理推荐:</div>
<ActionIcon
icon={RefreshCw}
onClick={handleRefresh}
size={{ blockSize: 24, fontSize: 14 }}
title={'换一批'}
/>
</Flexbox>
<Grid gap={8} rows={2}>
{isLoading ? loadingCards : cards}
</Grid>
</Flexbox>
);
});

export default AgentsSuggest;
89 changes: 89 additions & 0 deletions src/features/Conversation/components/InboxWelcome/FeatureCards.tsx
@@ -0,0 +1,89 @@
'use client';

import { ActionIcon, Grid, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { ArrowRight, Blocks, Bot, BrainCircuit, Eye, Images, Mic2 } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import urlJoin from 'url-join';

const BASE_DOC_URL = 'https://lobehub.com/docs/usage/features';

const useStyles = createStyles(({ css, token }) => ({
card: css`
padding: 16px;
color: ${token.colorText};
background: ${token.colorFillTertiary};
border-radius: ${token.borderRadius}px;
&:hover {
background: ${token.colorFillSecondary};
}
`,
icon: css`
color: ${token.colorTextSecondary};
`,
title: css`
color: ${token.colorTextDescription};
`,
}));

const FeatureCards = memo(() => {
const { styles } = useStyles();
const cards = [
{
icon: Eye,
title: '视觉识别',
url: 'vision',
},
{
icon: Mic2,
title: 'TTS & STT',
url: 'tts',
},
{
icon: Images,
title: '文生图',
url: 'text-to-image',
},
{
icon: Blocks,
title: '插件系统',
url: 'plugin-system',
},
{
icon: Bot,
title: '助手市场',
url: 'agent-market',
},
{
icon: BrainCircuit,
title: '多模型服务商',
url: 'multi-ai-providers',
},
];

return (
<Flexbox gap={8} width={'100%'}>
<Flexbox align={'center'} horizontal justify={'space-between'}>
<div className={styles.title}>查看我的能力:</div>
<Link href={urlJoin(BASE_DOC_URL, 'start')} target={'_blank'}>
<ActionIcon icon={ArrowRight} size={{ blockSize: 24, fontSize: 16 }} title={'了解更多'} />
</Link>
</Flexbox>
<Grid gap={8} maxItemWidth={160}>
{cards.map((card) => (
<Link href={urlJoin(BASE_DOC_URL, card.url)} key={card.url} target={'_blank'}>
<Flexbox align={'center'} className={styles.card} gap={8} horizontal>
<Icon className={styles.icon} icon={card.icon} size={{ fontSize: 18 }} />
{card.title}
</Flexbox>
</Link>
))}
</Grid>
</Flexbox>
);
});

export default FeatureCards;
52 changes: 52 additions & 0 deletions src/features/Conversation/components/InboxWelcome/index.tsx
@@ -0,0 +1,52 @@
'use client';

import { FluentEmoji } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { memo } from 'react';
import { Center, Flexbox } from 'react-layout-kit';

import AgentsSuggest from './AgentsSuggest';
import FeatureCards from './FeatureCards';

const useStyles = createStyles(({ css, responsive }) => ({
container: css`
align-items: center;
${responsive.mobile} {
align-items: flex-start;
}
`,
desc: css`
font-size: 14px;
`,
title: css`
margin-top: 0.2em;
margin-bottom: 0;
font-size: 32px;
font-weight: bolder;
line-height: 1;
${responsive.mobile} {
font-size: 24px;
}
`,
}));

const InboxWelcome = memo(() => {
const { styles } = useStyles();
return (
<Center padding={24} width={'100%'}>
<Flexbox className={styles.container} gap={16} style={{ maxWidth: 800 }} width={'100%'}>
<Flexbox align={'center'} gap={8} horizontal>
<FluentEmoji emoji={'👋'} size={40} type={'anim'} />
<h1 className={styles.title}>下午好</h1>
</Flexbox>
<p className={styles.desc}>我是 LobeChat 你的私人智能助理,我今天能帮你做什么?</p>
<AgentsSuggest />
<FeatureCards />
{/*{t('inbox.defaultMessage')}*/}
</Flexbox>
</Center>
);
});

export default InboxWelcome;
15 changes: 11 additions & 4 deletions src/features/Conversation/components/VirtualizedList/index.tsx
Expand Up @@ -9,10 +9,15 @@ import { isMobileScreen } from '@/utils/screen';

import AutoScroll from '../AutoScroll';
import Item from '../ChatItem';
import InboxWelcome from '../InboxWelcome';

const WELCOME_ID = 'welcome';

const itemContent = (index: number, id: string) => {
const isMobile = isMobileScreen();

if (id === WELCOME_ID) return <InboxWelcome />;

return index === 0 ? (
<div style={{ height: 24 + (isMobile ? 0 : 64) }} />
) : (
Expand All @@ -27,15 +32,17 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
const virtuosoRef = useRef<VirtuosoHandle>(null);
const [atBottom, setAtBottom] = useState(true);

const data = useChatStore(
(s) => ['empty', ...chatSelectors.currentChatIDsWithGuideMessage(s)],
isEqual,
);
const [id, chatLoading] = useChatStore((s) => [
chatSelectors.currentChatKey(s),
chatSelectors.currentChatLoadingState(s),
]);

const data = useChatStore((s) => {
const showInboxWelcome = chatSelectors.showInboxWelcome(s);
const ids = showInboxWelcome ? [WELCOME_ID] : chatSelectors.currentChatIDsWithGuideMessage(s);
return ['empty', ...ids];
}, isEqual);

useEffect(() => {
if (virtuosoRef.current) {
virtuosoRef.current.scrollToIndex({ align: 'end', behavior: 'auto', index: 'LAST' });
Expand Down
12 changes: 11 additions & 1 deletion src/store/chat/slices/message/selectors.ts
Expand Up @@ -50,6 +50,15 @@ const currentChats = (s: ChatStore): ChatMessage[] => {
};

const initTime = Date.now();

const showInboxWelcome = (s: ChatStore): boolean => {
const isInbox = s.activeId === INBOX_SESSION_ID;
if (!isInbox) return false;
const data = currentChats(s);
const isBrandNewChat = data.length === 0;
return isBrandNewChat;
};

// 针对新助手添加初始化时的自定义消息
const currentChatsWithGuideMessage =
(meta: MetaData) =>
Expand All @@ -62,7 +71,7 @@ const currentChatsWithGuideMessage =

const [activeId, isInbox] = [s.activeId, s.activeId === INBOX_SESSION_ID];

const inboxMsg = t('inbox.defaultMessage', { ns: 'chat' });
const inboxMsg = '';
const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
name: meta.title || t('defaultAgent'),
ns: 'chat',
Expand Down Expand Up @@ -135,4 +144,5 @@ export const chatSelectors = {
getMessageById,
getTraceIdByMessageId,
latestMessage,
showInboxWelcome,
};

0 comments on commit 7c27767

Please sign in to comment.