diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 69981013a1..3807a941c1 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -16,6 +16,7 @@ export const runtimeFlagsSchema = z.object({ enableTestProperties: z.boolean(), enableBroadcastChannelProvider: z.boolean(), enableDebugPage: z.boolean(), + githubUrl: z.string(), changelogUrl: z.string(), downloadUrl: z.string(), // see: tools/workers diff --git a/packages/frontend/component/src/components/tour-modal/editingVideo.mp4 b/packages/frontend/component/src/components/tour-modal/editingVideo.mp4 deleted file mode 100644 index b8dbd6b014..0000000000 Binary files a/packages/frontend/component/src/components/tour-modal/editingVideo.mp4 and /dev/null differ diff --git a/packages/frontend/component/src/components/tour-modal/index.css.ts b/packages/frontend/component/src/components/tour-modal/index.css.ts deleted file mode 100644 index 451bf11a1f..0000000000 --- a/packages/frontend/component/src/components/tour-modal/index.css.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { cssVar } from '@toeverything/theme'; -import { keyframes, style } from '@vanilla-extract/css'; -export const modalStyle = style({ - width: '100%', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - backgroundColor: cssVar('backgroundSecondaryColor'), - borderRadius: '16px', - overflow: 'hidden', -}); -export const titleContainerStyle = style({ - width: 'calc(100% - 72px)', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - position: 'relative', - height: '60px', - overflow: 'hidden', -}); -export const titleStyle = style({ - fontSize: cssVar('fontH6'), - fontWeight: '600', - marginTop: '12px', - position: 'absolute', - marginBottom: '12px', -}); -const slideToLeft = keyframes({ - '0%': { - transform: 'translateX(0)', - opacity: 1, - }, - '100%': { - transform: 'translateX(-300px)', - opacity: 0, - }, -}); -const slideToRight = keyframes({ - '0%': { - transform: 'translateX(0)', - opacity: 1, - }, - '100%': { - transform: 'translateX(300px)', - opacity: 0, - }, -}); -const slideFormLeft = keyframes({ - '0%': { - transform: 'translateX(300px)', - opacity: 0, - }, - '100%': { - transform: 'translateX(0)', - opacity: 1, - }, -}); -const slideFormRight = keyframes({ - '0%': { - transform: 'translateX(-300px)', - opacity: 0, - }, - '100%': { - transform: 'translateX(0)', - opacity: 1, - }, -}); -export const formSlideToLeftStyle = style({ - animation: `${slideFormLeft} 0.3s ease-in-out forwards`, -}); -export const formSlideToRightStyle = style({ - animation: `${slideFormRight} 0.3s ease-in-out forwards`, -}); -export const slideToLeftStyle = style({ - animation: `${slideToLeft} 0.3s ease-in-out forwards`, -}); -export const slideToRightStyle = style({ - animation: `${slideToRight} 0.3s ease-in-out forwards`, -}); -export const containerStyle = style({ - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', -}); -export const videoContainerStyle = style({ - height: '300px', - width: 'calc(100% - 72px)', - display: 'flex', - alignItems: 'center', - flexGrow: 1, - justifyContent: 'space-between', - position: 'relative', - overflow: 'hidden', -}); -export const videoSlideStyle = style({ - width: '100%', - position: 'absolute', - top: 0, - display: 'flex', - justifyContent: 'center', -}); -export const videoStyle = style({ - position: 'absolute', - objectFit: 'fill', - height: '300px', - border: `1px solid ${cssVar('borderColor')}`, - transition: 'opacity 0.5s ease-in-out', -}); -const fadeIn = keyframes({ - '0%': { - transform: 'translateX(300px)', - }, - '100%': { - transform: 'translateX(0)', - }, -}); -export const videoActiveStyle = style({ - animation: `${fadeIn} 0.5s ease-in-out forwards`, - opacity: 0, -}); -export const arrowStyle = style({ - wordBreak: 'break-all', - wordWrap: 'break-word', - width: '36px', - fontSize: '32px', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '240px', - flexGrow: 0.2, - cursor: 'pointer', -}); -export const descriptionContainerStyle = style({ - width: 'calc(100% - 112px)', - height: '100px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - position: 'relative', - overflow: 'hidden', -}); -export const descriptionStyle = style({ - marginTop: '15px', - width: '100%', - display: 'flex', - fontSize: cssVar('fontSm'), - lineHeight: '18px', - position: 'absolute', -}); -export const tabStyle = style({ - width: '40px', - height: '40px', - content: '""', - margin: '40px 10px 40px 0', - transition: 'all 0.15s ease-in-out', - position: 'relative', - cursor: 'pointer', - ':hover': { - opacity: 1, - }, - '::after': { - content: '""', - position: 'absolute', - bottom: '20px', - left: '0', - width: '100%', - height: '2px', - background: cssVar('textPrimaryColor'), - transition: 'all 0.15s ease-in-out', - opacity: 0.2, - cursor: 'pointer', - }, -}); -export const tabActiveStyle = style({ - '::after': { - opacity: 1, - }, -}); -export const tabContainerStyle = style({ - width: '100%', - marginTop: '20px', - position: 'relative', - height: '2px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', -}); -export const buttonDisableStyle = style({ - cursor: 'not-allowed', - color: cssVar('textDisableColor'), -}); diff --git a/packages/frontend/component/src/components/tour-modal/index.tsx b/packages/frontend/component/src/components/tour-modal/index.tsx deleted file mode 100644 index dd7a09ecc7..0000000000 --- a/packages/frontend/component/src/components/tour-modal/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './tour-modal'; diff --git a/packages/frontend/component/src/components/tour-modal/switchVideo.mp4 b/packages/frontend/component/src/components/tour-modal/switchVideo.mp4 deleted file mode 100644 index 0c57351a35..0000000000 Binary files a/packages/frontend/component/src/components/tour-modal/switchVideo.mp4 and /dev/null differ diff --git a/packages/frontend/component/src/components/tour-modal/tour-modal.tsx b/packages/frontend/component/src/components/tour-modal/tour-modal.tsx deleted file mode 100644 index 95610743ab..0000000000 --- a/packages/frontend/component/src/components/tour-modal/tour-modal.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/// -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons'; -import clsx from 'clsx'; -import { useState } from 'react'; - -import { Modal, type ModalProps } from '../../ui/modal'; -import editingVideo from './editingVideo.mp4'; -import { - arrowStyle, - buttonDisableStyle, - containerStyle, - descriptionContainerStyle, - descriptionStyle, - formSlideToLeftStyle, - formSlideToRightStyle, - modalStyle, - slideToLeftStyle, - slideToRightStyle, - tabActiveStyle, - tabContainerStyle, - tabStyle, - titleContainerStyle, - titleStyle, - videoContainerStyle, - videoSlideStyle, - videoStyle, -} from './index.css'; -import switchVideo from './switchVideo.mp4'; - -export const TourModal = (props: ModalProps) => { - const t = useAFFiNEI18N(); - const [step, setStep] = useState(-1); - return ( - -
-
- {step !== -1 && ( -
- {t['com.affine.onboarding.title2']()} -
- )} -
- {t['com.affine.onboarding.title1']()} -
-
- -
-
step === 1 && setStep(0)} - data-testid="onboarding-modal-pre-button" - > - -
-
-
- {step !== -1 && ( - - )} - -
-
-
setStep(1)} - data-testid="onboarding-modal-next-button" - > - -
-
-
    -
  • setStep(0)} - >
  • -
  • setStep(1)} - >
  • -
