diff --git a/README.md b/README.md index a92f9f9df8..972f870c2d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@
Home Page | - Discord | + Discord | Live Demo | Blog | Documentation diff --git a/packages/backend/server/src/fundamentals/mailer/template.ts b/packages/backend/server/src/fundamentals/mailer/template.ts index f467ca141a..3e50a31718 100644 --- a/packages/backend/server/src/fundamentals/mailer/template.ts +++ b/packages/backend/server/src/fundamentals/mailer/template.ts @@ -148,7 +148,7 @@ export const emailTemplate = ({ - AFFiNE discord link { + if (value.defaultState !== undefined) { + docCollection.awarenessStore.setFlag( + key as keyof BlockSuiteFlags, + value.defaultState + ); + } + }); } catch (err) { logger.error('syncEditorFlags', err); } @@ -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 = 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 = { + 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 = { + 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, + }, +}; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/config.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/config.tsx index aa3b717903..c87f2715b4 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/config.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/config.tsx @@ -21,7 +21,7 @@ export const relatedLinks = [ { icon: , title: 'Discord', - link: 'https://discord.gg/Arn7TqJBvG', + link: 'https://discord.gg/whd5mjYqVw', }, { icon: , diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.css.ts b/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.css.ts index c07da6ff9c..052c2e45f2 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.css.ts +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.css.ts @@ -54,6 +54,7 @@ export const switchRow = style({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', + width: '100%', }); export const switchDisabled = style({ opacity: 0.5, @@ -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, +}); diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.tsx index 9eec1433ed..0038f97645 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/experimental-features/index.tsx @@ -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'; @@ -75,28 +86,75 @@ const ExperimentalFeaturesPrompt = ({ ); }; +const FeedbackIcon = ({ type }: { type: FeedbackType }) => { + switch (type) { + case 'discord': + return ; + case 'email': + return ; + case 'github': + return ; + default: + return null; + } +}; + +const feedbackLink: Record = { + 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 ( -
- {title} - +
+
+ {title} + +
+ {!!description && ( + +
{description}
+
+ )} + {!!feedbackType && ( +
+ + Discussion about this feature + + + )}
); }; @@ -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 ( ); }; -// feature flag -> display name -const blocksuiteFeatureFlags: Partial> = { - 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( @@ -148,16 +202,25 @@ const BlocksuiteFeatureFlagSettings = () => { return ( <> - {Object.entries(blocksuiteFeatureFlags).map(([flag, displayName]) => ( - - toggleSetting(flag as keyof BlockSuiteFlags, checked) - } - /> - ))} + {Object.entries(blocksuiteFeatureFlags).map(([key, value]) => { + const hidden = value.precondition && !value.precondition(); + + if (hidden) { + return null; + } + return ( + + toggleSetting(key as keyof BlockSuiteFlags, checked) + } + /> + ); + })} ); }; @@ -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' + ]()} />
{ const hasPaymentFeature = useLiveData( serverConfigService.serverConfig.features$.map(f => f?.payment) ); - const isEarlyAccess = useLiveData( - userFeatureService.userFeature.isEarlyAccess$ - ); useEffect(() => { userFeatureService.userFeature.revalidate(); @@ -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'](), diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 68d16618d9..c679f18132 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -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.",