mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-24 05:07:06 +08:00
feat(core): show ai-island and navigate to chat page if not available in sidebar (#13085)
close AI-318, AI-317 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Updated the AI chat button label to "AFFiNE Intelligence" and changed its icon for improved clarity. * Enhanced the AI chat button's placement in the sidebar for better accessibility. * Improved the AI chat button’s visibility and interaction logic based on current view and sidebar state. * **Style** * Adjusted button styles to disable interaction when hidden, enhancing user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -18,7 +18,7 @@ import { useI18n } from '@affine/i18n';
|
|||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import type { Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
import {
|
import {
|
||||||
AiIcon,
|
AiOutlineIcon,
|
||||||
AllDocsIcon,
|
AllDocsIcon,
|
||||||
ImportIcon,
|
ImportIcon,
|
||||||
JournalIcon,
|
JournalIcon,
|
||||||
@@ -97,8 +97,8 @@ const AIChatButton = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuLinkItem icon={<AiIcon />} active={aiChatActive} to={'/chat'}>
|
<MenuLinkItem icon={<AiOutlineIcon />} active={aiChatActive} to={'/chat'}>
|
||||||
<span data-testid="ai-chat">Intelligent</span>
|
<span data-testid="ai-chat">AFFiNE Intelligence</span>
|
||||||
</MenuLinkItem>
|
</MenuLinkItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -198,10 +198,10 @@ export const RootAppSidebar = memo((): ReactElement => {
|
|||||||
/>
|
/>
|
||||||
<AddPageButton />
|
<AddPageButton />
|
||||||
</div>
|
</div>
|
||||||
<AIChatButton />
|
|
||||||
<AllDocsButton />
|
<AllDocsButton />
|
||||||
<AppSidebarJournalButton />
|
<AppSidebarJournalButton />
|
||||||
{sessionStatus === 'authenticated' && <NotificationButton />}
|
{sessionStatus === 'authenticated' && <NotificationButton />}
|
||||||
|
<AIChatButton />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data-testid="slider-bar-workspace-setting-button"
|
data-testid="slider-bar-workspace-setting-button"
|
||||||
icon={<SettingsIcon />}
|
icon={<SettingsIcon />}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { IslandContainer } from './container';
|
import { IslandContainer } from './container';
|
||||||
import { AIIcon } from './icons';
|
import { AIIcon } from './icons';
|
||||||
import { aiIslandBtn, aiIslandWrapper, toolStyle } from './styles.css';
|
import { aiIslandBtn, aiIslandWrapper, toolStyle } from './styles.css';
|
||||||
|
|
||||||
|
const hideChat: Array<string | ((path: string) => boolean)> = [
|
||||||
|
'/chat',
|
||||||
|
path => path.includes('attachments'),
|
||||||
|
];
|
||||||
|
|
||||||
export const AIIsland = () => {
|
export const AIIsland = () => {
|
||||||
// to make sure ai island is hidden first and animate in
|
// to make sure ai island is hidden first and animate in
|
||||||
const [hide, setHide] = useState(true);
|
const [hide, setHide] = useState(true);
|
||||||
@@ -16,12 +21,33 @@ export const AIIsland = () => {
|
|||||||
const haveChatTab = useLiveData(
|
const haveChatTab = useLiveData(
|
||||||
activeView.sidebarTabs$.map(tabs => tabs.some(t => t.id === 'chat'))
|
activeView.sidebarTabs$.map(tabs => tabs.some(t => t.id === 'chat'))
|
||||||
);
|
);
|
||||||
|
const activeLocation = useLiveData(activeView.location$);
|
||||||
const activeTab = useLiveData(activeView.activeSidebarTab$);
|
const activeTab = useLiveData(activeView.activeSidebarTab$);
|
||||||
const sidebarOpen = useLiveData(workbench.sidebarOpen$);
|
const sidebarOpen = useLiveData(workbench.sidebarOpen$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHide((sidebarOpen && activeTab?.id === 'chat') || !haveChatTab);
|
let hide = true;
|
||||||
}, [activeTab, haveChatTab, sidebarOpen]);
|
if (haveChatTab) {
|
||||||
|
hide = !!sidebarOpen && activeTab?.id === 'chat';
|
||||||
|
} else {
|
||||||
|
const path = activeLocation.pathname;
|
||||||
|
hide = hideChat.some(item =>
|
||||||
|
typeof item === 'string' ? path === item : item(path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setHide(hide);
|
||||||
|
}, [activeLocation.pathname, activeTab, haveChatTab, sidebarOpen]);
|
||||||
|
|
||||||
|
const onOpenChat = useCallback(() => {
|
||||||
|
if (hide) return;
|
||||||
|
if (haveChatTab) {
|
||||||
|
workbench.openSidebar();
|
||||||
|
activeView.activeSidebarTab('chat');
|
||||||
|
} else {
|
||||||
|
workbench.open('/chat');
|
||||||
|
workbench.closeSidebar();
|
||||||
|
}
|
||||||
|
}, [activeView, haveChatTab, hide, workbench]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IslandContainer className={clsx(toolStyle, { hide })}>
|
<IslandContainer className={clsx(toolStyle, { hide })}>
|
||||||
@@ -29,11 +55,7 @@ export const AIIsland = () => {
|
|||||||
<button
|
<button
|
||||||
className={aiIslandBtn}
|
className={aiIslandBtn}
|
||||||
data-testid="ai-island"
|
data-testid="ai-island"
|
||||||
onClick={() => {
|
onClick={onOpenChat}
|
||||||
if (hide) return;
|
|
||||||
workbench.openSidebar();
|
|
||||||
activeView.activeSidebarTab('chat');
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<AIIcon />
|
<AIIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ export const rightSidebarButton = style({
|
|||||||
opacity: 0,
|
opacity: 0,
|
||||||
maxWidth: 0,
|
maxWidth: 0,
|
||||||
marginLeft: 0,
|
marginLeft: 0,
|
||||||
|
// prevent click event from being triggered
|
||||||
|
pointerEvents: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user