Skip to content

Commit

Permalink
feat(core): add configuration for experimental features (#7699)
Browse files Browse the repository at this point in the history
close AF-1218 AF-1219

Added configuration for experimental features

Example:
```
const blocksuiteFeatureFlags = {
  ...
  enable_expand_database_block: {
    displayName: 'Enable Expand Database Block',
    description: 'Allows expanding database blocks for better view and management.',
    feedbackType: 'discord',
    displayChannel: ['stable', 'beta', 'canary', 'internal'],
    restrictedPlatform: 'client'
  },
    enable_ai_onboarding: {
    displayName: 'AI Onboarding',
    description: 'Enables AI onboarding.',
    displayChannel: [],
    defaultState: true,
  },
  ...
}

```

![CleanShot 2024-08-02 at 12 26 36@2x](https://github.com/user-attachments/assets/98b1e8e7-cd8b-4309-8063-323b2f3b5a94)
  • Loading branch information
JimmFly committed Aug 13, 2024
1 parent 6228b27 commit 9037e66
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

<div align="center">
<a href="https://affine.pro">Home Page</a> |
<a href="https://discord.com/invite/yz6tGVsf5p">Discord</a> |
<a href="https://discord.gg/whd5mjYqVw">Discord</a> |
<a href="https://app.affine.pro">Live Demo</a> |
<a href="https://affine.pro/blog/">Blog</a> |
<a href="https://docs.affine.pro/docs/">Documentation</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const emailTemplate = ({
</a>
</td>
<td style="padding: 0 10px">
<a href="https://discord.gg/Arn7TqJBvG" target="_blank"
<a href="https://discord.gg/whd5mjYqVw" target="_blank"
><img
src="https://cdn.affine.pro/mail/2023-8-9/Discord.png"
alt="AFFiNE discord link"
Expand Down
99 changes: 94 additions & 5 deletions packages/common/infra/src/atom/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,14 @@ export function setupEditorFlags(docCollection: DocCollection) {

// override this flag in app settings
// TODO(@eyhn): need a better way to manage block suite flags
docCollection.awarenessStore.setFlag('enable_synced_doc_block', true);
docCollection.awarenessStore.setFlag('enable_edgeless_text', true);
docCollection.awarenessStore.setFlag('enable_color_picker', true);
docCollection.awarenessStore.setFlag('enable_ai_chat_block', true);
docCollection.awarenessStore.setFlag('enable_ai_onboarding', true);
Object.entries(blocksuiteFeatureFlags).forEach(([key, value]) => {
if (value.defaultState !== undefined) {
docCollection.awarenessStore.setFlag(
key as keyof BlockSuiteFlags,
value.defaultState
);
}
});
} catch (err) {
logger.error('syncEditorFlags', err);
}
Expand Down Expand Up @@ -140,3 +143,89 @@ export const appSettingAtom = atom<
});
}
);

export type BuildChannel = 'stable' | 'beta' | 'canary' | 'internal';

export type FeedbackType = 'discord' | 'email' | 'github';

export type PreconditionType = () => boolean | undefined;

export type Flag<K extends string> = Partial<{
[key in K]: {
displayName: string;
description?: string;
precondition?: PreconditionType;
defaultState?: boolean; // default to open and not controlled by user
feedbackType?: FeedbackType;
};
}>;

const isNotStableBuild: PreconditionType = () => {
return runtimeConfig.appBuildType !== 'stable';
};
const isDesktopEnvironment: PreconditionType = () => environment.isDesktop;
const neverShow: PreconditionType = () => false;

export const blocksuiteFeatureFlags: Flag<keyof BlockSuiteFlags> = {
enable_database_attachment_note: {
displayName: 'Database Attachment Note',
description: 'Allows adding notes to database attachments.',
precondition: isNotStableBuild,
},
enable_database_statistics: {
displayName: 'Database Block Statistics',
description: 'Shows statistics for database blocks.',
precondition: isNotStableBuild,
},
enable_block_query: {
displayName: 'Todo Block Query',
description: 'Enables querying of todo blocks.',
precondition: isNotStableBuild,
},
enable_synced_doc_block: {
displayName: 'Synced Doc Block',
description: 'Enables syncing of doc blocks.',
precondition: neverShow,
defaultState: true,
},
enable_edgeless_text: {
displayName: 'Edgeless Text',
description: 'Enables edgeless text blocks.',
precondition: neverShow,
defaultState: true,
},
enable_color_picker: {
displayName: 'Color Picker',
description: 'Enables color picker blocks.',
precondition: neverShow,
defaultState: true,
},
enable_ai_chat_block: {
displayName: 'AI Chat Block',
description: 'Enables AI chat blocks.',
precondition: neverShow,
defaultState: true,
},
enable_ai_onboarding: {
displayName: 'AI Onboarding',
description: 'Enables AI onboarding.',
precondition: neverShow,
defaultState: true,
},
enable_expand_database_block: {
displayName: 'Expand Database Block',
description: 'Enables expanding of database blocks.',
precondition: neverShow,
defaultState: true,
},
};

