mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
feat: add onboarding for client (#2144)
Co-authored-by: Himself65 <himself65@outlook.com>
This commit is contained in:
BIN
apps/web/public/editingVideo.mp4
Normal file
BIN
apps/web/public/editingVideo.mp4
Normal file
Binary file not shown.
BIN
apps/web/public/switchVideo.mp4
Normal file
BIN
apps/web/public/switchVideo.mp4
Normal file
Binary file not shown.
@@ -52,3 +52,18 @@ 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,
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -77,6 +77,7 @@ export const currentEditorAtom = rootCurrentEditorAtom;
|
||||
export const openWorkspacesModalAtom = atom(false);
|
||||
export const openCreateWorkspaceModalAtom = atom(false);
|
||||
export const openQuickSearchModalAtom = atom(false);
|
||||
export const openOnboardingModalAtom = atom(false);
|
||||
|
||||
export const openDisableCloudAlertModalAtom = atom(false);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ const CommonMenu = () => {
|
||||
<Menu
|
||||
width={276}
|
||||
content={content}
|
||||
// placement="bottom-end"
|
||||
placement="bottom"
|
||||
disablePortal={true}
|
||||
trigger="click"
|
||||
>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const StyledHeaderContainer = styled('div')<{
|
||||
top: 0,
|
||||
background: 'var(--affine-background-primary-color)',
|
||||
WebkitAppRegion: 'drag',
|
||||
zIndex: 1,
|
||||
zIndex: 'var(--affine-z-index-popover)',
|
||||
'@media (max-width: 768px)': {
|
||||
'&[data-open="true"]': {
|
||||
WebkitAppRegion: 'no-drag',
|
||||
|
||||
45
apps/web/src/components/pure/OnboardingModal.tsx
Normal file
45
apps/web/src/components/pure/OnboardingModal.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { TourModal } from '@affine/component/tour-modal';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { openOnboardingModalAtom } from '../../atoms';
|
||||
import { guideOnboardingAtom } from '../../atoms/guide';
|
||||
|
||||
type OnboardingModalProps = {
|
||||
onClose: () => void;
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const getHelperGuide = (): { onBoarding: boolean } | null => {
|
||||
const helperGuide = localStorage.getItem('helper-guide');
|
||||
if (helperGuide) {
|
||||
return JSON.parse(helperGuide);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const OnboardingModal: React.FC<OnboardingModalProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
}) => {
|
||||
const [, setShowOnboarding] = useAtom(guideOnboardingAtom);
|
||||
const [, setOpenOnboarding] = useAtom(openOnboardingModalAtom);
|
||||
const onCloseTourModal = useCallback(() => {
|
||||
setShowOnboarding(false);
|
||||
onClose();
|
||||
}, [onClose, setShowOnboarding]);
|
||||
|
||||
const shouldShow = useMemo(() => {
|
||||
const helperGuide = getHelperGuide();
|
||||
return helperGuide?.onBoarding ?? true;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldShow) {
|
||||
setOpenOnboarding(true);
|
||||
}
|
||||
}, [shouldShow, setOpenOnboarding]);
|
||||
return <TourModal open={open} onClose={onCloseTourModal} />;
|
||||
};
|
||||
|
||||
export default OnboardingModal;
|
||||
@@ -1,8 +1,11 @@
|
||||
import { MuiFade, Tooltip } from '@affine/component';
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, NewIcon } from '@blocksuite/icons';
|
||||
import { useAtom } from 'jotai';
|
||||
import { lazy, Suspense, useState } from 'react';
|
||||
|
||||
import { openOnboardingModalAtom } from '../../../atoms';
|
||||
import { ShortcutsModal } from '../shortcuts-modal';
|
||||
import { ContactIcon, HelpIcon, KeyboardIcon } from './Icons';
|
||||
import {
|
||||
@@ -11,19 +14,25 @@ import {
|
||||
StyledIsland,
|
||||
StyledTriggerWrapper,
|
||||
} from './style';
|
||||
|
||||
const env = getEnvironment();
|
||||
const ContactModal = lazy(() =>
|
||||
import('@affine/component/contact-modal').then(({ ContactModal }) => ({
|
||||
default: ContactModal,
|
||||
}))
|
||||
);
|
||||
|
||||
export type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts';
|
||||
const DEFAULT_SHOW_LIST: IslandItemNames[] = [
|
||||
'whatNew',
|
||||
'contact',
|
||||
'shortcuts',
|
||||
];
|
||||
const DESKTOP_SHOW_LIST: IslandItemNames[] = [...DEFAULT_SHOW_LIST, 'guide'];
|
||||
export type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts' | 'guide';
|
||||
export const HelpIsland = ({
|
||||
showList = ['whatNew', 'contact', 'shortcuts'],
|
||||
showList = env.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST,
|
||||
}: {
|
||||
showList?: IslandItemNames[];
|
||||
}) => {
|
||||
const [, setOpenOnboarding] = useAtom(openOnboardingModalAtom);
|
||||
const [spread, setShowSpread] = useState(false);
|
||||
// const { triggerShortcutsModal, triggerContactModal } = useModal();
|
||||
// const blockHub = useGlobalState(store => store.blockHub);
|
||||
@@ -98,6 +107,19 @@ export const HelpIsland = ({
|
||||
</StyledIconWrapper>
|
||||
</Tooltip>
|
||||
)}
|
||||
{showList.includes('guide') && (
|
||||
<Tooltip content={'Easy Guide'} placement="left-end">
|
||||
<StyledIconWrapper
|
||||
data-testid="easy-guide"
|
||||
onClick={() => {
|
||||
setShowSpread(false);
|
||||
setOpenOnboarding(true);
|
||||
}}
|
||||
>
|
||||
<HelpIcon />
|
||||
</StyledIconWrapper>
|
||||
</Tooltip>
|
||||
)}
|
||||
</StyledAnimateWrapper>
|
||||
|
||||
<Tooltip content={t['Help and Feedback']()} placement="left-end">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
currentWorkspaceIdAtom,
|
||||
openCreateWorkspaceModalAtom,
|
||||
openDisableCloudAlertModalAtom,
|
||||
openOnboardingModalAtom,
|
||||
openWorkspacesModalAtom,
|
||||
} from '../atoms';
|
||||
import { useAffineLogIn } from '../hooks/affine/use-affine-log-in';
|
||||
@@ -37,6 +39,11 @@ const TmpDisableAffineCloudModal = lazy(() =>
|
||||
})
|
||||
)
|
||||
);
|
||||
const OnboardingModalAtom = lazy(() =>
|
||||
import('../components/pure/OnboardingModal').then(module => ({
|
||||
default: module.OnboardingModal,
|
||||
}))
|
||||
);
|
||||
|
||||
export function Modals() {
|
||||
const [openWorkspacesModal, setOpenWorkspacesModal] = useAtom(
|
||||
@@ -49,6 +56,9 @@ export function Modals() {
|
||||
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
|
||||
openDisableCloudAlertModalAtom
|
||||
);
|
||||
const [openOnboardingModal, setOpenOnboardingModal] = useAtom(
|
||||
openOnboardingModalAtom
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
const { jumpToSubPath } = useRouterHelper(router);
|
||||
@@ -59,7 +69,10 @@ export function Modals() {
|
||||
const [, setCurrentWorkspace] = useCurrentWorkspace();
|
||||
const { createLocalWorkspace } = useAppHelper();
|
||||
const [transitioning, transition] = useTransition();
|
||||
|
||||
const env = getEnvironment();
|
||||
const onCloseOnboardingModal = useCallback(() => {
|
||||
setOpenOnboardingModal(false);
|
||||
}, [setOpenOnboardingModal]);
|
||||
return (
|
||||
<>
|
||||
<Suspense>
|
||||
@@ -70,6 +83,15 @@ export function Modals() {
|
||||
}, [setOpenDisableCloudAlertModal])}
|
||||
/>
|
||||
</Suspense>
|
||||
{env.isDesktop && (
|
||||
<Suspense>
|
||||
<OnboardingModalAtom
|
||||
open={openOnboardingModal}
|
||||
onClose={onCloseOnboardingModal}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
<Suspense>
|
||||
<WorkspaceListModal
|
||||
disabled={transitioning}
|
||||
|
||||
Reference in New Issue
Block a user