Feat/land page (#391)
* feat: update landing page * feat: update landing page * fix: react warning * feat(route): rename variables * feat(route): better refresh * fix: update logo and i18n * feat(code): remove unused comment * refactor(workspace): rename workspaceId * refactor(page): rename pageId * feat(edit): more robust editing experience * fix: landing page mobile * fix: landing page mobile Co-authored-by: tzhangchi <terry.zhangchi@outlook.com> Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
This commit is contained in:
@@ -21,7 +21,7 @@ export function LigoVirgoRoutes() {
|
|||||||
/>
|
/>
|
||||||
<Route path="/ui" element={<UIPage />} />
|
<Route path="/ui" element={<UIPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="/:workspace_id/*"
|
path="/:workspaceId/*"
|
||||||
element={
|
element={
|
||||||
<RoutePrivate>
|
<RoutePrivate>
|
||||||
<WorkspaceContainer />
|
<WorkspaceContainer />
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type EdgelessProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Edgeless = (props: EdgelessProps) => {
|
export const Edgeless = (props: EdgelessProps) => {
|
||||||
const { page_id } = useParams();
|
const { pageId } = useParams();
|
||||||
const { user } = useUserAndSpaces();
|
const { user } = useUserAndSpaces();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -31,7 +31,5 @@ export const Edgeless = (props: EdgelessProps) => {
|
|||||||
update_recent_pages();
|
update_recent_pages();
|
||||||
}, [user, props.workspace]);
|
}, [user, props.workspace]);
|
||||||
|
|
||||||
return (
|
return <MemoAffineBoard workspace={props.workspace} rootBlockId={pageId} />;
|
||||||
<MemoAffineBoard workspace={props.workspace} rootBlockId={page_id} />
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,22 +5,23 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||||||
|
|
||||||
export function WorkspaceHome() {
|
export function WorkspaceHome() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { workspace_id } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const { user } = useUserAndSpaces();
|
const { user } = useUserAndSpaces();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const navigate_to_user_initial_page = async () => {
|
const navigateToUserInitialPage = async () => {
|
||||||
const [recent_pages, user_initial_page_id] = await Promise.all([
|
const [recentPages, userInitialPageId] = await Promise.all([
|
||||||
services.api.userConfig.getRecentPages(workspace_id, user.id),
|
services.api.userConfig.getRecentPages(workspaceId, user.id),
|
||||||
services.api.userConfig.getUserInitialPage(
|
services.api.userConfig.getUserInitialPage(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
user.id
|
user.id
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
if (recent_pages.length === 0) {
|
// if recent pages if null, run initialize task
|
||||||
|
if (recentPages.length === 0) {
|
||||||
await services.api.editorBlock.copyTemplateToPage(
|
await services.api.editorBlock.copyTemplateToPage(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
user_initial_page_id,
|
userInitialPageId,
|
||||||
TemplateFactory.generatePageTemplateByGroupKeys({
|
TemplateFactory.generatePageTemplateByGroupKeys({
|
||||||
name: '👋 Get Started with AFFiNE',
|
name: '👋 Get Started with AFFiNE',
|
||||||
groupKeys: [
|
groupKeys: [
|
||||||
@@ -31,11 +32,12 @@ export function WorkspaceHome() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (userInitialPageId) {
|
||||||
navigate(`/${workspace_id}/${user_initial_page_id}`);
|
navigate(`/${workspaceId}/${userInitialPageId}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
navigate_to_user_initial_page();
|
navigateToUserInitialPage();
|
||||||
}, [navigate, user.id, workspace_id]);
|
}, [navigate, user.id, workspaceId]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,27 +7,20 @@ import { WorkspaceHome } from './Home';
|
|||||||
import Pages from './pages';
|
import Pages from './pages';
|
||||||
|
|
||||||
export function WorkspaceContainer() {
|
export function WorkspaceContainer() {
|
||||||
const { workspace_id } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const { user, currentSpaceId } = useUserAndSpaces();
|
const { user, currentSpaceId } = useUserAndSpaces();
|
||||||
|
|
||||||
if (
|
|
||||||
user &&
|
|
||||||
![currentSpaceId, 'affine2vin277tcmafwq'].includes(workspace_id)
|
|
||||||
) {
|
|
||||||
// return <Navigate to={`/${currentSpaceId}`} replace={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<WorkspaceRootContainer />}>
|
<Route path="/" element={<WorkspaceRootContainer />}>
|
||||||
<Route path="/pages" element={<Pages />} />
|
<Route path="/pages" element={<Pages />} />
|
||||||
<Route
|
<Route
|
||||||
path="/:page_id/edgeless"
|
path="/:pageId/edgeless"
|
||||||
element={<Edgeless workspace={workspace_id} />}
|
element={<Edgeless workspace={workspaceId} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/:page_id"
|
path="/:pageId"
|
||||||
element={<Page workspace={workspace_id} />}
|
element={<Page workspace={workspaceId} />}
|
||||||
/>
|
/>
|
||||||
<Route path="/" element={<WorkspaceHome />} />
|
<Route path="/" element={<WorkspaceHome />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function Page(props: PageProps) {
|
|||||||
const [activeTab, setActiveTab] = useState(
|
const [activeTab, setActiveTab] = useState(
|
||||||
TabMap.get(TAB_TITLE.PAGES).value
|
TabMap.get(TAB_TITLE.PAGES).value
|
||||||
);
|
);
|
||||||
const { page_id } = useParams();
|
const { pageId } = useParams();
|
||||||
const { showSpaceSidebar, fixedDisplay, setSpaceSidebarVisible } =
|
const { showSpaceSidebar, fixedDisplay, setSpaceSidebarVisible } =
|
||||||
useShowSpaceSidebar();
|
useShowSpaceSidebar();
|
||||||
const dailyNotesFlag = useFlag('BooleanDailyNotes', false);
|
const dailyNotesFlag = useFlag('BooleanDailyNotes', false);
|
||||||
@@ -79,7 +79,7 @@ export function Page(props: PageProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<CollapsiblePageTree title="PAGES">
|
<CollapsiblePageTree title="PAGES">
|
||||||
{page_id ? <PageTree /> : null}
|
{pageId ? <PageTree /> : null}
|
||||||
</CollapsiblePageTree>
|
</CollapsiblePageTree>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,7 +91,7 @@ export function Page(props: PageProps) {
|
|||||||
</WorkspaceSidebarContent>
|
</WorkspaceSidebarContent>
|
||||||
</WorkspaceSidebar>
|
</WorkspaceSidebar>
|
||||||
</LigoLeftContainer>
|
</LigoLeftContainer>
|
||||||
<EditorContainer workspace={props.workspace} pageId={page_id} />
|
<EditorContainer workspace={props.workspace} pageId={pageId} />
|
||||||
</LigoApp>
|
</LigoApp>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export type CollapsiblePageTreeProps = {
|
|||||||
export function CollapsiblePageTree(props: CollapsiblePageTreeProps) {
|
export function CollapsiblePageTree(props: CollapsiblePageTreeProps) {
|
||||||
const { className, style, children, title, initialOpen = true } = props;
|
const { className, style, children, title, initialOpen = true } = props;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { workspace_id, page_id } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
|
|
||||||
const { handleAddPage } = usePageTree();
|
const { handleAddPage } = usePageTree();
|
||||||
const { addPageToday } = useCalendarHeatmap();
|
const { addPageToday } = useCalendarHeatmap();
|
||||||
@@ -46,18 +46,18 @@ export function CollapsiblePageTree(props: CollapsiblePageTreeProps) {
|
|||||||
const [open, setOpen] = useState(initialOpen);
|
const [open, setOpen] = useState(initialOpen);
|
||||||
|
|
||||||
const create_page = useCallback(async () => {
|
const create_page = useCallback(async () => {
|
||||||
if (page_id) {
|
if (pageId) {
|
||||||
const newPage = await services.api.editorBlock.create({
|
const newPage = await services.api.editorBlock.create({
|
||||||
workspace: workspace_id,
|
workspace: workspaceId,
|
||||||
type: 'page' as const,
|
type: 'page' as const,
|
||||||
});
|
});
|
||||||
|
|
||||||
await handleAddPage(newPage.id);
|
await handleAddPage(newPage.id);
|
||||||
addPageToday();
|
addPageToday();
|
||||||
|
|
||||||
navigate(`/${workspace_id}/${newPage.id}`);
|
navigate(`/${workspaceId}/${newPage.id}`);
|
||||||
}
|
}
|
||||||
}, [addPageToday, handleAddPage, navigate, page_id, workspace_id]);
|
}, [addPageToday, handleAddPage, navigate, pageId, workspaceId]);
|
||||||
|
|
||||||
const [newPageBtnVisible, setNewPageBtnVisible] = useState<boolean>(false);
|
const [newPageBtnVisible, setNewPageBtnVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const renderTOCContent = tocDataSource => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TOC = () => {
|
export const TOC = () => {
|
||||||
const { page_id } = useParams();
|
const { pageId } = useParams();
|
||||||
const [tocDataSource, setTocDataSource] = useState<TOCType[]>([]);
|
const [tocDataSource, setTocDataSource] = useState<TOCType[]>([]);
|
||||||
const [activeBlockId, setActiveBlockId] = useState('');
|
const [activeBlockId, setActiveBlockId] = useState('');
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ export const TOC = () => {
|
|||||||
const listenerMapRef = useRef<ListenerMap>(new Map());
|
const listenerMapRef = useRef<ListenerMap>(new Map());
|
||||||
|
|
||||||
const { currentEditors } = useCurrentEditors();
|
const { currentEditors } = useCurrentEditors();
|
||||||
const editor = currentEditors[page_id] as Virgo;
|
const editor = currentEditors[pageId] as Virgo;
|
||||||
|
|
||||||
const updateTocDataSource = useCallback(async () => {
|
const updateTocDataSource = useCallback(async () => {
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
@@ -134,7 +134,7 @@ export const TOC = () => {
|
|||||||
const listenerMap = listenerMapRef.current;
|
const listenerMap = listenerMapRef.current;
|
||||||
|
|
||||||
/* page listener: trigger update-notice when add new group */
|
/* page listener: trigger update-notice when add new group */
|
||||||
const pageAsyncBlock = (await editor.getBlockByIds([page_id]))?.[0];
|
const pageAsyncBlock = (await editor.getBlockByIds([pageId]))?.[0];
|
||||||
if (!listenerMap.has(pageAsyncBlock.id)) {
|
if (!listenerMap.has(pageAsyncBlock.id)) {
|
||||||
listenerMap.set(
|
listenerMap.set(
|
||||||
pageAsyncBlock.id,
|
pageAsyncBlock.id,
|
||||||
@@ -144,7 +144,7 @@ export const TOC = () => {
|
|||||||
|
|
||||||
/* block listener: trigger update-notice when change block content */
|
/* block listener: trigger update-notice when change block content */
|
||||||
const { children = [] } =
|
const { children = [] } =
|
||||||
(await editor.queryByPageId(page_id))?.[0] || {};
|
(await editor.queryByPageId(pageId))?.[0] || {};
|
||||||
const asyncBlocks = (await editor.getBlockByIds(children)) || [];
|
const asyncBlocks = (await editor.getBlockByIds(children)) || [];
|
||||||
const { tocContents } = await getContentByAsyncBlocks(
|
const { tocContents } = await getContentByAsyncBlocks(
|
||||||
asyncBlocks,
|
asyncBlocks,
|
||||||
@@ -155,7 +155,7 @@ export const TOC = () => {
|
|||||||
/* toc: flat content */
|
/* toc: flat content */
|
||||||
const tocDataSource = getPageTOC(asyncBlocks, tocContents);
|
const tocDataSource = getPageTOC(asyncBlocks, tocContents);
|
||||||
setTocDataSource(tocDataSource);
|
setTocDataSource(tocDataSource);
|
||||||
}, [editor, page_id]);
|
}, [editor, pageId]);
|
||||||
|
|
||||||
/* init toc and add page/block update-listener & unmount-listener */
|
/* init toc and add page/block update-listener & unmount-listener */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -90,13 +90,14 @@ export const AFFiNEFooter = ({
|
|||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
maxWidth: '400px',
|
flexWrap: 'wrap',
|
||||||
|
maxWidth: '420px',
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
marginBottom: '2em',
|
marginBottom: '2em',
|
||||||
'--joy-shadow-sm': 0,
|
'--joy-shadow-sm': 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
<Box sx={{ display: 'flex', width: '84px' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -144,7 +145,7 @@ export const AFFiNEFooter = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
<Box sx={{ display: 'flex', width: '84px' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -190,7 +191,7 @@ export const AFFiNEFooter = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
<Box sx={{ display: 'flex', width: '84px' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -239,7 +240,7 @@ export const AFFiNEFooter = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
width: '84px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@@ -285,7 +286,7 @@ export const AFFiNEFooter = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
<Box sx={{ display: 'flex', width: '84px' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import GitHubIcon from '@mui/icons-material/GitHub';
|
|||||||
import { styled, useMediaQuery } from '@mui/material';
|
import { styled, useMediaQuery } from '@mui/material';
|
||||||
import AFFiNETextLogo from './affine-text-logo.png';
|
import AFFiNETextLogo from './affine-text-logo.png';
|
||||||
import { HoverMenu } from './HoverMenu';
|
import { HoverMenu } from './HoverMenu';
|
||||||
|
import { MobileHeader } from './MobileHeader';
|
||||||
|
|
||||||
import { options } from '../i18n';
|
import { options } from '../i18n';
|
||||||
|
|
||||||
@@ -22,7 +23,10 @@ export const AFFiNEHeader = () => {
|
|||||||
const changeLanguage = (event: any) => {
|
const changeLanguage = (event: any) => {
|
||||||
i18n.changeLanguage(event);
|
i18n.changeLanguage(event);
|
||||||
};
|
};
|
||||||
const matchesIPAD = useMediaQuery('(max-width: 768px)');
|
const matchesIPAD = useMediaQuery('(max-width: 1080px)');
|
||||||
|
if (matchesIPAD) {
|
||||||
|
return <MobileHeader />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Container container spacing={2}>
|
<Container container spacing={2}>
|
||||||
<Grid xs={6} sx={{ display: 'flex', alignItems: 'center' }}>
|
<Grid xs={6} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
@@ -135,6 +139,34 @@ export const AFFiNEHeader = () => {
|
|||||||
window.open(href);
|
window.open(href);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<HoverMenu
|
||||||
|
title={t('ContactUs')}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
title: 'Discord',
|
||||||
|
value: 'https://discord.gg/Arn7TqJBvG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Telegram',
|
||||||
|
value: 'https://t.me/affineworkos',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Reddit',
|
||||||
|
value: 'https://www.reddit.com/r/Affine/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Medium',
|
||||||
|
value: 'https://medium.com/@affineworkos',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Email',
|
||||||
|
value: 'mailto:contact@toeverything.info',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onSelect={href => {
|
||||||
|
window.open(href);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Select
|
<Select
|
||||||
defaultValue="en"
|
defaultValue="en"
|
||||||
sx={{ display: matchesIPAD ? 'none' : 'intial' }}
|
sx={{ display: matchesIPAD ? 'none' : 'intial' }}
|
||||||
|
|||||||
208
apps/venus/src/app/common/MobileHeader.tsx
Normal file
208
apps/venus/src/app/common/MobileHeader.tsx
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
import Close from '@mui/icons-material/Close';
|
||||||
|
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||||
|
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||||
|
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||||
|
import Menu from '@mui/icons-material/Menu';
|
||||||
|
import { Button, Grid } from '@mui/joy';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
Collapse,
|
||||||
|
Drawer,
|
||||||
|
List,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemText,
|
||||||
|
styled,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import AFFiNETextLogo from './affine-text-logo.png';
|
||||||
|
|
||||||
|
export const MobileHeader = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { i18n, t } = useTranslation();
|
||||||
|
const anchor = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [openedDrawer, setDrawerState] = useState(false);
|
||||||
|
const handleDrawerOpenState = () => {
|
||||||
|
setDrawerState(!openedDrawer);
|
||||||
|
};
|
||||||
|
const [contactUsCollapse, setContactUsCollapse] = useState(false);
|
||||||
|
const handleContactUsClick = () => {
|
||||||
|
setContactUsCollapse(!contactUsCollapse);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [languageCollapse, setLanguageCollapse] = useState(false);
|
||||||
|
const handleLanguageClick = () => {
|
||||||
|
setLanguageCollapse(!languageCollapse);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container ref={anchor} container spacing={2}>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StyledImage
|
||||||
|
src={AFFiNETextLogo}
|
||||||
|
alt="affine"
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
'https://github.com/toeverything/AFFiNE'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<GitHubIcon />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleDrawerOpenState} variant="plain">
|
||||||
|
{openedDrawer ? <Close /> : <Menu />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<Drawer
|
||||||
|
anchor="top"
|
||||||
|
open={openedDrawer}
|
||||||
|
onClose={() => setDrawerState(false)}
|
||||||
|
>
|
||||||
|
<StyledDrawerContainer>
|
||||||
|
<List>
|
||||||
|
<ListItemButton onClick={() => navigate('/aboutus')}>
|
||||||
|
<ListItemText primary={t('AboutUs') as string} />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://blog.affine.pro')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary={t('Blog') as string} />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://docs.affine.pro/')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary={t('Docs') as string} />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://feedback.affine.pro/')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary={t('Feedback') as string} />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton onClick={handleContactUsClick}>
|
||||||
|
<ListItemText primary={t('ContactUs') as string} />
|
||||||
|
{contactUsCollapse ? (
|
||||||
|
<ExpandLess />
|
||||||
|
) : (
|
||||||
|
<ExpandMore />
|
||||||
|
)}
|
||||||
|
</ListItemButton>
|
||||||
|
<Collapse
|
||||||
|
in={contactUsCollapse}
|
||||||
|
timeout="auto"
|
||||||
|
unmountOnExit
|
||||||
|
>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://discord.gg/Arn7TqJBvG')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary="Discord" />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://t.me/affineworkos')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary="Telegram" />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
'https://www.reddit.com/r/Affine/'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary="Reddit" />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
'https://medium.com/@affineworkos'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary="Medium" />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
'mailto:contact@toeverything.info'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItemText primary="Email" />
|
||||||
|
</ListItemButton>
|
||||||
|
</Collapse>
|
||||||
|
<ListItemButton onClick={handleLanguageClick}>
|
||||||
|
<ListItemText primary={t('language') as string} />
|
||||||
|
{languageCollapse ? <ExpandLess /> : <ExpandMore />}
|
||||||
|
</ListItemButton>
|
||||||
|
<Collapse
|
||||||
|
in={languageCollapse}
|
||||||
|
timeout="auto"
|
||||||
|
unmountOnExit
|
||||||
|
>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() => i18n.changeLanguage('en')}
|
||||||
|
>
|
||||||
|
<ListItemText primary="English" />
|
||||||
|
<Checkbox checked={i18n.language === 'en'} />
|
||||||
|
</ListItemButton>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() => i18n.changeLanguage('zh')}
|
||||||
|
>
|
||||||
|
<ListItemText primary="简体中文" />
|
||||||
|
<Checkbox checked={i18n.language === 'zh'} />
|
||||||
|
</ListItemButton>
|
||||||
|
</Collapse>
|
||||||
|
</List>
|
||||||
|
</StyledDrawerContainer>
|
||||||
|
</Drawer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = styled(Grid)({
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: '100%',
|
||||||
|
paddingTop: '1em',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
zIndex: 1500,
|
||||||
|
maxWidth: '1440px',
|
||||||
|
margin: 'auto',
|
||||||
|
marginTop: '0 !important',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledImage = styled('img')({
|
||||||
|
height: '24px',
|
||||||
|
marginRight: '16px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledDrawerContainer = styled('div')({
|
||||||
|
paddingTop: '72px',
|
||||||
|
});
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"Alternative": "Alternative",
|
"Alternative": "Alternative",
|
||||||
"Check GitHub": "Check GitHub",
|
"Check GitHub": "Check GitHub",
|
||||||
"Try it Online": "Try it Online",
|
"Try it Online": "Try it Online",
|
||||||
|
"language": "Language",
|
||||||
"description1": {
|
"description1": {
|
||||||
"part1": "Affine is the next-generation collaborative knowledge base for professionals.",
|
"part1": "Affine is the next-generation collaborative knowledge base for professionals.",
|
||||||
"part2": "It's not just a collection of Docs, whiteboard, and tables.",
|
"part2": "It's not just a collection of Docs, whiteboard, and tables.",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"Alternative": "的另一种选择",
|
"Alternative": "的另一种选择",
|
||||||
"Check GitHub": "GitHub中查看",
|
"Check GitHub": "GitHub中查看",
|
||||||
"Try it Online": "在线试用",
|
"Try it Online": "在线试用",
|
||||||
|
"language": "语言",
|
||||||
"description1": {
|
"description1": {
|
||||||
"part1": "Affine是面向专业人士的下一代协同知识库",
|
"part1": "Affine是面向专业人士的下一代协同知识库",
|
||||||
"part2": "它不仅仅是一个文档、白板和表格的集合。",
|
"part2": "它不仅仅是一个文档、白板和表格的集合。",
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||||||
import { Descendant } from 'slate';
|
import { Descendant } from 'slate';
|
||||||
|
|
||||||
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
|
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
|
||||||
import { BlockSearchItem } from '@toeverything/datasource/jwt';
|
|
||||||
import { styled } from '@toeverything/components/ui';
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
import { BlockSearchItem } from '@toeverything/datasource/jwt';
|
||||||
|
|
||||||
import { BlockPreview } from '../../block-preview';
|
import { BlockPreview } from '../../block-preview';
|
||||||
|
|
||||||
@@ -26,14 +26,14 @@ type InlineRefLinkProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const InlineRefLink = ({ block, pageId }: InlineRefLinkProps) => {
|
export const InlineRefLink = ({ block, pageId }: InlineRefLinkProps) => {
|
||||||
const { workspace_id } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
if (block) {
|
if (block) {
|
||||||
return (
|
return (
|
||||||
<BlockPreviewContainer
|
<BlockPreviewContainer
|
||||||
block={block}
|
block={block}
|
||||||
onClick={() => navigate(`/${workspace_id}/${pageId}`)}
|
onClick={() => navigate(`/${workspaceId}/${pageId}`)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,9 +164,6 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
|||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleLangChange(langType ? langType : DEFAULT_LANG);
|
handleLangChange(langType ? langType : DEFAULT_LANG);
|
||||||
// setTimeout(() => {
|
|
||||||
// focusCode();
|
|
||||||
// }, 100);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const copyCode = () => {
|
const copyCode = () => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
} from '../../components/text-manage';
|
} from '../../components/text-manage';
|
||||||
|
|
||||||
export const PageView = ({ block, editor }: CreateView) => {
|
export const PageView = ({ block, editor }: CreateView) => {
|
||||||
const { workspace_id } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const textRef = useRef<ExtendedTextUtils>(null);
|
const textRef = useRef<ExtendedTextUtils>(null);
|
||||||
const [backLinks, setBackLinks] =
|
const [backLinks, setBackLinks] =
|
||||||
useState<Awaited<ReturnType<typeof editor.search>>>();
|
useState<Awaited<ReturnType<typeof editor.search>>>();
|
||||||
@@ -100,7 +100,7 @@ export const PageView = ({ block, editor }: CreateView) => {
|
|||||||
className={style9(styles.content)}
|
className={style9(styles.content)}
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
> */}
|
> */}
|
||||||
<BackLink blocks={backLinks} workspaceId={workspace_id} />
|
<BackLink blocks={backLinks} workspaceId={workspaceId} />
|
||||||
{/*{block.childrenIds.map(childId => (*/}
|
{/*{block.childrenIds.map(childId => (*/}
|
||||||
{/* <RenderBlock key={childId} blockId={childId} />*/}
|
{/* <RenderBlock key={childId} blockId={childId} />*/}
|
||||||
{/*))}*/}
|
{/*))}*/}
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import { CreateView } from '@toeverything/framework/virgo';
|
|||||||
type RefLinkView = CreateView;
|
type RefLinkView = CreateView;
|
||||||
|
|
||||||
export const RefLinkView = ({ block, editor }: RefLinkView) => {
|
export const RefLinkView = ({ block, editor }: RefLinkView) => {
|
||||||
const page_id = useMemo(() => block.getProperty('reference'), [block]);
|
const pageId = useMemo(() => block.getProperty('reference'), [block]);
|
||||||
|
|
||||||
const [block_content, set_block] =
|
const [block_content, set_block] =
|
||||||
useState<Awaited<ReturnType<typeof editor.search>>[number]>();
|
useState<Awaited<ReturnType<typeof editor.search>>[number]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
editor
|
editor
|
||||||
.search({ tag: `id:${page_id}` })
|
.search({ tag: `id:${pageId}` })
|
||||||
.then(block => set_block(block[0]));
|
.then(block => set_block(block[0]));
|
||||||
}, [editor, page_id]);
|
}, [editor, pageId]);
|
||||||
|
|
||||||
return <InlineRefLink block={block_content} pageId={page_id} />;
|
return <InlineRefLink block={block_content} pageId={pageId} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ export const TextView = ({
|
|||||||
await block.removeChildren();
|
await block.removeChildren();
|
||||||
await block.after(nextBlock);
|
await block.after(nextBlock);
|
||||||
|
|
||||||
editor.selectionManager.activeNodeByNodeId(nextBlock.id);
|
setTimeout(() => {
|
||||||
|
editor.selectionManager.activeNodeByNodeId(nextBlock.id, 'start');
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { services } from '@toeverything/datasource/db-service';
|
import { services } from '@toeverything/datasource/db-service';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
import { WithEditorSelectionType } from '../menu/inline-menu/types';
|
import { WithEditorSelectionType } from '../menu/inline-menu/types';
|
||||||
|
|
||||||
export const useAddComment = ({
|
export const useAddComment = ({
|
||||||
@@ -8,7 +8,7 @@ export const useAddComment = ({
|
|||||||
selectionInfo,
|
selectionInfo,
|
||||||
setShow,
|
setShow,
|
||||||
}: WithEditorSelectionType) => {
|
}: WithEditorSelectionType) => {
|
||||||
const { workspace_id: workspaceId, page_id: pageId } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
const [currentComment, setCurrentComment] = useState('');
|
const [currentComment, setCurrentComment] = useState('');
|
||||||
|
|
||||||
const createComment = useCallback(async (): Promise<{
|
const createComment = useCallback(async (): Promise<{
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const DoubleLinkMenu = ({
|
|||||||
hooks,
|
hooks,
|
||||||
style,
|
style,
|
||||||
}: DoubleLinkMenuProps) => {
|
}: DoubleLinkMenuProps) => {
|
||||||
const { page_id: curPageId } = useParams();
|
const { pageId: curPageId } = useParams();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const dialogRef = useRef<HTMLDivElement>();
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const normalizeUrl = (url: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const LinkMenu = ({ editor, hooks }: LinkMenuProps) => {
|
export const LinkMenu = ({ editor, hooks }: LinkMenuProps) => {
|
||||||
const { page_id: curPageId } = useParams();
|
const { pageId: curPageId } = useParams();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const dialogRef = useRef<HTMLDivElement>();
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type SearchProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Search = (props: SearchProps) => {
|
export const Search = (props: SearchProps) => {
|
||||||
const { workspace_id: workspaceId } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const Switcher = () => {
|
|||||||
if (targetViewMode === pageViewMode) {
|
if (targetViewMode === pageViewMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const workspaceId = params['workspace_id'];
|
const workspaceId = params['workspaceId'];
|
||||||
/**
|
/**
|
||||||
* There are two possible modes:
|
* There are two possible modes:
|
||||||
* Page mode: /{workspaceId}/{pageId}
|
* Page mode: /{workspaceId}/{pageId}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const AffineHeader = () => {
|
|||||||
useUserAndSpaces();
|
useUserAndSpaces();
|
||||||
|
|
||||||
const showCenterTab =
|
const showCenterTab =
|
||||||
(params['workspace_id'] || pathname.includes('/space/')) && params['*'];
|
(params['workspaceId'] || pathname.includes('/space/')) && params['*'];
|
||||||
|
|
||||||
if (hideAffineHeader(pathname)) {
|
if (hideAffineHeader(pathname)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -82,7 +82,7 @@ export const AffineHeader = () => {
|
|||||||
isEdgelessView
|
isEdgelessView
|
||||||
? navigate(
|
? navigate(
|
||||||
`/${
|
`/${
|
||||||
params['workspace_id'] ||
|
params['workspaceId'] ||
|
||||||
'space'
|
'space'
|
||||||
}/${params['*'].slice(0, -11)}`
|
}/${params['*'].slice(0, -11)}`
|
||||||
)
|
)
|
||||||
@@ -108,7 +108,7 @@ export const AffineHeader = () => {
|
|||||||
? null
|
? null
|
||||||
: navigate(
|
: navigate(
|
||||||
`/${
|
`/${
|
||||||
params['workspace_id'] ||
|
params['workspaceId'] ||
|
||||||
'space'
|
'space'
|
||||||
}/${params['*']}` + '/edgeless'
|
}/${params['*']}` + '/edgeless'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const MESSAGES = {
|
|||||||
|
|
||||||
function PageSettingPortal() {
|
function PageSettingPortal() {
|
||||||
const [alertOpen, setAlertOpen] = useState(false);
|
const [alertOpen, setAlertOpen] = useState(false);
|
||||||
const { workspace_id } = useParams();
|
const { workspaceId } = useParams();
|
||||||
const [pageBlock, setPageBlock] = useState<PageBlock>();
|
const [pageBlock, setPageBlock] = useState<PageBlock>();
|
||||||
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -64,7 +64,7 @@ function PageSettingPortal() {
|
|||||||
|
|
||||||
const fetchPageBlock = useCallback(async () => {
|
const fetchPageBlock = useCallback(async () => {
|
||||||
const dbPageBlock = await services.api.editorBlock.getBlock(
|
const dbPageBlock = await services.api.editorBlock.getBlock(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
pageId
|
pageId
|
||||||
);
|
);
|
||||||
if (!dbPageBlock) return;
|
if (!dbPageBlock) return;
|
||||||
@@ -78,11 +78,11 @@ function PageSettingPortal() {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
text.value[0].text,
|
text.value[0].text,
|
||||||
});
|
});
|
||||||
}, [workspace_id, pageId]);
|
}, [workspaceId, pageId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPageBlock();
|
fetchPageBlock();
|
||||||
}, [workspace_id, pageId, fetchPageBlock]);
|
}, [workspaceId, pageId, fetchPageBlock]);
|
||||||
const redirectToPage = (newWorkspaceId: string, newPageId: string) => {
|
const redirectToPage = (newWorkspaceId: string, newPageId: string) => {
|
||||||
navigate('/' + newWorkspaceId + '/' + newPageId);
|
navigate('/' + newWorkspaceId + '/' + newPageId);
|
||||||
};
|
};
|
||||||
@@ -90,23 +90,23 @@ function PageSettingPortal() {
|
|||||||
const handleDuplicatePage = async () => {
|
const handleDuplicatePage = async () => {
|
||||||
//create page
|
//create page
|
||||||
const newPage = await services.api.editorBlock.create({
|
const newPage = await services.api.editorBlock.create({
|
||||||
workspace: workspace_id,
|
workspace: workspaceId,
|
||||||
type: 'page' as const,
|
type: 'page' as const,
|
||||||
});
|
});
|
||||||
//add page to tree
|
//add page to tree
|
||||||
await services.api.pageTree.addNextPageToWorkspace(
|
await services.api.pageTree.addNextPageToWorkspace(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
pageId,
|
pageId,
|
||||||
newPage.id
|
newPage.id
|
||||||
);
|
);
|
||||||
//copy source page to new page
|
//copy source page to new page
|
||||||
await services.api.editorBlock.copyPage(
|
await services.api.editorBlock.copyPage(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
pageId,
|
pageId,
|
||||||
newPage.id
|
newPage.id
|
||||||
);
|
);
|
||||||
|
|
||||||
redirectToPage(workspace_id, newPage.id);
|
redirectToPage(workspaceId, newPage.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
@@ -128,7 +128,7 @@ function PageSettingPortal() {
|
|||||||
.inspector()
|
.inspector()
|
||||||
.load()
|
.load()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location.href = `/${workspace_id}/`;
|
window.location.href = `/${workspaceId}/`;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ function PageSettingPortal() {
|
|||||||
fullWidthChecked: checked,
|
fullWidthChecked: checked,
|
||||||
},
|
},
|
||||||
id: pageId,
|
id: pageId,
|
||||||
workspace: workspace_id,
|
workspace: workspaceId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { useUserAndSpaces } from '@toeverything/datasource/state';
|
|||||||
export const CurrentPageTitle = () => {
|
export const CurrentPageTitle = () => {
|
||||||
const { user } = useUserAndSpaces();
|
const { user } = useUserAndSpaces();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { workspace_id } = params;
|
const { workspaceId } = params;
|
||||||
const [pageId, setPageId] = useState<string>('');
|
const [pageId, setPageId] = useState<string>('');
|
||||||
const [pageTitle, setPageTitle] = useState<string | undefined>();
|
const [pageTitle, setPageTitle] = useState<string | undefined>();
|
||||||
/* card.7 */
|
/* card.7 */
|
||||||
@@ -25,9 +25,9 @@ export const CurrentPageTitle = () => {
|
|||||||
}, [params]);
|
}, [params]);
|
||||||
|
|
||||||
const fetchPageTitle = useCallback(async () => {
|
const fetchPageTitle = useCallback(async () => {
|
||||||
if (!workspace_id || !pageId) return;
|
if (!workspaceId || !pageId) return;
|
||||||
const [pageEditorBlock] = await services.api.editorBlock.get({
|
const [pageEditorBlock] = await services.api.editorBlock.get({
|
||||||
workspace: workspace_id,
|
workspace: workspaceId,
|
||||||
ids: [pageId],
|
ids: [pageId],
|
||||||
});
|
});
|
||||||
/* card.7 */
|
/* card.7 */
|
||||||
@@ -39,21 +39,21 @@ export const CurrentPageTitle = () => {
|
|||||||
?.map(v => v.text)
|
?.map(v => v.text)
|
||||||
.join('') ?? 'Untitled'
|
.join('') ?? 'Untitled'
|
||||||
);
|
);
|
||||||
}, [pageId, workspace_id]);
|
}, [pageId, workspaceId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) fetchPageTitle();
|
if (user) fetchPageTitle();
|
||||||
}, [fetchPageTitle, user]);
|
}, [fetchPageTitle, user]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user || !workspace_id || !pageId || pageTitle === undefined)
|
if (!user || !workspaceId || !pageId || pageTitle === undefined)
|
||||||
return () => {};
|
return () => {};
|
||||||
|
|
||||||
let unobserve: () => void;
|
let unobserve: () => void;
|
||||||
const auto_update_title = async () => {
|
const auto_update_title = async () => {
|
||||||
// console.log(';; title registration auto update');
|
// console.log(';; title registration auto update');
|
||||||
unobserve = await services.api.editorBlock.observe(
|
unobserve = await services.api.editorBlock.observe(
|
||||||
{ workspace: workspace_id, id: pageId },
|
{ workspace: workspaceId, id: pageId },
|
||||||
businessBlock => {
|
businessBlock => {
|
||||||
// console.log(';; auto_update_title', businessBlock);
|
// console.log(';; auto_update_title', businessBlock);
|
||||||
fetchPageTitle();
|
fetchPageTitle();
|
||||||
@@ -65,7 +65,7 @@ export const CurrentPageTitle = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
// unobserve?.();
|
// unobserve?.();
|
||||||
};
|
};
|
||||||
}, [fetchPageTitle, pageId, pageTitle, user, workspace_id]);
|
}, [fetchPageTitle, pageId, pageTitle, user, workspaceId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = pageTitle || '';
|
document.title = pageTitle || '';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import type { CommentInfo } from './type';
|
import type { CommentInfo } from './type';
|
||||||
|
|
||||||
export const useComments = () => {
|
export const useComments = () => {
|
||||||
const { workspace_id: workspaceId, page_id: pageId } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
|
|
||||||
const [comments, setComments] = useState<CommentInfo[]>([]);
|
const [comments, setComments] = useState<CommentInfo[]>([]);
|
||||||
const [observeIds, setObserveIds] = useState<string[]>([]);
|
const [observeIds, setObserveIds] = useState<string[]>([]);
|
||||||
@@ -71,7 +71,7 @@ export const useComments = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useActiveComment = () => {
|
export const useActiveComment = () => {
|
||||||
const { workspace_id: workspaceId, page_id: pageId } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
const { currentEditors } = useCurrentEditors();
|
const { currentEditors } = useCurrentEditors();
|
||||||
const editor = useMemo(() => {
|
const editor = useMemo(() => {
|
||||||
return currentEditors[pageId] as Virgo;
|
return currentEditors[pageId] as Virgo;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface UseWorkspaceAndPageIdReturn {
|
|||||||
|
|
||||||
export const useWorkspaceAndPageId = (): UseWorkspaceAndPageIdReturn => {
|
export const useWorkspaceAndPageId = (): UseWorkspaceAndPageIdReturn => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const workspaceId = params['workspace_id'];
|
const workspaceId = params['workspaceId'];
|
||||||
const pageId = params['*'].split('/')[0];
|
const pageId = params['*'].split('/')[0];
|
||||||
return {
|
return {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ export async function fetchActivitiesHeatmap(
|
|||||||
// const pages = await db.getByType('page');
|
// const pages = await db.getByType('page');
|
||||||
const pages_with_ids = (await Promise.all(
|
const pages_with_ids = (await Promise.all(
|
||||||
flattenedItems.map(async (page_item: any) => {
|
flattenedItems.map(async (page_item: any) => {
|
||||||
const page_id = page_item.id;
|
const pageId = page_item.id;
|
||||||
return [page_id, await db.get(page_id as 'page')];
|
return [pageId, await db.get(pageId as 'page')];
|
||||||
})
|
})
|
||||||
)) as [string, BlockImplInstance][];
|
)) as [string, BlockImplInstance][];
|
||||||
const pages = new Map(pages_with_ids);
|
const pages = new Map(pages_with_ids);
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
|||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { workspace_id, page_id } = useParams();
|
const params = useParams();
|
||||||
|
const workspaceId = params['workspaceId'];
|
||||||
|
const curPageId = params['pageId'];
|
||||||
const BooleanPageTreeItemMoreActions = useFlag(
|
const BooleanPageTreeItemMoreActions = useFlag(
|
||||||
'BooleanPageTreeItemMoreActions',
|
'BooleanPageTreeItemMoreActions',
|
||||||
true
|
true
|
||||||
@@ -71,7 +73,7 @@ export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
|||||||
disableSelection={disableSelection}
|
disableSelection={disableSelection}
|
||||||
disableInteraction={disableInteraction}
|
disableInteraction={disableInteraction}
|
||||||
spacing={`${indentationWidth * depth + 12}px`}
|
spacing={`${indentationWidth * depth + 12}px`}
|
||||||
active={pageId === page_id}
|
active={pageId === curPageId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{childCount !== 0 ? (
|
{childCount !== 0 ? (
|
||||||
@@ -89,12 +91,12 @@ export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<TreeItemContent {...handleProps}>
|
<TreeItemContent {...handleProps}>
|
||||||
<TextLink to={`/${workspace_id}/${pageId}`}>
|
<TextLink to={`/${workspaceId}/${pageId}`}>
|
||||||
{value}
|
{value}
|
||||||
</TextLink>
|
</TextLink>
|
||||||
{BooleanPageTreeItemMoreActions && (
|
{BooleanPageTreeItemMoreActions && (
|
||||||
<MoreActions
|
<MoreActions
|
||||||
workspaceId={workspace_id}
|
workspaceId={workspaceId}
|
||||||
pageId={pageId}
|
pageId={pageId}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
const page_tree_atom = atom<TreeItems | undefined>([]);
|
const page_tree_atom = atom<TreeItems | undefined>([]);
|
||||||
|
|
||||||
export const usePageTree = ({ indentationWidth = 16 }: DndTreeProps = {}) => {
|
export const usePageTree = ({ indentationWidth = 16 }: DndTreeProps = {}) => {
|
||||||
const { workspace_id, page_id } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [items] = useAtom(page_tree_atom);
|
const [items] = useAtom(page_tree_atom);
|
||||||
const [activeId, setActiveId] = useState<string | undefined>(undefined);
|
const [activeId, setActiveId] = useState<string | undefined>(undefined);
|
||||||
@@ -56,11 +56,11 @@ export const usePageTree = ({ indentationWidth = 16 }: DndTreeProps = {}) => {
|
|||||||
const savePageTreeData = useCallback(
|
const savePageTreeData = useCallback(
|
||||||
async (treeData?: TreeItem[]) => {
|
async (treeData?: TreeItem[]) => {
|
||||||
await services.api.pageTree.setPageTree<TreeItem>(
|
await services.api.pageTree.setPageTree<TreeItem>(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
treeData || []
|
treeData || []
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[workspace_id]
|
[workspaceId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const resetState = useCallback(() => {
|
const resetState = useCallback(() => {
|
||||||
@@ -129,19 +129,19 @@ export const usePageTree = ({ indentationWidth = 16 }: DndTreeProps = {}) => {
|
|||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
async (id: string) => {
|
async (id: string) => {
|
||||||
await savePageTreeData(removeItem(items, id));
|
await savePageTreeData(removeItem(items, id));
|
||||||
await services.api.userConfig.removePage(workspace_id, id);
|
await services.api.userConfig.removePage(workspaceId, id);
|
||||||
//remove page from jwst
|
//remove page from jwst
|
||||||
await services.api.pageTree.removePage(workspace_id, id);
|
await services.api.pageTree.removePage(workspaceId, id);
|
||||||
if (id === page_id) {
|
if (id === pageId) {
|
||||||
navigate(`/${workspace_id}`);
|
navigate(`/${workspaceId}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[items, savePageTreeData, workspace_id]
|
[items, savePageTreeData, workspaceId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAddPage = useCallback(
|
const handleAddPage = useCallback(
|
||||||
async (page_id?: string) => {
|
async (pageId?: string) => {
|
||||||
await savePageTreeData([{ id: page_id, children: [] }, ...items]);
|
await savePageTreeData([{ id: pageId, children: [] }, ...items]);
|
||||||
},
|
},
|
||||||
[items, savePageTreeData]
|
[items, savePageTreeData]
|
||||||
);
|
);
|
||||||
@@ -177,25 +177,25 @@ export const usePageTree = ({ indentationWidth = 16 }: DndTreeProps = {}) => {
|
|||||||
|
|
||||||
export const useDndTreeAutoUpdate = () => {
|
export const useDndTreeAutoUpdate = () => {
|
||||||
const [, set_items] = useAtom(page_tree_atom);
|
const [, set_items] = useAtom(page_tree_atom);
|
||||||
const { workspace_id, page_id } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
|
|
||||||
const fetch_page_tree_data = useCallback(async () => {
|
const fetch_page_tree_data = useCallback(async () => {
|
||||||
const pages = await services.api.pageTree.getPageTree<TreeItem>(
|
const pages = await services.api.pageTree.getPageTree<TreeItem>(
|
||||||
workspace_id
|
workspaceId
|
||||||
);
|
);
|
||||||
set_items(pages);
|
set_items(pages);
|
||||||
}, [set_items, workspace_id]);
|
}, [set_items, workspaceId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch_page_tree_data();
|
fetch_page_tree_data();
|
||||||
}, [fetch_page_tree_data]);
|
}, [fetch_page_tree_data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!page_id) return () => {};
|
if (!pageId) return () => {};
|
||||||
let unobserve: () => void;
|
let unobserve: () => void;
|
||||||
const auto_update_page_tree = async () => {
|
const auto_update_page_tree = async () => {
|
||||||
unobserve = await services.api.pageTree.observe(
|
unobserve = await services.api.pageTree.observe(
|
||||||
{ workspace: workspace_id, page: page_id },
|
{ workspace: workspaceId, page: pageId },
|
||||||
() => {
|
() => {
|
||||||
fetch_page_tree_data();
|
fetch_page_tree_data();
|
||||||
}
|
}
|
||||||
@@ -206,5 +206,5 @@ export const useDndTreeAutoUpdate = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
unobserve?.();
|
unobserve?.();
|
||||||
};
|
};
|
||||||
}, [fetch_page_tree_data, page_id, workspace_id]);
|
}, [fetch_page_tree_data, pageId, workspaceId]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -265,37 +265,34 @@ export class EditorBlock extends ServiceBaseClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async copyPage(
|
async copyPage(
|
||||||
workspace_id: string,
|
workspaceId: string,
|
||||||
source_page_id: string,
|
sourcePageId: string,
|
||||||
new_page_id: string
|
newPageId: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const db = await this.database.getDatabase(workspace_id);
|
const db = await this.database.getDatabase(workspaceId);
|
||||||
|
|
||||||
const source_page = await this.getBlock(
|
const sourcePage = await this.getBlock(
|
||||||
workspace_id,
|
workspaceId,
|
||||||
source_page_id as 'block'
|
sourcePageId as 'block'
|
||||||
);
|
);
|
||||||
const new_page = await this.getBlock(
|
const newPage = await this.getBlock(workspaceId, newPageId as 'block');
|
||||||
workspace_id,
|
if (!sourcePage) {
|
||||||
new_page_id as 'block'
|
|
||||||
);
|
|
||||||
if (!source_page) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const source_page_children = source_page.children;
|
const sourcePageChildren = sourcePage.children;
|
||||||
const decorations = source_page.getDecorations();
|
const decorations = sourcePage.getDecorations();
|
||||||
Object.entries(decorations).forEach(([key, value]) => {
|
Object.entries(decorations).forEach(([key, value]) => {
|
||||||
new_page?.setDecoration(key, source_page.getDecoration(key));
|
newPage?.setDecoration(key, sourcePage.getDecoration(key));
|
||||||
});
|
});
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
this.decorate_page_title(new_page, 'copy from ');
|
this.decorate_page_title(newPage, 'copy from ');
|
||||||
|
|
||||||
for (let i = 0; i < source_page_children.length; i++) {
|
for (let i = 0; i < sourcePageChildren.length; i++) {
|
||||||
const source_page_child = await db.get(
|
const sourcePageChild = await db.get(
|
||||||
source_page_children[i] as 'block'
|
sourcePageChildren[i] as 'block'
|
||||||
);
|
);
|
||||||
new_page?.insertChildren(source_page_child);
|
newPage?.insertChildren(sourcePageChild);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
|||||||
const [file] = (await fromEvent(handles)) as File[];
|
const [file] = (await fromEvent(handles)) as File[];
|
||||||
const binary = await file?.arrayBuffer();
|
const binary = await file?.arrayBuffer();
|
||||||
console.log(this._provider.providers);
|
console.log(this._provider.providers);
|
||||||
let { indexeddb } = (
|
const { indexeddb } = (
|
||||||
this._provider.providers as any[]
|
this._provider.providers as any[]
|
||||||
).find(p => p.indexeddb);
|
).find(p => p.indexeddb);
|
||||||
await indexeddb?.idb?.clearData();
|
await indexeddb?.idb?.clearData();
|
||||||
@@ -391,9 +391,9 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
|||||||
},
|
},
|
||||||
parse: () => this._doc.toJSON(),
|
parse: () => this._doc.toJSON(),
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
parse_page: (page_id: string) => {
|
parse_page: (pageId: string) => {
|
||||||
const blocks = this._blocks.toJSON();
|
const blocks = this._blocks.toJSON();
|
||||||
return resolve_block(blocks, page_id);
|
return resolve_block(blocks, pageId);
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
parse_pages: (resolve = false) => {
|
parse_pages: (resolve = false) => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const _currentEditors = atom<EditorsMap>({} as EditorsMap);
|
|||||||
|
|
||||||
/** hook for using editors outside page */
|
/** hook for using editors outside page */
|
||||||
export const useCurrentEditors = () => {
|
export const useCurrentEditors = () => {
|
||||||
const { workspace_id: workspaceId, page_id: pageId } = useParams();
|
const { workspaceId, pageId } = useParams();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const [currentEditors, setCurrentEditors] = useAtom(_currentEditors);
|
const [currentEditors, setCurrentEditors] = useAtom(_currentEditors);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
} from 'firebase/auth';
|
} from 'firebase/auth';
|
||||||
import { atom, useAtom } from 'jotai';
|
import { atom, useAtom } from 'jotai';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router';
|
|
||||||
|
|
||||||
import { useIdentifyUser } from '@toeverything/datasource/feature-flags';
|
import { useIdentifyUser } from '@toeverything/datasource/feature-flags';
|
||||||
import { UserInfo } from '@toeverything/utils';
|
import { UserInfo } from '@toeverything/utils';
|
||||||
@@ -63,15 +62,11 @@ const BRAND_ID = 'AFFiNE';
|
|||||||
|
|
||||||
const _localTrigger = atom<boolean>(false);
|
const _localTrigger = atom<boolean>(false);
|
||||||
const _useUserAndSpacesForFreeLogin = () => {
|
const _useUserAndSpacesForFreeLogin = () => {
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [user, setUser] = useAtom(_userAtom);
|
const [user, setUser] = useAtom(_userAtom);
|
||||||
const [loading, setLoading] = useAtom(_loadingAtom);
|
const [loading, setLoading] = useAtom(_loadingAtom);
|
||||||
const [localTrigger] = useAtom(_localTrigger);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
navigate(`/${BRAND_ID}`);
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ export function getUserDisplayName(user?: UserInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get page_id from URL
|
* Get pageId from URL
|
||||||
* @returns page_id
|
* @returns pageId
|
||||||
*/
|
*/
|
||||||
export function getPageId() {
|
export function getPageId() {
|
||||||
const path = window.location.pathname.match(/\/(\w+)\/(\w+)/);
|
const path = window.location.pathname.match(/\/(\w+)\/(\w+)/);
|
||||||
|
|||||||
Reference in New Issue
Block a user