mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
feat: add novice guide for quick search arrow button (#1493)
This commit is contained in:
5
apps/web/src/atoms/first-load.ts
Normal file
5
apps/web/src/atoms/first-load.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { atom } from 'jotai';
|
||||
import { atomWithStorage } from 'jotai/utils';
|
||||
|
||||
export const isFirstLoadAtom = atomWithStorage<boolean>('isFirstLoad', true);
|
||||
export const openTipsAtom = atom<boolean>(false);
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Tooltip } from '@affine/component';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import {
|
||||
useIsFirstLoad,
|
||||
useOpenTips,
|
||||
} from '../../../hooks/affine/use-is-first-load';
|
||||
import { useSidebarStatus } from '../../../hooks/affine/use-sidebar-status';
|
||||
import { SidebarSwitchIcon } from './icons';
|
||||
import { StyledSidebarSwitch } from './style';
|
||||
@@ -17,6 +21,8 @@ export const SidebarSwitch = ({
|
||||
}: SidebarSwitchProps) => {
|
||||
const [open, setOpen] = useSidebarStatus();
|
||||
const [tooltipVisible, setTooltipVisible] = useState(false);
|
||||
const [isFirstLoad, setIsFirstLoad] = useIsFirstLoad();
|
||||
const [, setOpenTips] = useOpenTips();
|
||||
const { t } = useTranslation();
|
||||
tooltipContent =
|
||||
tooltipContent || (open ? t('Collapse sidebar') : t('Expand sidebar'));
|
||||
@@ -34,7 +40,13 @@ export const SidebarSwitch = ({
|
||||
onClick={useCallback(() => {
|
||||
setOpen(!open);
|
||||
setTooltipVisible(false);
|
||||
}, [open, setOpen])}
|
||||
if (isFirstLoad) {
|
||||
setIsFirstLoad(false);
|
||||
setTimeout(() => {
|
||||
setOpenTips(true);
|
||||
}, 200);
|
||||
}
|
||||
}, [isFirstLoad, open, setIsFirstLoad, setOpen, setOpenTips])}
|
||||
onMouseEnter={useCallback(() => {
|
||||
setTooltipVisible(true);
|
||||
}, [])}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Content } from '@affine/component';
|
||||
import { Content, QuickSearchTips } from '@affine/component';
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { ArrowDownSmallIcon } from '@blocksuite/icons';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import React from 'react';
|
||||
|
||||
import { openQuickSearchModalAtom } from '../../../atoms';
|
||||
import { useOpenTips } from '../../../hooks/affine/use-is-first-load';
|
||||
import { usePageMeta } from '../../../hooks/use-page-meta';
|
||||
import { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { PageNotFoundError } from '../../affine/affine-error-eoundary';
|
||||
@@ -11,6 +14,8 @@ import { EditorModeSwitch } from './editor-mode-switch';
|
||||
import Header from './header';
|
||||
import { QuickSearchButton } from './quick-search-button';
|
||||
import {
|
||||
StyledQuickSearchTipButton,
|
||||
StyledQuickSearchTipContent,
|
||||
StyledSearchArrowWrapper,
|
||||
StyledSwitchWrapper,
|
||||
StyledTitle,
|
||||
@@ -43,6 +48,39 @@ export const BlockSuiteEditorHeader: React.FC<BlockSuiteEditorHeaderProps> = ({
|
||||
assertExists(pageMeta);
|
||||
const title = pageMeta.title;
|
||||
const { trash: isTrash } = pageMeta;
|
||||
const [openTips, setOpenTips] = useOpenTips();
|
||||
const isMac = () => {
|
||||
const env = getEnvironment();
|
||||
return env.isBrowser && env.isMacOs;
|
||||
};
|
||||
const tipsContent = () => {
|
||||
return (
|
||||
<StyledQuickSearchTipContent>
|
||||
<div>
|
||||
Click button
|
||||
{
|
||||
<span
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
>
|
||||
<ArrowDownSmallIcon />
|
||||
</span>
|
||||
}
|
||||
or use
|
||||
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
|
||||
can search keywords or quickly open recently viewed pages.
|
||||
</div>
|
||||
<StyledQuickSearchTipButton
|
||||
data-testid="quick-search-got-it"
|
||||
onClick={() => setOpenTips(false)}
|
||||
>
|
||||
Got it
|
||||
</StyledQuickSearchTipButton>
|
||||
</StyledQuickSearchTipContent>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Header
|
||||
rightItems={
|
||||
@@ -68,13 +106,21 @@ export const BlockSuiteEditorHeader: React.FC<BlockSuiteEditorHeaderProps> = ({
|
||||
/>
|
||||
</StyledSwitchWrapper>
|
||||
<Content ellipsis={true}>{title || 'Untitled'}</Content>
|
||||
<StyledSearchArrowWrapper>
|
||||
<QuickSearchButton
|
||||
onClick={() => {
|
||||
setOpenQuickSearch(true);
|
||||
}}
|
||||
/>
|
||||
</StyledSearchArrowWrapper>
|
||||
<QuickSearchTips
|
||||
data-testid="quick-search-tips"
|
||||
content={tipsContent()}
|
||||
placement="bottom"
|
||||
open={openTips}
|
||||
offset={[0, -5]}
|
||||
>
|
||||
<StyledSearchArrowWrapper>
|
||||
<QuickSearchButton
|
||||
onClick={() => {
|
||||
setOpenQuickSearch(true);
|
||||
}}
|
||||
/>
|
||||
</StyledSearchArrowWrapper>
|
||||
</QuickSearchTips>
|
||||
</StyledTitleWrapper>
|
||||
</StyledTitle>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { IconButton, IconButtonProps } from '@affine/component';
|
||||
import { styled } from '@affine/component';
|
||||
import { ArrowDownSmallIcon } from '@blocksuite/icons';
|
||||
import React from 'react';
|
||||
|
||||
const StyledIconButtonWithAnimate = styled(IconButton)(({ theme }) => {
|
||||
return {
|
||||
|
||||
@@ -113,3 +113,24 @@ export const StyledPageListTittleWrapper = styled(StyledTitle)(({ theme }) => {
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledQuickSearchTipButton = styled('div')(({ theme }) => {
|
||||
return {
|
||||
...displayFlex('center', 'center'),
|
||||
marginTop: '12px',
|
||||
color: '#FFFFFF',
|
||||
width: '60px',
|
||||
height: ' 26px',
|
||||
fontSize: theme.font.sm,
|
||||
lineHeight: '22px',
|
||||
background: theme.colors.primaryColor,
|
||||
borderRadius: '8px',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
};
|
||||
});
|
||||
export const StyledQuickSearchTipContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
...displayFlex('center', 'flex-end'),
|
||||
flexDirection: 'column',
|
||||
};
|
||||
});
|
||||
|
||||
@@ -92,8 +92,8 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
|
||||
width={620}
|
||||
style={{
|
||||
maxHeight: '80vh',
|
||||
minHeight: isPublicAndNoQuery() ? '72px' : '350px',
|
||||
top: '12vh',
|
||||
minHeight: isPublicAndNoQuery() ? '72px' : '412px',
|
||||
top: '80px',
|
||||
}}
|
||||
>
|
||||
<Command
|
||||
|
||||
@@ -2,8 +2,8 @@ import { displayFlex, styled } from '@affine/component';
|
||||
|
||||
export const StyledContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
minHeight: '220px',
|
||||
maxHeight: '55vh',
|
||||
minHeight: '280px',
|
||||
maxHeight: '70vh',
|
||||
width: '100%',
|
||||
overflow: 'auto',
|
||||
marginBottom: '10px',
|
||||
|
||||
@@ -124,6 +124,7 @@ export const WorkSpaceSliderBar: React.FC<WorkSpaceSliderBarProps> = ({
|
||||
testid="sliderBar-arrowButton-collapse"
|
||||
/>
|
||||
</StyledSidebarWrapper>
|
||||
|
||||
<StyledSliderBarWrapper data-testid="sliderBar">
|
||||
<WorkspaceSelector
|
||||
currentWorkspace={currentWorkspace}
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
LocalWorkspace,
|
||||
RemWorkspaceFlavour,
|
||||
} from '../../shared';
|
||||
import { useIsFirstLoad, useOpenTips } from '../affine/use-is-first-load';
|
||||
import {
|
||||
useRecentlyViewed,
|
||||
useSyncRecentViewsWithRouter,
|
||||
@@ -314,3 +315,21 @@ describe('useRecentlyViewed', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('useIsFirstLoad', () => {
|
||||
test('basic', async () => {
|
||||
const firstLoad = renderHook(() => useIsFirstLoad());
|
||||
const setFirstLoad = firstLoad.result.current[1];
|
||||
expect(firstLoad.result.current[0]).toEqual(true);
|
||||
setFirstLoad(false);
|
||||
firstLoad.rerender();
|
||||
expect(firstLoad.result.current[0]).toEqual(false);
|
||||
});
|
||||
test('useOpenTips', async () => {
|
||||
const openTips = renderHook(() => useOpenTips());
|
||||
const setOpenTips = openTips.result.current[1];
|
||||
expect(openTips.result.current[0]).toEqual(false);
|
||||
setOpenTips(true);
|
||||
openTips.rerender();
|
||||
expect(openTips.result.current[0]).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
12
apps/web/src/hooks/affine/use-is-first-load.ts
Normal file
12
apps/web/src/hooks/affine/use-is-first-load.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
import { isFirstLoadAtom, openTipsAtom } from '../../atoms/first-load';
|
||||
|
||||
export function useIsFirstLoad() {
|
||||
const [isFirstLoad, setIsFirstLoad] = useAtom(isFirstLoadAtom);
|
||||
return [isFirstLoad, setIsFirstLoad] as const;
|
||||
}
|
||||
export function useOpenTips() {
|
||||
const [openTips, setOpenTips] = useAtom(openTipsAtom);
|
||||
return [openTips, setOpenTips] as const;
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
# Welcome to AFFiNE
|
||||
|
||||
👋 Quick Start
|
||||
==============
|
||||
# 👋 Quick Start
|
||||
|
||||
> Your content should be as feature-rich as you like. We offer you an easy and simple approach to writing your content, with advanced tools that stay out of the way when you don't need them.
|
||||
|
||||
AFFiNE - not just a note taking app
|
||||
-----------------------------------
|
||||
## AFFiNE - not just a note taking app
|
||||
|
||||
There are lots of apps out there, so why choose AFFiNE?
|
||||
|
||||
@@ -18,18 +16,16 @@ AFFiNE is **privacy focused** - with a local-first approach, keep control of you
|
||||
|
||||
AFFiNE is **open source** - you can check us out on [GitHub: toeverything/AFFiNE](https://github.com/toeverything/affine)
|
||||
|
||||
Let's get started!
|
||||
------------------
|
||||
## Let's get started!
|
||||
|
||||
* Create a new page and begin editing - in `Paper` or `Edgeless`
|
||||
- Create a new page and begin editing - in `Paper` or `Edgeless`
|
||||
|
||||

|
||||
|
||||
* Head over to `Workspace Settings` to find your `General` settings, enable multiplayer with `Collaboration`, share your docs with `Publish` and download your data with `Export`
|
||||
- Head over to `Workspace Settings` to find your `General` settings, enable multiplayer with `Collaboration`, share your docs with `Publish` and download your data with `Export`
|
||||
|
||||

|
||||
|
||||
Looking for more support?
|
||||
-------------
|
||||
## Looking for more support?
|
||||
|
||||
Why not come and join the awesome [AFFiNE Community](https://community.affine.pro)? Whether you want to share new ideas or interact with other like minded individuals - we look forward to having you.
|
||||
|
||||
Reference in New Issue
Block a user