-
- {step !== -1 && ( -
- {t['com.affine.onboarding.videoDescription2']()} -
- )} -
- {t['com.affine.onboarding.videoDescription1']()} -
-
-
-
- ); -}; - -export default TourModal; diff --git a/packages/frontend/component/src/ui/modal/index.ts b/packages/frontend/component/src/ui/modal/index.ts index 1d7b6892d3..f1de4385bc 100644 --- a/packages/frontend/component/src/ui/modal/index.ts +++ b/packages/frontend/component/src/ui/modal/index.ts @@ -1,2 +1,3 @@ export * from './confirm-modal'; export * from './modal'; +export * from './overlay-modal'; diff --git a/packages/frontend/component/src/ui/modal/modal.stories.tsx b/packages/frontend/component/src/ui/modal/modal.stories.tsx index 906ed94fb6..30e3264051 100644 --- a/packages/frontend/component/src/ui/modal/modal.stories.tsx +++ b/packages/frontend/component/src/ui/modal/modal.stories.tsx @@ -5,6 +5,7 @@ import { Button } from '../button'; import { Input, type InputProps } from '../input'; import { ConfirmModal, type ConfirmModalProps } from './confirm-modal'; import { Modal, type ModalProps } from './modal'; +import { OverlayModal, type OverlayModalProps } from './overlay-modal'; export default { title: 'UI/Modal', @@ -65,5 +66,38 @@ const ConfirmModalTemplate: StoryFn = () => { ); }; +const OverlayModalTemplate: StoryFn = () => { + const [open, setOpen] = useState(false); + + return ( + <> + + + } + /> + + ); +}; + export const Confirm: StoryFn = ConfirmModalTemplate.bind(undefined); + +export const Overlay: StoryFn = + OverlayModalTemplate.bind(undefined); diff --git a/packages/frontend/component/src/ui/modal/overlay-modal.css.ts b/packages/frontend/component/src/ui/modal/overlay-modal.css.ts new file mode 100644 index 0000000000..447d3a503b --- /dev/null +++ b/packages/frontend/component/src/ui/modal/overlay-modal.css.ts @@ -0,0 +1,37 @@ +import { cssVar } from '@toeverything/theme'; +import { style } from '@vanilla-extract/css'; + +export const title = style({ + padding: '20px 24px 8px 24px', + fontSize: cssVar('fontH6'), + fontFamily: cssVar('fontFamily'), + fontWeight: '600', + lineHeight: '26px', +}); + +export const content = style({ + padding: '0px 24px 8px', + fontSize: cssVar('fontBase'), + lineHeight: '24px', + fontWeight: 400, +}); + +export const footer = style({ + padding: '20px 24px', + display: 'flex', + justifyContent: 'flex-end', + gap: '20px', +}); + +export const gotItBtn = style({ + fontWeight: 500, +}); + +export const buttonText = style({ + color: cssVar('pureWhite'), + textDecoration: 'none', + cursor: 'pointer', + ':visited': { + color: cssVar('pureWhite'), + }, +}); diff --git a/packages/frontend/component/src/ui/modal/overlay-modal.tsx b/packages/frontend/component/src/ui/modal/overlay-modal.tsx new file mode 100644 index 0000000000..88fc22e65f --- /dev/null +++ b/packages/frontend/component/src/ui/modal/overlay-modal.tsx @@ -0,0 +1,102 @@ +import { DialogTrigger } from '@radix-ui/react-dialog'; +import { cssVar } from '@toeverything/theme'; +import { memo, useCallback } from 'react'; +import { Link } from 'react-router-dom'; + +import { Button, type ButtonProps } from '../button'; +import { Modal, type ModalProps } from './modal'; +import * as styles from './overlay-modal.css'; + +const defaultContentOptions: ModalProps['contentOptions'] = { + style: { + padding: 0, + overflow: 'hidden', + boxShadow: cssVar('menuShadow'), + }, +}; +const defaultOverlayOptions: ModalProps['overlayOptions'] = { + style: { + background: cssVar('white80'), + backdropFilter: 'blur(2px)', + }, +}; + +export interface OverlayModalProps extends ModalProps { + to?: string; + external?: boolean; + topImage?: React.ReactNode; + confirmText?: string; + confirmButtonOptions?: ButtonProps; + onConfirm?: () => void; + cancelText?: string; + cancelButtonOptions?: ButtonProps; + withoutCancelButton?: boolean; +} + +export const OverlayModal = memo(function OverlayModal({ + open, + topImage, + onOpenChange, + title, + description, + onConfirm, + to, + external, + confirmButtonOptions, + cancelButtonOptions, + withoutCancelButton, + contentOptions = defaultContentOptions, + overlayOptions = defaultOverlayOptions, + // FIXME: we need i18n + cancelText = 'Cancel', + confirmText = 'Confirm', + width = 400, +}: OverlayModalProps) { + const handleConfirm = useCallback(() => { + onOpenChange?.(false); + onConfirm?.(); + }, [onOpenChange, onConfirm]); + + return ( + + {topImage} +
{title}
+
{description}
+
+ {!withoutCancelButton ? ( + + + + ) : null} + + {to ? ( + external ? ( + //FIXME: we need a more standardized way to implement this link with other click events + + + + ) : ( + + + + ) + ) : ( + + )} +
+
+ ); +}); diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts index 6841ae540c..17358c0051 100644 --- a/packages/frontend/core/.webpack/runtime-config.ts +++ b/packages/frontend/core/.webpack/runtime-config.ts @@ -23,6 +23,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableTestProperties: false, enableBroadcastChannelProvider: true, enableDebugPage: true, + githubUrl: 'https://github.com/toeverything/AFFiNE', changelogUrl: 'https://affine.pro/what-is-new', downloadUrl: 'https://affine.pro/download', imageProxyUrl: '/api/worker/image-proxy', @@ -64,6 +65,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableTestProperties: true, enableBroadcastChannelProvider: true, enableDebugPage: true, + githubUrl: 'https://github.com/toeverything/AFFiNE', changelogUrl: 'https://github.com/toeverything/AFFiNE/releases', downloadUrl: 'https://affine.pro/download', imageProxyUrl: '/api/worker/image-proxy', diff --git a/packages/frontend/core/public/static/githubStar.mp4 b/packages/frontend/core/public/static/githubStar.mp4 new file mode 100644 index 0000000000..fd77394ffb Binary files /dev/null and b/packages/frontend/core/public/static/githubStar.mp4 differ diff --git a/packages/frontend/core/public/static/newIssue.mp4 b/packages/frontend/core/public/static/newIssue.mp4 new file mode 100644 index 0000000000..647209488e Binary files /dev/null and b/packages/frontend/core/public/static/newIssue.mp4 differ diff --git a/packages/frontend/core/src/atoms/guide.ts b/packages/frontend/core/src/atoms/guide.ts index c17d4a2ba3..b49d72d940 100644 --- a/packages/frontend/core/src/atoms/guide.ts +++ b/packages/frontend/core/src/atoms/guide.ts @@ -55,21 +55,6 @@ export const guideChangeLogAtom = atom< })); } ); -export const guideOnboardingAtom = atom< - Guide['onBoarding'], - [open: boolean], - void ->( - get => { - return get(guidePrimitiveAtom).onBoarding; - }, - (_, set, open) => { - set(guidePrimitiveAtom, tips => ({ - ...tips, - onBoarding: open, - })); - } -); export const guideDownloadClientTipAtom = atom< Guide['downloadClientTip'], diff --git a/packages/frontend/core/src/atoms/index.ts b/packages/frontend/core/src/atoms/index.ts index 47516d5c7d..6c1454ff83 100644 --- a/packages/frontend/core/src/atoms/index.ts +++ b/packages/frontend/core/src/atoms/index.ts @@ -10,10 +10,11 @@ import type { SettingProps } from '../components/affine/setting-modal'; export const openWorkspacesModalAtom = atom(false); export const openCreateWorkspaceModalAtom = atom(false); export const openQuickSearchModalAtom = atom(false); -export const openOnboardingModalAtom = atom(false); export const openSignOutModalAtom = atom(false); export const openPaymentDisableAtom = atom(false); export const openQuotaModalAtom = atom(false); +export const openStarAFFiNEModalAtom = atom(false); +export const openIssueFeedbackModalAtom = atom(false); export type SettingAtom = Pick< SettingProps, diff --git a/packages/frontend/core/src/commands/affine-help.tsx b/packages/frontend/core/src/commands/affine-help.tsx index 5c0bbc0fd6..cf938cdd90 100644 --- a/packages/frontend/core/src/commands/affine-help.tsx +++ b/packages/frontend/core/src/commands/affine-help.tsx @@ -1,9 +1,9 @@ import type { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { ContactWithUsIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons'; +import { ContactWithUsIcon, NewIcon } from '@blocksuite/icons'; import { registerAffineCommand } from '@toeverything/infra/command'; import type { createStore } from 'jotai'; -import { openOnboardingModalAtom, openSettingModalAtom } from '../atoms'; +import { openSettingModalAtom } from '../atoms'; export function registerAffineHelpCommands({ t, @@ -39,18 +39,6 @@ export function registerAffineHelpCommands({ }, }) ); - unsubs.push( - registerAffineCommand({ - id: 'affine:help-getting-started', - category: 'affine:help', - icon: , - label: t['com.affine.cmdk.affine.getting-started'](), - preconditionStrategy: () => environment.isDesktop, - run() { - store.set(openOnboardingModalAtom, true); - }, - }) - ); return () => { unsubs.forEach(unsub => unsub()); diff --git a/packages/frontend/core/src/components/affine/issue-feedback-modal/index.tsx b/packages/frontend/core/src/components/affine/issue-feedback-modal/index.tsx new file mode 100644 index 0000000000..fc5818dc6d --- /dev/null +++ b/packages/frontend/core/src/components/affine/issue-feedback-modal/index.tsx @@ -0,0 +1,35 @@ +import { OverlayModal } from '@affine/component'; +import { openIssueFeedbackModalAtom } from '@affine/core/atoms'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useAtom } from 'jotai'; + +export const IssueFeedbackModal = () => { + const t = useAFFiNEI18N(); + const [open, setOpen] = useAtom(openIssueFeedbackModalAtom); + + return ( + + } + title={t['com.affine.issue-feedback.title']()} + onOpenChange={setOpen} + description={t['com.affine.issue-feedback.description']()} + cancelText={t['com.affine.issue-feedback.cancel']()} + to={`${runtimeConfig.githubUrl}/issues/new/choose`} + confirmText={t['com.affine.issue-feedback.confirm']()} + confirmButtonOptions={{ + type: 'primary', + }} + external + /> + ); +}; diff --git a/packages/frontend/core/src/components/affine/onboarding-modal.tsx b/packages/frontend/core/src/components/affine/onboarding-modal.tsx deleted file mode 100644 index 5fc6abe6ff..0000000000 --- a/packages/frontend/core/src/components/affine/onboarding-modal.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { TourModal } from '@affine/component/tour-modal'; -import { useAtom } from 'jotai'; -import { memo, useCallback } from 'react'; - -import { openOnboardingModalAtom } from '../../atoms'; -import { guideOnboardingAtom } from '../../atoms/guide'; - -export const OnboardingModal = memo(function OnboardingModal() { - const [open, setOpen] = useAtom(openOnboardingModalAtom); - const [guideOpen, setShowOnboarding] = useAtom(guideOnboardingAtom); - const onOpenChange = useCallback( - (open: boolean) => { - if (open) return; - setShowOnboarding(false); - setOpen(false); - }, - [setOpen, setShowOnboarding] - ); - - return ( - - ); -}); 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 index 7b48ea9088..451c3e1ce1 100644 --- a/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx +++ b/packages/frontend/core/src/components/affine/onboarding/workspace-guide-modal.tsx @@ -1,14 +1,11 @@ -import { Button, Modal, type ModalProps } from '@affine/component'; +import { OverlayModal } from '@affine/component'; +import type { ModalProps } from '@affine/component/ui/modal'; 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: @@ -36,7 +33,6 @@ export const WorkspaceGuideModal = memo(function WorkspaceGuideModal() { }, [open]); const gotIt = useCallback(() => { - setOpen(false); setDismiss(true); }, [setDismiss]); @@ -47,28 +43,23 @@ export const WorkspaceGuideModal = memo(function WorkspaceGuideModal() { }, []); return ( - - -
- {t['com.affine.onboarding.workspace-guide.title']()} -
-
- {t['com.affine.onboarding.workspace-guide.content']()} -
-
- -
-
+ topImage={} + title={t['com.affine.onboarding.workspace-guide.title']()} + description={t['com.affine.onboarding.workspace-guide.content']()} + onConfirm={gotIt} + overlayOptions={overlayOptions} + withoutCancelButton + confirmButtonOptions={{ + style: { + fontWeight: 500, + }, + type: 'primary', + size: 'large', + }} + confirmText={t['com.affine.onboarding.workspace-guide.got-it']()} + /> ); }); diff --git a/packages/frontend/core/src/components/affine/setting-modal/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/index.tsx index 52e3929892..68e1371202 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/index.tsx @@ -1,8 +1,13 @@ import { WorkspaceDetailSkeleton } from '@affine/component/setting-components'; import { Modal, type ModalProps } from '@affine/component/ui/modal'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { + openIssueFeedbackModalAtom, + openStarAFFiNEModalAtom, +} from '@affine/core/atoms'; +import { Trans } from '@affine/i18n'; import { ContactWithUsIcon } from '@blocksuite/icons'; import type { WorkspaceMetadata } from '@toeverything/infra'; +import { useSetAtom } from 'jotai'; import { debounce } from 'lodash-es'; import { Suspense, useCallback, useLayoutEffect, useRef } from 'react'; @@ -37,7 +42,6 @@ export const SettingModal = ({ onSettingClick, ...modalProps }: SettingProps) => { - const t = useAFFiNEI18N(); const loginStatus = useCurrentLoginStatus(); const modalContentRef = useRef(null); @@ -79,6 +83,16 @@ export const SettingModal = ({ }, [onSettingClick] ); + const setOpenIssueFeedbackModal = useSetAtom(openIssueFeedbackModalAtom); + const setOpenStarAFFiNEModal = useSetAtom(openStarAFFiNEModalAtom); + + const handleOpenIssueFeedbackModal = useCallback(() => { + setOpenIssueFeedbackModal(true); + }, [setOpenIssueFeedbackModal]); + + const handleOpenStarAFFiNEModal = useCallback(() => { + setOpenStarAFFiNEModal(true); + }, [setOpenStarAFFiNEModal]); return (
- - - - - {t['com.affine.settings.suggestion']()} - + + + ), + 2: ( + + ), + }} + />
diff --git a/packages/frontend/core/src/components/affine/setting-modal/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/style.css.ts index 7413d439d4..57f98c750c 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/style.css.ts +++ b/packages/frontend/core/src/components/affine/setting-modal/style.css.ts @@ -42,4 +42,12 @@ export const footer = style({ justifyContent: 'center', alignItems: 'center', paddingBottom: '20px', + gap: '4px', + fontSize: cssVar('fontXs'), + flexWrap: 'wrap', +}); + +export const link = style({ + color: cssVar('linkColor'), + cursor: 'pointer', }); diff --git a/packages/frontend/core/src/components/affine/star-affine-modal/index.tsx b/packages/frontend/core/src/components/affine/star-affine-modal/index.tsx new file mode 100644 index 0000000000..01df190e7a --- /dev/null +++ b/packages/frontend/core/src/components/affine/star-affine-modal/index.tsx @@ -0,0 +1,35 @@ +import { OverlayModal } from '@affine/component'; +import { openStarAFFiNEModalAtom } from '@affine/core/atoms'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useAtom } from 'jotai'; + +export const StarAFFiNEModal = () => { + const t = useAFFiNEI18N(); + const [open, setOpen] = useAtom(openStarAFFiNEModalAtom); + + return ( + + } + title={t['com.affine.star-affine.title']()} + onOpenChange={setOpen} + description={t['com.affine.star-affine.description']()} + cancelText={t['com.affine.star-affine.cancel']()} + to={runtimeConfig.githubUrl} + confirmButtonOptions={{ + type: 'primary', + }} + confirmText={t['com.affine.star-affine.confirm']()} + external + /> + ); +}; diff --git a/packages/frontend/core/src/components/pure/help-island/index.tsx b/packages/frontend/core/src/components/pure/help-island/index.tsx index fa77c692ba..24304504ef 100644 --- a/packages/frontend/core/src/components/pure/help-island/index.tsx +++ b/packages/frontend/core/src/components/pure/help-island/index.tsx @@ -1,12 +1,12 @@ import { Tooltip } from '@affine/component/ui/tooltip'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { CloseIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons'; +import { CloseIcon, NewIcon } from '@blocksuite/icons'; import { useSetAtom } from 'jotai/react'; import { useAtomValue } from 'jotai/react'; import { useCallback, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { openOnboardingModalAtom, openSettingModalAtom } from '../../../atoms'; +import { openSettingModalAtom } from '../../../atoms'; import { currentModeAtom } from '../../../atoms/mode'; import type { SettingProps } from '../../affine/setting-modal'; import { ContactIcon, HelpIcon, KeyboardIcon } from './icons'; @@ -22,14 +22,14 @@ const DEFAULT_SHOW_LIST: IslandItemNames[] = [ 'contact', 'shortcuts', ]; -const DESKTOP_SHOW_LIST: IslandItemNames[] = [...DEFAULT_SHOW_LIST, 'guide']; -type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts' | 'guide'; + +const DESKTOP_SHOW_LIST: IslandItemNames[] = [...DEFAULT_SHOW_LIST]; +type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts'; const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST; export const HelpIsland = () => { const mode = useAtomValue(currentModeAtom); - const setOpenOnboarding = useSetAtom(openOnboardingModalAtom); const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom); const [spread, setShowSpread] = useState(false); const t = useAFFiNEI18N(); @@ -102,22 +102,6 @@ export const HelpIsland = () => { )} - {showList.includes('guide') && ( - - { - setShowSpread(false); - setOpenOnboarding(true); - }} - > - - - - )} {spread ? ( diff --git a/packages/frontend/core/src/providers/modal-provider.tsx b/packages/frontend/core/src/providers/modal-provider.tsx index 4f89bc15e1..b7daa21849 100644 --- a/packages/frontend/core/src/providers/modal-provider.tsx +++ b/packages/frontend/core/src/providers/modal-provider.tsx @@ -47,11 +47,6 @@ const TmpDisableAffineCloudModal = lazy(() => ) ); -const OnboardingModal = lazy(() => - import('../components/affine/onboarding-modal').then(module => ({ - default: module.OnboardingModal, - })) -); const WorkspaceGuideModal = lazy(() => import('../components/affine/onboarding/workspace-guide-modal').then( module => ({ @@ -77,6 +72,16 @@ const CloudQuotaModal = lazy(() => default: module.CloudQuotaModal, })) ); +const StarAFFiNEModal = lazy(() => + import('../components/affine/star-affine-modal').then(module => ({ + default: module.StarAFFiNEModal, + })) +); +const IssueFeedbackModal = lazy(() => + import('../components/affine/issue-feedback-modal').then(module => ({ + default: module.IssueFeedbackModal, + })) +); export const Setting = () => { const [{ open, workspaceMetadata, activeTab }, setOpenSettingModalAtom] = @@ -174,11 +179,8 @@ export function CurrentWorkspaceModals() { onOpenChange={setOpenDisableCloudAlertModal} /> - {environment.isDesktop && ( - - - - )} + + {currentWorkspace ? : null} {currentWorkspace?.flavour === WorkspaceFlavour.LOCAL && ( diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 39943c4d4e..54624ca8ce 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -887,6 +887,7 @@ "com.affine.settings.storage.description": "Check or change storage location", "com.affine.settings.storage.description-alt": "Check or change storage location. Click path to edit location.", "com.affine.settings.suggestion": "Need more customization options? Tell us in the community.", + "com.affine.settings.suggestion-2": "Love our app? <1>Star us on GitHub and <2>create issues for your valuable feedback!", "com.affine.settings.translucent-style": "Translucent UI on the sidebar", "com.affine.settings.translucent-style-description": "Use transparency effect on the sidebar.", "com.affine.settings.workspace": "Workspace", @@ -1070,5 +1071,13 @@ "com.affine.journal.conflict-show-more": "{{count}} more articles", "com.affine.journal.app-sidebar-title": "Journals", "com.affine.journal.cmdk.append-to-today": "Append to Journal", - "com.affine.editor.reference-not-found": "Linked page not found" + "com.affine.editor.reference-not-found": "Linked page not found", + "com.affine.star-affine.title": "Star Us on GitHub", + "com.affine.star-affine.description": "Are you finding our app useful and enjoyable? We'd love your support to keep improving! A great way to help us out is by giving us a star on GitHub. This simple action can make a big difference and helps us continue to deliver the best experience for you.", + "com.affine.star-affine.confirm": "Star on GitHub", + "com.affine.star-affine.cancel": "Maybe Later", + "com.affine.issue-feedback.title": "Share Your Feedback on GitHub", + "com.affine.issue-feedback.description": "Got feedback? We're all ears! Create an issue on GitHub to let us know your thoughts and suggestions", + "com.affine.issue-feedback.confirm": "Create Issue on GitHub", + "com.affine.issue-feedback.cancel": "Maybe Later" } diff --git a/tests/affine-desktop/e2e/basic.spec.ts b/tests/affine-desktop/e2e/basic.spec.ts index 0a8b080e80..e19a7b57e8 100644 --- a/tests/affine-desktop/e2e/basic.spec.ts +++ b/tests/affine-desktop/e2e/basic.spec.ts @@ -25,10 +25,6 @@ test('new page', async ({ page, workspace }) => { // macOS only // if (platform() === 'darwin') { test('app sidebar router forward/back', async ({ page }) => { - await page.getByTestId('help-island').click(); - await page.getByTestId('easy-guide').click(); - await page.getByTestId('onboarding-modal-next-button').click(); - await page.getByTestId('onboarding-modal-close-button').click(); { // create pages await page.waitForTimeout(500); @@ -128,25 +124,6 @@ test('app theme', async ({ page, electronApp }) => { } }); -test('affine onboarding button', async ({ page }) => { - await page.getByTestId('help-island').click(); - await page.getByTestId('easy-guide').click(); - const onboardingModal = page.locator('[data-testid=onboarding-modal]'); - await expect(onboardingModal).toBeVisible(); - const switchVideo = page.locator( - '[data-testid=onboarding-modal-switch-video]' - ); - await expect(switchVideo).toBeVisible(); - await page.getByTestId('onboarding-modal-next-button').click(); - const editingVideo = page.locator( - '[data-testid=onboarding-modal-editing-video]' - ); - await expect(editingVideo).toBeVisible(); - await page.getByTestId('onboarding-modal-close-button').click(); - - await expect(onboardingModal).toBeHidden(); -}); - test('windows only check', async ({ page }) => { const windowOnlyUI = page.locator('[data-platform-target=win32]'); if (process.platform === 'win32') { diff --git a/tests/kit/electron.ts b/tests/kit/electron.ts index 182b215c04..c8770a8607 100644 --- a/tests/kit/electron.ts +++ b/tests/kit/electron.ts @@ -36,9 +36,6 @@ export const test = base.extend<{ }>({ page: async ({ electronApp }, use) => { const page = await electronApp.firstWindow(); - await page.getByTestId('onboarding-modal-close-button').click({ - delay: 100, - }); // wait for blocksuite to be loaded await page.waitForSelector('v-line'); if (enableCoverage) { diff --git a/tests/storybook/src/stories/onboarding-modal.stories.tsx b/tests/storybook/src/stories/onboarding-modal.stories.tsx deleted file mode 100644 index db41e6f2a1..0000000000 --- a/tests/storybook/src/stories/onboarding-modal.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* deepscan-disable USELESS_ARROW_FUNC_BIND */ -import { TourModal } from '@affine/component/tour-modal'; -import type { Meta, StoryFn } from '@storybook/react'; - -export default { - title: 'AFFiNE/TourModal', - component: TourModal, - parameters: { - chromatic: { disableSnapshot: true }, - }, -} satisfies Meta; - -export const Basic: StoryFn = () => { - return ; -}; -Basic.args = { - logoSrc: '/imgs/affine-text-logo.png', -};