mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
🐛 fix: layout recent locale and support dismiss banner (#13739)
* fix: CN locale for rencents * fix: community profile setup modal * feat: support skill banner dismiss
This commit is contained in:
parent
732a3ae54a
commit
08769e5bf1
9 changed files with 74 additions and 24 deletions
|
|
@ -564,6 +564,7 @@
|
|||
"skillDetail.tabs.tools": "Capabilities",
|
||||
"skillDetail.tools": "Tools",
|
||||
"skillDetail.trustWarning": "Only use connectors from developers you trust. LobeHub does not control which tools developers make available and cannot verify that they will work as intended or that they won't change.",
|
||||
"skillInstallBanner.dismiss": "Dismiss",
|
||||
"skillInstallBanner.title": "Add skills to Lobe AI",
|
||||
"store.actions.cancel": "Cancel",
|
||||
"store.actions.configure": "Configure",
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@
|
|||
"promptTransform.actions.translate": "翻译",
|
||||
"promptTransform.status.rewrite": "正在丰富细节...",
|
||||
"promptTransform.status.translate": "正在翻译...",
|
||||
"recents": "Recents",
|
||||
"recents": "最近",
|
||||
"regenerate": "重新生成",
|
||||
"releaseNotes": "版本详情",
|
||||
"rename": "重命名",
|
||||
|
|
|
|||
|
|
@ -564,6 +564,7 @@
|
|||
"skillDetail.tabs.tools": "技能功能",
|
||||
"skillDetail.tools": "工具",
|
||||
"skillDetail.trustWarning": "请仅使用您信任的开发者提供的连接器。LobeHub 无法控制开发者提供哪些工具,也无法验证这些工具是否按预期工作或是否会发生变化。",
|
||||
"skillInstallBanner.dismiss": "不再显示",
|
||||
"skillInstallBanner.title": "为 Lobe AI 添加技能",
|
||||
"store.actions.cancel": "取消安装",
|
||||
"store.actions.configure": "配置",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { Center, Flexbox, Icon, Input, Modal, Text, TextArea, Tooltip } from '@lobehub/ui';
|
||||
import { Center, Flexbox, Icon, Input, Text, TextArea, Tooltip } from '@lobehub/ui';
|
||||
import { Modal } from '@lobehub/ui/base-ui';
|
||||
import { type UploadProps } from 'antd';
|
||||
import { App, Form, Modal as AntModal, Upload } from 'antd';
|
||||
import { cssVar } from 'antd-style';
|
||||
|
|
|
|||
|
|
@ -576,6 +576,7 @@ export default {
|
|||
'skillDetail.tools': 'Tools',
|
||||
'skillDetail.trustWarning':
|
||||
"Only use connectors from developers you trust. LobeHub does not control which tools developers make available and cannot verify that they will work as intended or that they won't change.",
|
||||
'skillInstallBanner.dismiss': 'Dismiss',
|
||||
'skillInstallBanner.title': 'Add skills to Lobe AI',
|
||||
'store.actions.cancel': 'Cancel',
|
||||
'store.actions.configure': 'Configure',
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
'use client';
|
||||
|
||||
import { getKlavisServerByServerIdentifier, getLobehubSkillProviderById } from '@lobechat/const';
|
||||
import { Flexbox, Icon } from '@lobehub/ui';
|
||||
import { ActionIcon, Flexbox, Icon } from '@lobehub/ui';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { Blocks } from 'lucide-react';
|
||||
import { Blocks, X } from 'lucide-react';
|
||||
import React, { createElement, memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { createSkillStoreModal } from '@/features/SkillStore';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
import { useToolStore } from '@/store/tool';
|
||||
|
||||
// Bump this id when the banner content changes so dismissing the old
|
||||
// variant does not hide the new one.
|
||||
export const SKILL_INSTALL_BANNER_ID = 'skill-install-v1';
|
||||
|
||||
const ICON_SIZE = 16;
|
||||
const AVATAR_SIZE = 24;
|
||||
|
||||
|
|
@ -45,7 +50,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|||
|
||||
margin-block-end: -6px;
|
||||
padding-block: 42px 10px;
|
||||
padding-inline: 16px;
|
||||
padding-inline: 16px 12px;
|
||||
border: 1px solid ${cssVar.colorFillSecondary};
|
||||
border-radius: 20px;
|
||||
|
||||
|
|
@ -80,6 +85,8 @@ const SkillInstallBanner = memo(() => {
|
|||
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
|
||||
const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
|
||||
|
||||
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
||||
|
||||
// Prefetch skill connections data so SkillStore opens faster
|
||||
const [useFetchLobehubSkillConnections, useFetchUserKlavisServers] = useToolStore((s) => [
|
||||
s.useFetchLobehubSkillConnections,
|
||||
|
|
@ -112,29 +119,49 @@ const SkillInstallBanner = memo(() => {
|
|||
createSkillStoreModal();
|
||||
}, []);
|
||||
|
||||
const handleDismiss = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const current = useGlobalStore.getState().status.dismissedBannerIds || [];
|
||||
if (current.includes(SKILL_INSTALL_BANNER_ID)) return;
|
||||
updateSystemStatus({
|
||||
dismissedBannerIds: [...current, SKILL_INSTALL_BANNER_ID],
|
||||
});
|
||||
},
|
||||
[updateSystemStatus],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.banner} data-testid="skill-install-banner" onClick={handleOpenStore}>
|
||||
<Flexbox horizontal align="center" gap={4}>
|
||||
<Icon className={styles.icon} icon={Blocks} size={18} />
|
||||
<span className={styles.text}>{t('skillInstallBanner.title')}</span>
|
||||
</Flexbox>
|
||||
{skillIcons.length > 0 && (
|
||||
<div className={styles.iconGroup}>
|
||||
{skillIcons.map(({ icon, key }, index) => (
|
||||
<div
|
||||
className={styles.avatar}
|
||||
key={key}
|
||||
style={{ marginLeft: index === 0 ? 0 : -6, zIndex: index }}
|
||||
>
|
||||
{typeof icon === 'string' ? (
|
||||
<img alt={key} height={ICON_SIZE} src={icon} width={ICON_SIZE} />
|
||||
) : (
|
||||
createElement(icon, { size: ICON_SIZE })
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Flexbox horizontal align="center" gap={8}>
|
||||
{skillIcons.length > 0 && (
|
||||
<div className={styles.iconGroup}>
|
||||
{skillIcons.map(({ icon, key }, index) => (
|
||||
<div
|
||||
className={styles.avatar}
|
||||
key={key}
|
||||
style={{ marginLeft: index === 0 ? 0 : -6, zIndex: index }}
|
||||
>
|
||||
{typeof icon === 'string' ? (
|
||||
<img alt={key} height={ICON_SIZE} src={icon} width={ICON_SIZE} />
|
||||
) : (
|
||||
createElement(icon, { size: ICON_SIZE })
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<ActionIcon
|
||||
icon={X}
|
||||
size="small"
|
||||
title={t('skillInstallBanner.dismiss')}
|
||||
onClick={handleDismiss}
|
||||
/>
|
||||
</Flexbox>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@ import { ChatInputProvider, DesktopChatInput } from '@/features/ChatInput';
|
|||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentByIdSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { systemStatusSelectors } from '@/store/global/selectors';
|
||||
import { useHomeStore } from '@/store/home';
|
||||
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
|
||||
import CommunityRecommend from '../CommunityRecommend';
|
||||
import SuggestQuestions from '../SuggestQuestions';
|
||||
import ModeTag from './ModeTag';
|
||||
import SkillInstallBanner from './SkillInstallBanner';
|
||||
import SkillInstallBanner, { SKILL_INSTALL_BANNER_ID } from './SkillInstallBanner';
|
||||
import StarterList from './StarterList';
|
||||
import { useSend } from './useSend';
|
||||
|
||||
|
|
@ -25,7 +27,10 @@ const InputArea = () => {
|
|||
const inputActiveMode = useHomeStore((s) => s.inputActiveMode);
|
||||
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
|
||||
const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
|
||||
const showSkillBanner = isLobehubSkillEnabled || isKlavisEnabled;
|
||||
const isSkillBannerDismissed = useGlobalStore(
|
||||
systemStatusSelectors.isBannerDismissed(SKILL_INSTALL_BANNER_ID),
|
||||
);
|
||||
const showSkillBanner = (isLobehubSkillEnabled || isKlavisEnabled) && !isSkillBannerDismissed;
|
||||
const chatInputRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// When a starter mode is activated (e.g. Create Agent / Create Group / Write),
|
||||
|
|
|
|||
|
|
@ -99,6 +99,11 @@ export interface SystemStatus {
|
|||
chatInputHeight?: number;
|
||||
disabledModelProvidersSortType?: string;
|
||||
disabledModelsSortType?: string;
|
||||
/**
|
||||
* IDs of banners/ads the user has dismissed. New banners use a new ID
|
||||
* so dismissing the current one does not hide future ones.
|
||||
*/
|
||||
dismissedBannerIds?: string[];
|
||||
expandInputActionbar?: boolean;
|
||||
// which sessionGroup should expand
|
||||
expandSessionGroupKeys: string[];
|
||||
|
|
@ -237,6 +242,7 @@ export const INITIAL_STATUS = {
|
|||
recentPageSize: 5,
|
||||
disabledModelProvidersSortType: 'default',
|
||||
disabledModelsSortType: 'default',
|
||||
dismissedBannerIds: [],
|
||||
expandInputActionbar: true,
|
||||
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
||||
fileManagerViewMode: 'list' as const,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,13 @@ const isNotificationRead =
|
|||
const slugs = s.status.readNotificationSlugs || [];
|
||||
return slugs.includes(slug);
|
||||
};
|
||||
|
||||
const isBannerDismissed =
|
||||
(bannerId: string) =>
|
||||
(s: GlobalState): boolean => {
|
||||
const ids = s.status.dismissedBannerIds || [];
|
||||
return ids.includes(bannerId);
|
||||
};
|
||||
const tokenDisplayFormatShort = (s: GlobalState) =>
|
||||
s.status.tokenDisplayFormatShort !== undefined ? s.status.tokenDisplayFormatShort : true;
|
||||
|
||||
|
|
@ -95,6 +102,7 @@ export const systemStatusSelectors = {
|
|||
imageTopicViewMode,
|
||||
imageTopicPanelWidth,
|
||||
inZenMode,
|
||||
isBannerDismissed,
|
||||
isNotificationRead,
|
||||
isShowCredit,
|
||||
isStatusInit,
|
||||
|
|
|
|||
Loading…
Reference in a new issue