fix: bugs

This commit is contained in:
Kilu 2024-06-29 16:18:33 +08:00
parent e211fdce13
commit 215910cd24
22 changed files with 170 additions and 77 deletions

View File

@ -1,7 +1,6 @@
import { YDoc } from '@/application/collab.type';
import { db } from '@/application/db';
import { ViewMeta } from '@/application/db/tables/view_metas';
import { notify } from '@/components/_shared/notify';
import { AFConfigContext } from '@/components/app/AppConfig';
import { useLiveQuery } from 'dexie-react-hooks';
import { createContext, useCallback, useContext } from 'react';
@ -50,7 +49,6 @@ export const PublishProvider = ({
navigate(`/${namespace}/${publishName}`);
} catch (e) {
notify.error('The view has not been published yet.');
return Promise.reject(e);
}
},

View File

@ -5,6 +5,12 @@ export const notify = {
error: (message: string) => {
window.toast.error(message);
},
default: (message: string) => {
window.toast.default(message);
},
warning: (message: string) => {
window.toast.warning(message);
},
info: (message: string) => {
window.toast.info(message);
},

View File

@ -1,4 +1,5 @@
import { useAppLanguage } from '@/components/app/useAppLanguage';
import { useSnackbar } from 'notistack';
import React, { createContext, useEffect, useState } from 'react';
import { AFService, AFServiceConfig } from '@/application/services/services.type';
import { getService } from '@/application/services';
@ -43,6 +44,35 @@ function AppConfig({ children }: { children: React.ReactNode }) {
})();
}, [appConfig]);
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
useEffect(() => {
const commonClasses = 'flex items-center justify-center gap-3 bg-bg-body';
window.toast = {
success: (message: string) => {
enqueueSnackbar(message, { variant: 'success' });
},
error: (message: string) => {
console.log('error', message);
enqueueSnackbar(message, { variant: 'error' });
},
warning: (message: string) => {
enqueueSnackbar(message, { variant: 'warning' });
},
default: (message: string) => {
enqueueSnackbar(message, { variant: 'default' });
},
info: (message: string) => {
enqueueSnackbar(message, { variant: 'info' });
},
clear: () => {
closeSnackbar();
},
};
}, [closeSnackbar, enqueueSnackbar]);
return (
<AFConfigContext.Provider
value={{

View File

@ -2,37 +2,38 @@ import { ErrorBoundary } from 'react-error-boundary';
import { ErrorHandlerPage } from 'src/components/error/ErrorHandlerPage';
import AppTheme from '@/components/app/AppTheme';
import AppConfig from '@/components/app/AppConfig';
import { Suspense, useEffect } from 'react';
import { SnackbarProvider, useSnackbar } from 'notistack';
import { Suspense } from 'react';
import { SnackbarProvider } from 'notistack';
import { styled } from '@mui/material';
const StyledSnackbarProvider = styled(SnackbarProvider)`
&.notistack-MuiContent-default {
background-color: var(--fill-toolbar);
}
&.notistack-MuiContent-info {
background-color: var(--function-info);
}
&.notistack-MuiContent-success {
background-color: var(--function-success);
}
&.notistack-MuiContent-error {
background-color: var(--function-error);
}
&.notistack-MuiContent-warning {
background-color: var(--function-warning);
}
`;
export default function withAppWrapper(Component: React.FC): React.FC {
return function AppWrapper(): JSX.Element {
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
useEffect(() => {
window.toast = {
success: (message: string) => {
enqueueSnackbar(message, { variant: 'success' });
},
error: (message: string) => {
enqueueSnackbar(message, { variant: 'error' });
},
warning: (message: string) => {
enqueueSnackbar(message, { variant: 'warning' });
},
info: (message: string) => {
enqueueSnackbar(message, { variant: 'info' });
},
clear: () => {
closeSnackbar();
},
};
}, [closeSnackbar, enqueueSnackbar]);
return (
<AppTheme>
<ErrorBoundary FallbackComponent={ErrorHandlerPage}>
<SnackbarProvider
<StyledSnackbarProvider
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
@ -44,7 +45,7 @@ export default function withAppWrapper(Component: React.FC): React.FC {
<Component />
</Suspense>
</AppConfig>
</SnackbarProvider>
</StyledSnackbarProvider>
</ErrorBoundary>
</AppTheme>
);

View File

@ -1,6 +1,9 @@
import { AlignType } from '@/application/collab.type';
import { notify } from '@/components/_shared/notify';
import { EditorElementProps, ImageBlockNode } from '@/components/editor/editor.type';
import { copyTextToClipboard } from '@/utils/copy';
import React, { forwardRef, memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactEditor, useSelected, useSlateStatic } from 'slate-react';
import ImageEmpty from './ImageEmpty';
import ImageRender from './ImageRender';
@ -24,14 +27,22 @@ export const ImageBlock = memo(
return align === AlignType.Center ? 'justify-center' : align === AlignType.Right ? 'justify-end' : 'justify-start';
}, [align]);
const { t } = useTranslation();
return (
<div
{...attributes}
ref={containerRef}
onClick={() => {
if (!selected) onFocusNode();
onClick={async () => {
if (!url) return;
try {
await copyTextToClipboard(url);
notify.success(t('document.plugins.image.copiedToPasteBoard'));
} catch (_) {
// do nothing
}
}}
className={`${className || ''} image-block relative w-full cursor-pointer py-1`}
className={`${className || ''} image-block relative w-full py-1 ${url ? 'cursor-pointer' : 'cursor-default'}`}
>
<div ref={ref} className={'absolute left-0 top-0 h-full w-full select-none caret-transparent'}>
{children}

View File

@ -1,5 +1,7 @@
import KatexMath from '@/components/_shared/katex-math/KatexMath';
import { notify } from '@/components/_shared/notify';
import { EditorElementProps, MathEquationNode } from '@/components/editor/editor.type';
import { copyTextToClipboard } from '@/utils/copy';
import { FunctionsOutlined } from '@mui/icons-material';
import { forwardRef, memo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
@ -16,7 +18,18 @@ export const MathEquation = memo(
<div
{...attributes}
ref={containerRef}
className={`${className} math-equation-block relative w-full cursor-pointer py-2`}
onClick={async () => {
if (!formula) return;
try {
await copyTextToClipboard(formula);
notify.success(t('document.plugins.math.copiedToPasteBoard'));
} catch (_) {
// do nothing
}
}}
className={`${className} math-equation-block relative w-full ${
formula ? 'cursor-pointer' : 'cursor-default'
} py-2`}
>
<div
contentEditable={false}

View File

@ -25,7 +25,7 @@ export const Outline = memo(
if (element) {
void smoothScrollIntoViewIfNeeded(element, {
behavior: 'smooth',
block: 'center',
block: 'start',
});
}
}, []);

View File

@ -1,3 +1,4 @@
import { AFScroller } from '@/components/_shared/scroller';
import { EditorElementProps, TableCellNode, TableNode } from '@/components/editor/editor.type';
import React, { forwardRef, memo, useMemo } from 'react';
import { Grid } from '@atlaskit/primitives';
@ -38,7 +39,12 @@ const Table = memo(
}, [rowGroup, rowDefaultHeight]);
return (
<div ref={ref} {...attributes} className={`table-block relative my-2 w-full px-1 ${className || ''}`}>
<div
ref={ref}
{...attributes}
className={`table-block relative my-2 w-full overflow-hidden px-1 ${className || ''}`}
>
<div className={'h-full w-full overflow-x-auto overflow-y-hidden'}>
<Grid
id={`table-${node.blockId}`}
rowGap='space.0'
@ -50,6 +56,7 @@ const Table = memo(
{children}
</Grid>
</div>
</div>
);
}),
(prevProps, nextProps) => isEqual(prevProps.node, nextProps.node)

View File

@ -9,7 +9,7 @@ function MentionDate({ date, reminder }: { date: string; reminder?: { id: string
}, [date]);
return (
<span className={'mention-inline'}>
<span className={'mention-inline cursor-text'}>
{reminder ? <ReminderSvg className={'mention-icon'} /> : <DateSvg className={'mention-icon'} />}
<span className={'mention-content'}>{dateFormat}</span>

View File

@ -41,7 +41,7 @@ function MentionPage({ pageId }: { pageId: string }) {
contentEditable={false}
>
{unPublished ? (
<span className={'mention-unpublished font-semibold text-text-caption'}>No Access</span>
<span className={'mention-unpublished cursor-text font-semibold text-text-caption'}>No Access</span>
) : (
<>
<span className={'mention-icon icon'}>

View File

@ -1,3 +1,4 @@
@use "src/styles/mixin.scss";
.block-element:not([data-block-type="table/cell"]) {
@apply my-[4px];
@ -301,3 +302,7 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) {
margin-bottom: 0px;
}
}
.table-block {
@include mixin.scrollbar-style;
}

View File

@ -27,7 +27,7 @@ function BreadcrumbItem({ crumb, disableClick = false }: { crumb: Crumb; disable
try {
await onNavigateToView?.(viewId);
} catch (e) {
notify.error(t('publish.hasNotBeenPublished'));
notify.default(t('publish.hasNotBeenPublished'));
}
}}
>

View File

@ -72,7 +72,10 @@ function MoreActions() {
<div className={'flex w-[240px] flex-col gap-2 px-2 py-2'}>
{actions.map((action, index) => (
<button
onClick={action.onClick}
onClick={() => {
action.onClick();
handleClose();
}}
key={index}
className={
'flex items-center gap-2 rounded-[8px] p-1.5 text-sm hover:bg-content-blue-50 focus:bg-content-blue-50 focus:outline-none'

View File

@ -1,9 +1,10 @@
import { usePublishContext } from '@/application/publish';
import { openOrDownload } from '@/components/publish/header/utils';
import { Divider, IconButton } from '@mui/material';
import { Divider, IconButton, Tooltip } from '@mui/material';
import { debounce } from 'lodash-es';
import React, { useCallback, useMemo } from 'react';
import OutlinePopover from '@/components/publish/outline/OutlinePopover';
import { useTranslation } from 'react-i18next';
import Breadcrumb from './Breadcrumb';
import { ReactComponent as Logo } from '@/assets/logo.svg';
import MoreActions from './MoreActions';
@ -18,6 +19,7 @@ export function PublishViewHeader({
openDrawer: boolean;
drawerWidth: number;
}) {
const { t } = useTranslation();
const viewMeta = usePublishContext()?.viewMeta;
const crumbs = useMemo(() => {
const ancestors = viewMeta?.ancestor_views.slice(1) || [];
@ -95,9 +97,11 @@ export function PublishViewHeader({
<div className={'flex items-center gap-2'}>
<MoreActions />
<Divider orientation={'vertical'} className={'mx-2'} flexItem />
<Tooltip title={t('publish.downloadApp')}>
<button onClick={openOrDownload}>
<Logo className={'h-6 w-6'} />
</button>
</Tooltip>
</div>
</div>
</div>

View File

@ -4,10 +4,11 @@ import SearchInput from '@/components/publish/outline/SearchInput';
import { filterViews } from '@/components/publish/outline/utils';
import { CircularProgress } from '@mui/material';
import React, { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
function Outline({ viewMeta }: { viewMeta?: PublishViewInfo }) {
function Outline({ viewMeta, width }: { viewMeta?: PublishViewInfo; width: number }) {
const hasChildren = Boolean(viewMeta?.child_views?.length);
const { t } = useTranslation();
const [children, setChildren] = React.useState<PublishViewInfo[]>([]);
useEffect(() => {
@ -45,14 +46,16 @@ function Outline({ viewMeta }: { viewMeta?: PublishViewInfo }) {
<SearchInput onSearch={handleSearch} />
</div>
{hasChildren && (
{hasChildren ? (
<div className={'flex w-full flex-1 flex-col'}>
{children
.filter((view) => view.layout === ViewLayout.Document)
.map((view: PublishViewInfo) => (
<OutlineItem key={view.view_id} view={view} />
<OutlineItem width={width} key={view.view_id} view={view} />
))}
</div>
) : (
<div className={'flex w-full flex-1 items-center justify-center text-text-caption'}>{t('noPagesInside')}</div>
)}
</div>
);

View File

@ -55,7 +55,7 @@ function OutlineDrawer({ open, width, onClose }: { open: boolean; width: number;
</Tooltip>
</div>
<div className={'flex flex-1 flex-col overflow-y-auto px-4 pb-4'}>
<Outline viewMeta={viewMeta} />
<Outline width={width} viewMeta={viewMeta} />
</div>
</div>
</Drawer>

View File

@ -6,12 +6,15 @@ import React, { useCallback, useContext } from 'react';
import { ReactComponent as ChevronDownIcon } from '@/assets/chevron_down.svg';
import { useTranslation } from 'react-i18next';
function OutlineItem({ view }: { view: PublishViewInfo }) {
function OutlineItem({ view, level = 0, width }: { view: PublishViewInfo; width: number; level: number }) {
const [isExpanded, setIsExpanded] = React.useState(false);
const getIcon = useCallback(() => {
if (isExpanded) {
return (
<button
style={{
paddingLeft: 1.125 * level + 'rem',
}}
onClick={() => {
setIsExpanded(false);
}}
@ -23,6 +26,9 @@ function OutlineItem({ view }: { view: PublishViewInfo }) {
return (
<button
style={{
paddingLeft: 1.125 * level + 'rem',
}}
onClick={() => {
setIsExpanded(true);
}}
@ -30,24 +36,21 @@ function OutlineItem({ view }: { view: PublishViewInfo }) {
<ChevronDownIcon className={'h-4 w-4 -rotate-90 transform'} />
</button>
);
}, [isExpanded]);
}, [isExpanded, level]);
const { t } = useTranslation();
const navigateToView = useContext(PublishContext)?.toView;
const renderItem = (item: PublishViewInfo) => {
const { icon, layout, name, view_id } = item;
const hasChildren = Boolean(item.child_views?.length);
return (
<div className={'flex h-fit flex-col gap-2'}>
<div
style={{
marginLeft: hasChildren ? '0' : '1.125rem',
width: width - 32,
}}
className={'flex h-fit flex-col gap-2'}
>
<div
className={
'flex w-full items-center gap-0.5 rounded-[8px] p-1.5 text-sm hover:bg-content-blue-50 focus:bg-content-blue-50 focus:outline-none'
'flex items-center gap-0.5 rounded-[8px] p-1.5 text-sm hover:bg-content-blue-50 focus:bg-content-blue-50 focus:outline-none'
}
>
{item.child_views?.length ? getIcon() : null}
@ -59,7 +62,10 @@ function OutlineItem({ view }: { view: PublishViewInfo }) {
notify.error(t('publish.hasNotBeenPublished'));
}
}}
className={'flex flex-1 cursor-pointer items-center gap-1 overflow-hidden'}
style={{
paddingLeft: item.child_views?.length ? 0 : 1.125 * (level + 1) + 'rem',
}}
className={'flex flex-1 cursor-pointer items-center gap-1.5 overflow-hidden'}
>
<div className={'icon'}>{icon?.value || <ViewIcon layout={layout} size={'small'} />}</div>
<div className={'flex-1 truncate'}>{name}</div>
@ -69,23 +75,20 @@ function OutlineItem({ view }: { view: PublishViewInfo }) {
);
};
const hasChildren = Boolean(view.child_views?.length);
return (
<div className={'flex h-fit w-full flex-col'}>
<div className={'flex h-fit flex-col'}>
{renderItem(view)}
<div
className={'flex transform flex-col gap-2 transition-all'}
style={{
height: isExpanded && view.child_views?.length ? 'auto' : 0,
opacity: isExpanded && view.child_views?.length ? 1 : 0,
marginLeft: hasChildren ? '1.125rem' : '2.25rem',
}}
>
{view.child_views
?.filter((view) => view.layout === ViewLayout.Document)
?.map((item, index) => (
<OutlineItem key={index} view={item} />
<OutlineItem level={level + 1} width={width} key={index} view={item} />
))}
</div>
</div>

View File

@ -30,7 +30,7 @@ export function OutlinePopover({
onMouseLeave={onMouseLeave}
className={'flex h-fit max-h-[500px] w-[268px] flex-col overflow-y-auto overflow-x-hidden p-2'}
>
<Outline viewMeta={viewMeta} />
<Outline width={268} viewMeta={viewMeta} />
<div
style={{
position: 'sticky',

View File

@ -61,7 +61,8 @@ export function ViewMetaPreview({ icon, cover, name }: ViewMetaProps) {
'flex items-center gap-4 px-16 text-[2.25rem] font-bold leading-[1.5em] max-md:px-4 max-sm:text-[7vw]'
}
>
<div className={`view-icon`}>{icon?.value}</div>
{icon?.value ? <div className={'view-icon'}>{icon?.value}</div> : null}
{name || <span className={'text-text-placeholder'}>{t('menuAppHeader.defaultNewPageName')}</span>}
</div>
</div>

View File

@ -0,0 +1,3 @@
export async function copyTextToClipboard(text: string) {
await navigator.clipboard.writeText(text);
}

View File

@ -15,6 +15,7 @@ interface Window {
error: (message: string) => void;
info: (message: string) => void;
clear: () => void;
default: (message: string) => void;
warning: (message: string) => void;
};
}

View File

@ -1378,6 +1378,9 @@
"imageUploadFailed": "Image upload failed",
"errorCode": "Error code"
},
"math": {
"copiedToPasteBoard": "The math equation has been copied to the clipboard"
},
"urlPreview": {
"copiedToPasteBoard": "The link has been copied to the clipboard",
"convertToLink": "Convert to embed link"
@ -2037,7 +2040,8 @@
"publish": {
"hasNotBeenPublished": "This page hasn't been published yet",
"reportPage": "Report page",
"databaseHasNotBeenPublished": "This database hasn't been published yet",
"createdWith": "Created with"
"databaseHasNotBeenPublished": "Publishing a database is not supported yet.",
"createdWith": "Created with",
"downloadApp": "Download AppFlowy"
}
}