feat(core): move enable ai to feature flag (#8195)

This commit is contained in:
EYHN
2024-09-11 07:42:07 +00:00
parent 8c191e6baa
commit 498a69af53
15 changed files with 109 additions and 78 deletions

View File

@@ -1,5 +1,4 @@
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { useLiveData, useService } from '@toeverything/infra';
import { FeatureFlagService, useService } from '@toeverything/infra';
import { Suspense, useCallback, useEffect, useState } from 'react';
import { AIOnboardingEdgeless } from './edgeless.dialog';
@@ -30,10 +29,8 @@ const useDismiss = (key: AIOnboardingType) => {
export const WorkspaceAIOnboarding = () => {
const [dismissGeneral] = useDismiss(AIOnboardingType.GENERAL);
const [dismissLocal] = useDismiss(AIOnboardingType.LOCAL);
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$.map(s => s.enableAI)
);
const featureFlagService = useService(FeatureFlagService);
const enableAI = featureFlagService.flags.enable_ai.value;
return (
<Suspense>
@@ -45,10 +42,8 @@ export const WorkspaceAIOnboarding = () => {
export const PageAIOnboarding = () => {
const [dismissEdgeless] = useDismiss(AIOnboardingType.EDGELESS);
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$.map(s => s.enableAI)
);
const featureFlagService = useService(FeatureFlagService);
const enableAI = featureFlagService.flags.enable_ai.value;
return (
<Suspense>

View File

@@ -26,7 +26,11 @@ import {
import { useI18n } from '@affine/i18n';
import type { DocMode } from '@blocksuite/blocks';
import { DoneIcon, SearchIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
import {
FeatureFlagService,
useLiveData,
useServices,
} from '@toeverything/infra';
import clsx from 'clsx';
import {
type ChangeEvent,
@@ -398,15 +402,15 @@ export const SpellCheckSettings = () => {
const AISettings = () => {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
const { editorSettingService } = useServices({ EditorSettingService });
const { featureFlagService } = useServices({ FeatureFlagService });
const settings = useLiveData(editorSettingService.editorSetting.settings$);
const enableAI = useLiveData(featureFlagService.flags.enable_ai.$);
const onAIChange = useCallback(
(checked: boolean) => {
editorSettingService.editorSetting.set('enableAI', checked);
featureFlagService.flags.enable_ai.set(checked); // this will trigger page reload, see `FeatureFlagService`
},
[editorSettingService]
[featureFlagService]
);
const onToggleAI = useCallback(
(checked: boolean) => {
@@ -421,7 +425,11 @@ const AISettings = () => {
: t[
'com.affine.settings.editorSettings.general.ai.disable.description'
](),
confirmText: checked ? t['Enable']() : t['Disable'](),
confirmText: checked
? t['com.affine.settings.editorSettings.general.ai.enable.confirm']()
: t[
'com.affine.settings.editorSettings.general.ai.disable.confirm'
](),
cancelText: t['Cancel'](),
onConfirm: () => onAIChange(checked),
confirmButtonOptions: {
@@ -437,7 +445,7 @@ const AISettings = () => {
name={t['com.affine.settings.editorSettings.general.ai.title']()}
desc={t['com.affine.settings.editorSettings.general.ai.description']()}
>
<Switch checked={settings.enableAI} onChange={onToggleAI} />
<Switch checked={enableAI} onChange={onToggleAI} />
</SettingRow>
);
};

View File

@@ -120,7 +120,7 @@ const ExperimentalFeaturesItem = ({ flag }: { flag: Flag }) => {
: feedbackLink[flag.feedbackType]
: undefined;
if (flag.configurable === false) {
if (flag.configurable === false || flag.hide) {
return null;
}

View File

@@ -13,6 +13,7 @@ import type { Doc } from '@blocksuite/store';
import {
DocService,
DocsService,
FeatureFlagService,
useFramework,
useLiveData,
useService,
@@ -71,14 +72,14 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
peekViewService,
docService,
docsService,
editorSettingService,
editorService,
featureFlagService,
} = useServices({
PeekViewService,
DocService,
DocsService,
EditorSettingService,
EditorService,
FeatureFlagService,
});
const framework = useFramework();
const referenceRenderer: ReferenceReactRenderer = useMemo(() => {
@@ -101,12 +102,11 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
}, [mode, page.collection]);
const specs = useMemo(() => {
const enableAI =
editorSettingService.editorSetting.settings$.value.enableAI;
const enableAI = featureFlagService.flags.enable_ai.value;
return mode === 'edgeless'
? createEdgelessModeSpecs(framework, enableAI)
: createPageModeSpecs(framework, enableAI);
}, [editorSettingService, mode, framework]);
}, [featureFlagService, mode, framework]);
const confirmModal = useConfirmModal();
const patchedSpecs = useMemo(() => {

View File

@@ -11,6 +11,13 @@ import { map } from 'rxjs';
import type { EditorSettingProvider } from '../provider/editor-setting-provider';
import { EditorSettingSchema } from '../schema';
type SettingItem<T> = {
readonly value: T;
set: (value: T) => void;
// eslint-disable-next-line rxjs/finnish
$: T;
};
export class EditorSetting extends Entity {
constructor(public readonly provider: EditorSettingProvider) {
super();
@@ -20,6 +27,27 @@ export class EditorSetting extends Entity {
>(this.settings$, {});
this.settingSignal = signal;
this.disposables.push(cleanup);
Object.entries(EditorSettingSchema.shape).forEach(([flagKey, flag]) => {
const livedata$ = this.settings$.selector(
s => s[flagKey as keyof EditorSettingSchema]
);
const item = {
...flag,
get value() {
return livedata$.value;
},
set: (value: any) => {
this.set(flagKey as keyof EditorSettingSchema, value);
},
$: livedata$,
} as SettingItem<unknown>;
Object.defineProperty(this, flagKey, {
get: () => {
return item;
},
});
});
}
settings$ = LiveData.from<EditorSettingSchema>(this.watchAll(), null as any);
@@ -61,3 +89,7 @@ export class EditorSetting extends Entity {
);
}
}
export type EditorSettingExt = EditorSetting & {
[K in keyof EditorSettingSchema]: SettingItem<EditorSettingSchema[K]>;
};

View File

@@ -16,7 +16,6 @@ export const fontStyleOptions = [
}[];
const AffineEditorSettingSchema = z.object({
enableAI: z.boolean().default(true),
fontFamily: z.enum(['Sans', 'Serif', 'Mono', 'Custom']).default('Sans'),
customFontFamily: z.string().default(''),
newDocDefaultMode: z.enum(['edgeless', 'page']).default('page'),

View File

@@ -6,11 +6,16 @@ import {
WorkspaceInitialized,
} from '@toeverything/infra';
import { EditorSetting } from '../entities/editor-setting';
import {
EditorSetting,
type EditorSettingExt,
} from '../entities/editor-setting';
@OnEvent(WorkspaceInitialized, e => e.onWorkspaceInitialized)
export class EditorSettingService extends Service {
editorSetting = this.framework.createEntity(EditorSetting);
editorSetting = this.framework.createEntity(
EditorSetting
) as EditorSettingExt;
onWorkspaceInitialized(workspace: Workspace) {
// set default mode for new doc

View File

@@ -7,7 +7,6 @@ import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
import { EditorService } from '@affine/core/modules/editor';
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { RecentDocsService } from '@affine/core/modules/quicksearch';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import type { PageRootService } from '@blocksuite/blocks';
@@ -16,6 +15,7 @@ import { AiIcon, FrameIcon, TocIcon, TodayIcon } from '@blocksuite/icons/rc';
import { type AffineEditorContainer } from '@blocksuite/presets';
import {
DocService,
FeatureFlagService,
FrameworkScope,
GlobalContextService,
useLiveData,
@@ -61,7 +61,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
docService,
workspaceService,
globalContextService,
editorSettingService,
featureFlagService,
} = useServices({
WorkbenchService,
ViewService,
@@ -69,7 +69,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
DocService,
WorkspaceService,
GlobalContextService,
EditorSettingService,
FeatureFlagService,
});
const workbench = workbenchService.workbench;
const editor = editorService.editor;
@@ -250,7 +250,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
</div>
</ViewBody>
{editorSettingService.editorSetting.settings$.value.enableAI && (
{featureFlagService.flags.enable_ai.value && (
<ViewSidebarTab
tabId="chat"
icon={<AiIcon />}

View File

@@ -1,7 +1,6 @@
import { ConfirmModal, NotificationCenter, notify } from '@affine/component';
import { NotificationCenter, notify } from '@affine/component';
import { events } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useI18n } from '@affine/i18n';
import {
GlobalContextService,
useLiveData,
@@ -11,7 +10,7 @@ import {
} from '@toeverything/infra';
import { useAtom } from 'jotai';
import type { ReactElement } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect } from 'react';
import type { SettingAtom } from '../atoms';
import { openSettingModalAtom, openSignOutModalAtom } from '../atoms';
@@ -32,7 +31,6 @@ import { useAsyncCallback } from '../hooks/affine-async-hooks';
import { useNavigateHelper } from '../hooks/use-navigate-helper';
import { AuthService } from '../modules/cloud/services/auth';
import { CreateWorkspaceDialogProvider } from '../modules/create-workspace';
import { EditorSettingService } from '../modules/editor-settting';
import { FindInPageModal } from '../modules/find-in-page/view/find-in-page-modal';
import { ImportTemplateDialogProvider } from '../modules/import-template';
import { PeekViewManagerModal } from '../modules/peek-view';
@@ -185,43 +183,6 @@ export const SignOutConfirmModal = () => {
);
};
export const AIReloadConfirmModal = () => {
const t = useI18n();
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$.selector(s => s.enableAI)
);
const [aiState] = useState(enableAI);
const [open, setOpen] = useState(false);
useEffect(() => {
setOpen(enableAI !== aiState);
}, [aiState, enableAI]);
const onConfirm = useCallback(() => {
window.location.reload();
}, []);
return (
<ConfirmModal
open={open}
onOpenChange={setOpen}
onConfirm={onConfirm}
confirmButtonOptions={{
variant: 'primary',
}}
title={t['com.affine.settings.editorSettings.general.ai.reload.title']()}
description={t[
'com.affine.settings.editorSettings.general.ai.reload.description'
]()}
cancelText={t['Cancel']()}
confirmText={t[
'com.affine.settings.editorSettings.general.ai.reload.confirm'
]()}
/>
);
};
export const AllWorkspaceModals = (): ReactElement => {
return (
<>
@@ -230,7 +191,6 @@ export const AllWorkspaceModals = (): ReactElement => {
<CreateWorkspaceDialogProvider />
<AuthModal />
<SignOutConfirmModal />
<AIReloadConfirmModal />
</>
);
};