From e10609276db0b44b290db4a4ba0903472f95c493 Mon Sep 17 00:00:00 2001 From: Cats Juice Date: Tue, 19 Dec 2023 09:12:26 +0000 Subject: [PATCH] feat(core): add toggle workspace dialog (#5312) --- .../common/infra/src/app-config-storage.ts | 9 +- .../affine/onboarding/assets/thumb.tsx | 311 ++++++++++++++++++ .../onboarding/workspace-guide-modal.css.tsx | 26 ++ .../onboarding/workspace-guide-modal.tsx | 74 +++++ .../core/src/providers/modal-provider.tsx | 8 + packages/frontend/electron/src/main/index.ts | 5 + packages/frontend/i18n/src/resources/en.json | 5 +- tests/kit/playwright.ts | 5 +- 8 files changed, 439 insertions(+), 4 deletions(-) create mode 100644 packages/frontend/core/src/components/affine/onboarding/assets/thumb.tsx create mode 100644 packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.css.tsx create mode 100644 packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx diff --git a/packages/common/infra/src/app-config-storage.ts b/packages/common/infra/src/app-config-storage.ts index 87c167c7da..19e045f7f3 100644 --- a/packages/common/infra/src/app-config-storage.ts +++ b/packages/common/infra/src/app-config-storage.ts @@ -3,6 +3,8 @@ import { z } from 'zod'; const _appConfigSchema = z.object({ /** whether to show onboarding first */ onBoarding: z.boolean().optional().default(true), + /** whether to show change workspace guide modal */ + dismissWorkspaceGuideModal: z.boolean().optional().default(false), }); export type AppConfigSchema = z.infer; export const defaultAppConfig = _appConfigSchema.parse({}); @@ -20,7 +22,7 @@ interface StorageOptions { /** * Storage for app configuration, stored in memory by default */ -class Storage { +class Storage { private _cfg: T; private readonly _id = _inMemoryId++; private readonly _options; @@ -67,7 +69,10 @@ class Storage { return this._cfg; } } else { - return this.get()[key]; + const fullConfig = this.get(); + // TODO: handle key not found, set default value + // if (!(key in fullConfig)) {} + return fullConfig[key]; } } diff --git a/packages/frontend/core/src/components/affine/onboarding/assets/thumb.tsx b/packages/frontend/core/src/components/affine/onboarding/assets/thumb.tsx new file mode 100644 index 0000000000..e18d89f71c --- /dev/null +++ b/packages/frontend/core/src/components/affine/onboarding/assets/thumb.tsx @@ -0,0 +1,311 @@ +import { memo } from 'react'; + +export default memo(function Thumb() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}); diff --git a/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.css.tsx b/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.css.tsx new file mode 100644 index 0000000000..bd991c7738 --- /dev/null +++ b/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.css.tsx @@ -0,0 +1,26 @@ +import { style } from '@vanilla-extract/css'; + +export const title = style({ + padding: '20px 24px 8px 24px', + fontSize: '18px', + fontFamily: 'var(--affine-font-family)', + fontWeight: '600', + lineHeight: '26px', +}); + +export const content = style({ + padding: '0px 24px', + fontSize: '15px', + lineHeight: '24px', + fontWeight: 400, +}); + +export const footer = style({ + padding: '20px 28px', + display: 'flex', + justifyContent: 'flex-end', +}); + +export const gotItBtn = style({ + fontWeight: 500, +}); diff --git a/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx b/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx new file mode 100644 index 0000000000..7b48ea9088 --- /dev/null +++ b/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx @@ -0,0 +1,74 @@ +import { Button, Modal, type ModalProps } from '@affine/component'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { memo, useCallback, useEffect, useState } from 'react'; + +import { useAppConfigStorage } from '../../../hooks/use-app-config-storage'; +import Thumb from './assets/thumb'; +import * as styles from './workspace-guide-modal.css'; + +const contentOptions: ModalProps['contentOptions'] = { + style: { padding: 0, overflow: 'hidden' }, +}; +const overlayOptions: ModalProps['overlayOptions'] = { + style: { + background: + 'linear-gradient(95deg, transparent 0px, var(--affine-background-primary-color) 400px)', + }, +}; + +export const WorkspaceGuideModal = memo(function WorkspaceGuideModal() { + const t = useAFFiNEI18N(); + const [dismiss, setDismiss] = useAppConfigStorage( + 'dismissWorkspaceGuideModal' + ); + const [open, setOpen] = useState(!dismiss); + + // blur modal background, can't use css: `backdrop-filter: blur()`, + // because it won't behave as expected on client side (texts over transparent window are not blurred) + useEffect(() => { + const appDom = document.querySelector('#app') as HTMLElement; + if (!appDom) return; + appDom.style.filter = open ? 'blur(7px)' : 'none'; + + return () => { + appDom.style.filter = 'none'; + }; + }, [open]); + + const gotIt = useCallback(() => { + setOpen(false); + setDismiss(true); + }, [setDismiss]); + + const onOpenChange = useCallback((v: boolean) => { + setOpen(v); + // should set dismiss here ? + // setDismiss(true) + }, []); + + return ( + + +
+ {t['com.affine.onboarding.workspace-guide.title']()} +
+
+ {t['com.affine.onboarding.workspace-guide.content']()} +
+
+ +
+
+ ); +}); diff --git a/packages/frontend/core/src/providers/modal-provider.tsx b/packages/frontend/core/src/providers/modal-provider.tsx index 1e361aeaa7..8d8fca03b8 100644 --- a/packages/frontend/core/src/providers/modal-provider.tsx +++ b/packages/frontend/core/src/providers/modal-provider.tsx @@ -52,6 +52,13 @@ const OnboardingModal = lazy(() => default: module.OnboardingModal, })) ); +const WorkspaceGuideModal = lazy(() => + import('../components/affine/onboarding/workspace-guide-modal').then( + module => ({ + default: module.WorkspaceGuideModal, + }) + ) +); const SignOutModal = lazy(() => import('../components/affine/sign-out-modal').then(module => ({ @@ -160,6 +167,7 @@ export function CurrentWorkspaceModals() { )} + {currentWorkspace && } ); diff --git a/packages/frontend/electron/src/main/index.ts b/packages/frontend/electron/src/main/index.ts index eb2788f327..4002e0a3a7 100644 --- a/packages/frontend/electron/src/main/index.ts +++ b/packages/frontend/electron/src/main/index.ts @@ -6,6 +6,7 @@ import { app } from 'electron'; import { createApplicationMenu } from './application-menu/create'; import { buildType, overrideSession } from './config'; +import { persistentConfig } from './config-storage/persist'; import { setupDeepLink } from './deep-link'; import { registerEvents } from './events'; import { registerHandlers } from './handlers'; @@ -30,6 +31,10 @@ if (require('electron-squirrel-startup')) app.quit(); if (process.env.SKIP_ONBOARDING) { launchStage.value = 'main'; + persistentConfig.set({ + onBoarding: false, + dismissWorkspaceGuideModal: true, + }); } /** diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index c8ec783587..ee2b37977c 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1012,5 +1012,8 @@ "com.affine.history.confirm-restore-modal.restore": "Restore", "com.affine.history.confirm-restore-modal.hint": "You are about to restore the current version of the page to the latest version available. This action will overwrite any changes made prior to the latest version.", "com.affine.share-page.header.present": "Present", - "com.affine.page-operation.add-linked-page": "Add linked page" + "com.affine.page-operation.add-linked-page": "Add linked page", + "com.affine.onboarding.workspace-guide.title": "Start AFFiNE by creating your own Workspace here!", + "com.affine.onboarding.workspace-guide.content": "A Workspace is your virtual space to capture, create and plan as just one person or together as a team.", + "com.affine.onboarding.workspace-guide.got-it": "Got it!" } diff --git a/tests/kit/playwright.ts b/tests/kit/playwright.ts index dfac426a74..0a0b42398b 100644 --- a/tests/kit/playwright.ts +++ b/tests/kit/playwright.ts @@ -34,7 +34,10 @@ type CurrentWorkspace = { export const skipOnboarding = async (context: BrowserContext) => { await context.addInitScript(() => { - window.localStorage.setItem('app_config', '{"onBoarding":false}'); + window.localStorage.setItem( + 'app_config', + '{"onBoarding":false, "dismissWorkspaceGuideModal":true}' + ); }); };