export const affineFeatureFlags: Flag<keyof AppSetting> = {
enableMultiView: {
displayName: 'Split View',
description:
'The Split View feature in AFFiNE allows users to divide their workspace into multiple sections, enabling simultaneous viewing and editing of different documents.The Split View feature in AFFiNE allows users to divide their workspace into multiple sections, enabling simultaneous viewing and editing of different documents.',
feedbackType: 'discord',
precondition: isDesktopEnvironment,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const relatedLinks = [
{
icon: <DiscordIcon />,
title: 'Discord',
link: 'https://discord.gg/Arn7TqJBvG',
link: 'https://discord.gg/whd5mjYqVw',
},
{
icon: <YouTubeIcon />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const switchRow = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
});
export const switchDisabled = style({
opacity: 0.5,
Expand All @@ -64,3 +65,34 @@ export const subHeader = style({
color: cssVar('textSecondaryColor'),
marginBottom: 8,
});

export const rowContainer = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 10,
});
export const description = style({
color: cssVar('textSecondaryColor'),
fontSize: cssVar('fontXs'),
// 2 lines
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
width: '100%',
});
export const feedback = style({
width: '100%',
display: 'flex',
alignItems: 'center',
fontSize: cssVar('fontXs'),
color: cssVar('textSecondaryColor'),
gap: 8,
});

export const arrowRightIcon = style({
marginLeft: 'auto',
marginRight: 0,
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { Button, Checkbox, Loading, Switch } from '@affine/component';
import { Button, Checkbox, Loading, Switch, Tooltip } from '@affine/component';
import { SettingHeader } from '@affine/component/setting-components';
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useI18n } from '@affine/i18n';
import {
ArrowRightSmallIcon,
DiscordIcon,
EmailIcon,
GithubIcon,
} from '@blocksuite/icons/rc';
import {
affineFeatureFlags,
blocksuiteFeatureFlags,
type FeedbackType,
} from '@toeverything/infra';
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { Suspense, useCallback, useState } from 'react';
Expand Down Expand Up @@ -75,28 +86,75 @@ const ExperimentalFeaturesPrompt = ({
);
};

const FeedbackIcon = ({ type }: { type: FeedbackType }) => {
switch (type) {
case 'discord':
return <DiscordIcon fontSize={16} />;
case 'email':
return <EmailIcon fontSize={16} />;
case 'github':
return <GithubIcon fontSize={16} />;
default:
return null;
}
};

const feedbackLink: Record<FeedbackType, string> = {
discord: 'https://discord.gg/whd5mjYqVw',
email: 'mailto:support@toeverything.info',
github: 'https://github.com/toeverything/AFFiNE/issues',
};

const ExperimentalFeaturesItem = ({
title,
description,
feedbackType,
isMutating,
checked,
onChange,
testId,
}: {
title: React.ReactNode;
description?: React.ReactNode;
feedbackType?: FeedbackType;
isMutating?: boolean;
checked: boolean;
onChange: (checked: boolean) => void;
testId?: string;
}) => {
const link = feedbackType ? feedbackLink[feedbackType] : undefined;

return (
<div className={styles.switchRow}>
{title}
<Switch
checked={checked}
onChange={onChange}
className={isMutating ? styles.switchDisabled : ''}
data-testid={testId}
/>
<div className={styles.rowContainer}>
<div className={styles.switchRow}>
{title}
<Switch
checked={checked}
onChange={onChange}
className={isMutating ? styles.switchDisabled : ''}
data-testid={testId}
/>
</div>
{!!description && (
<Tooltip content={description}>
<div className={styles.description}>{description}</div>
</Tooltip>
)}
{!!feedbackType && (
<a
className={styles.feedback}
href={link}
target="_blank"
rel="noreferrer"
>
<FeedbackIcon type={feedbackType} />
<span>Discussion about this feature</span>
<ArrowRightSmallIcon
fontSize={20}
className={styles.arrowRightIcon}
/>
</a>
)}
</div>
);
};
Expand All @@ -110,28 +168,24 @@ const SplitViewSettingRow = () => {
},
[updateSettings]
);
const multiViewFlagConfig = affineFeatureFlags['enableMultiView'];
const shouldShow = multiViewFlagConfig?.precondition?.();

if (!environment.isDesktop) {
return null; // only enable on desktop
if (!multiViewFlagConfig || !shouldShow) {
return null;
}

return (
<ExperimentalFeaturesItem
title="Split View"
title={multiViewFlagConfig.displayName}
description={multiViewFlagConfig.description}
feedbackType={multiViewFlagConfig.feedbackType}
checked={appSettings.enableMultiView}
onChange={onToggle}
/>
);
};

// feature flag -> display name
const blocksuiteFeatureFlags: Partial<Record<keyof BlockSuiteFlags, string>> = {
enable_expand_database_block: 'Enable Expand Database Block',
enable_database_attachment_note: 'Enable Database Attachment Note',
enable_database_statistics: 'Enable Database Block Statistics',
enable_block_query: 'Enable Todo Block Query',
};

const BlocksuiteFeatureFlagSettings = () => {
const { appSettings, updateSettings } = useAppSettingHelper();
const toggleSetting = useCallback(
Expand All @@ -148,16 +202,25 @@ const BlocksuiteFeatureFlagSettings = () => {

return (
<>
{Object.entries(blocksuiteFeatureFlags).map(([flag, displayName]) => (
<ExperimentalFeaturesItem
key={flag}
title={'Block Suite: ' + displayName}
checked={!!appSettings.editorFlags?.[flag as EditorFlag]}
onChange={checked =>
toggleSetting(flag as keyof BlockSuiteFlags, checked)
}
/>
))}
{Object.entries(blocksuiteFeatureFlags).map(([key, value]) => {
const hidden = value.precondition && !value.precondition();

if (hidden) {
return null;
}
return (
<ExperimentalFeaturesItem
key={key}
title={'Block Suite: ' + value.displayName}
description={value.description}
feedbackType={value.feedbackType}
checked={!!appSettings.editorFlags?.[key as EditorFlag]}
onChange={checked =>
toggleSetting(key as keyof BlockSuiteFlags, checked)
}
/>
);
})}
</>
);
};
Expand All @@ -171,6 +234,9 @@ const ExperimentalFeaturesMain = () => {
title={t[
'com.affine.settings.workspace.experimental-features.header.plugins'
]()}
subtitle={t[
'com.affine.settings.workspace.experimental-features.header.subtitle'
]()}
/>
<div
className={styles.settingsContainer}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ export const useGeneralSettingList = (): GeneralSettingList => {
const hasPaymentFeature = useLiveData(
serverConfigService.serverConfig.features$.map(f => f?.payment)
);
const isEarlyAccess = useLiveData(
userFeatureService.userFeature.isEarlyAccess$
);

useEffect(() => {
userFeatureService.userFeature.revalidate();
Expand Down Expand Up @@ -86,7 +83,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
}
}

if (isEarlyAccess || runtimeConfig.enableExperimentalFeature) {
if (runtimeConfig.enableExperimentalFeature) {
settings.push({
key: 'experimental-features',
title: t['com.affine.settings.workspace.experimental-features'](),
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/i18n/src/resources/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@
"com.affine.settings.workspace.experimental-features": "Experimental features",
"com.affine.settings.workspace.experimental-features.get-started": "Get started",
"com.affine.settings.workspace.experimental-features.header.plugins": "Experimental features",
"com.affine.settings.workspace.experimental-features.header.subtitle": "You can customize your workspace here.",
"com.affine.settings.workspace.experimental-features.prompt-disclaimer": "I am aware of the risks, and I am willing to continue to use it.",
"com.affine.settings.workspace.experimental-features.prompt-header": "Do you want to use the plugin system that is in an experimental stage?",
"com.affine.settings.workspace.experimental-features.prompt-warning": "You are about to enable an experimental feature. This feature is still in development and may contain errors or behave unpredictably. Please proceed with caution and at your own risk.",
Expand Down

0 comments on commit 9037e66

Please sign in to comment.