🛂(frontend) bind ai_proxy abilities with AI feature
Bind ai_proxy abilities to the AI feature. If ai_proxy is false, the AI feature will not be available.
This commit is contained in:
@@ -696,6 +696,53 @@ test.describe('Doc Editor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_proxy: false,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai-proxy',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeHidden();
|
||||
});
|
||||
|
||||
test('it downloads unsafe files', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@ import { fetchAPI } from '@/api';
|
||||
import { useConfig } from '@/core';
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
|
||||
export const useAI = (docId: Doc['id']) => {
|
||||
export const useAI = (docId: Doc['id'], aiAllowed: boolean) => {
|
||||
const conf = useConfig().data;
|
||||
|
||||
return useMemo(() => {
|
||||
if (!aiAllowed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const extension = AIExtension({
|
||||
transport: new DefaultChatTransport({
|
||||
fetch: (input, init) => {
|
||||
@@ -27,5 +31,5 @@ export const useAI = (docId: Doc['id']) => {
|
||||
});
|
||||
|
||||
return extension;
|
||||
}, [conf?.AI_BOT, docId]);
|
||||
}, [conf?.AI_BOT, docId, aiAllowed]);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import type { Awareness } from 'y-protocols/awareness';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { Box, TextErrors } from '@/components';
|
||||
import { useConfig } from '@/core';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Doc, useProviderStore } from '@/docs/doc-management';
|
||||
import { avatarUrlFromName, useAuth } from '@/features/auth';
|
||||
@@ -46,7 +47,6 @@ import {
|
||||
PdfBlock,
|
||||
UploadLoaderBlock,
|
||||
} from './custom-blocks';
|
||||
|
||||
const AIMenu = BlockNoteAI?.AIMenu;
|
||||
const AIMenuController = BlockNoteAI?.AIMenuController;
|
||||
const useAI = BlockNoteAI?.useAI;
|
||||
@@ -104,7 +104,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
}
|
||||
|
||||
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
||||
const aiExtension = useAI?.(doc.id);
|
||||
const conf = useConfig().data;
|
||||
const aiAllowed = !!(conf?.AI_FEATURE_ENABLED && doc.abilities?.ai_proxy);
|
||||
const aiExtension = useAI?.(doc.id, aiAllowed);
|
||||
|
||||
const collabName = user?.full_name || user?.email;
|
||||
const cursorName = collabName || t('Anonymous');
|
||||
@@ -266,11 +268,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
comments={showComments}
|
||||
aria-label={t('Document editor')}
|
||||
>
|
||||
{aiExtension && AIMenuController && AIMenu && (
|
||||
{aiAllowed && AIMenuController && AIMenu && (
|
||||
<AIMenuController aiMenu={AIMenu} />
|
||||
)}
|
||||
<BlockNoteSuggestionMenu />
|
||||
<BlockNoteToolbar />
|
||||
<BlockNoteSuggestionMenu aiAllowed={aiAllowed} />
|
||||
<BlockNoteToolbar aiAllowed={aiAllowed} />
|
||||
</BlockNoteView>
|
||||
</Box>
|
||||
);
|
||||
@@ -337,7 +339,7 @@ export const BlockNoteReader = ({
|
||||
slashMenu={false}
|
||||
comments={false}
|
||||
>
|
||||
<BlockNoteToolbar />
|
||||
<BlockNoteToolbar aiAllowed={false} />
|
||||
</BlockNoteView>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -11,8 +11,6 @@ import {
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useConfig } from '@/core';
|
||||
|
||||
import {
|
||||
DocsBlockSchema,
|
||||
DocsInlineContentSchema,
|
||||
@@ -32,7 +30,11 @@ const getMultiColumnSlashMenuItems =
|
||||
|
||||
const getAISlashMenuItems = BlockNoteAI?.getAISlashMenuItems;
|
||||
|
||||
export const BlockNoteSuggestionMenu = () => {
|
||||
export const BlockNoteSuggestionMenu = ({
|
||||
aiAllowed,
|
||||
}: {
|
||||
aiAllowed: boolean;
|
||||
}) => {
|
||||
const editor = useBlockNoteEditor<
|
||||
DocsBlockSchema,
|
||||
DocsInlineContentSchema,
|
||||
@@ -44,7 +46,6 @@ export const BlockNoteSuggestionMenu = () => {
|
||||
const fileBlocksName = dictionaryDate.slash_menu.file.group;
|
||||
|
||||
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
|
||||
const { data: conf } = useConfig();
|
||||
|
||||
const getSlashMenuItems = useMemo(() => {
|
||||
// We insert it after the "Code Block" item to have the interlinking block displayed after the basic blocks
|
||||
@@ -56,9 +57,7 @@ export const BlockNoteSuggestionMenu = () => {
|
||||
getMultiColumnSlashMenuItems?.(editor) || [],
|
||||
getPdfReactSlashMenuItems(editor, t, fileBlocksName),
|
||||
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
|
||||
conf?.AI_FEATURE_ENABLED && getAISlashMenuItems
|
||||
? getAISlashMenuItems(editor)
|
||||
: [],
|
||||
aiAllowed && getAISlashMenuItems ? getAISlashMenuItems(editor) : [],
|
||||
);
|
||||
|
||||
const index = combinedMenu.findIndex(
|
||||
@@ -80,7 +79,7 @@ export const BlockNoteSuggestionMenu = () => {
|
||||
t,
|
||||
fileBlocksName,
|
||||
basicBlocksName,
|
||||
conf?.AI_FEATURE_ENABLED,
|
||||
aiAllowed,
|
||||
getInterlinkingMenuItems,
|
||||
]);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ModalConfirmDownloadUnsafe } from './ModalConfirmDownloadUnsafe';
|
||||
|
||||
const AIToolbarButton = BlockNoteAI?.AIToolbarButton;
|
||||
|
||||
export const BlockNoteToolbar = () => {
|
||||
export const BlockNoteToolbar = ({ aiAllowed }: { aiAllowed: boolean }) => {
|
||||
const dict = useDictionary();
|
||||
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
||||
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
||||
@@ -72,7 +72,7 @@ export const BlockNoteToolbar = () => {
|
||||
const formattingToolbar = useCallback(() => {
|
||||
return (
|
||||
<FormattingToolbar>
|
||||
{conf?.AI_FEATURE_ENABLED && AIToolbarButton && <AIToolbarButton />}
|
||||
{aiAllowed && AIToolbarButton && <AIToolbarButton />}
|
||||
|
||||
<CommentToolbarButton />
|
||||
|
||||
@@ -87,7 +87,7 @@ export const BlockNoteToolbar = () => {
|
||||
<MarkdownButton key="customButton" />
|
||||
</FormattingToolbar>
|
||||
);
|
||||
}, [toolbarItems, conf?.AI_FEATURE_ENABLED]);
|
||||
}, [toolbarItems, aiAllowed, conf?.AI_FEATURE_ENABLED]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user