fix: lazy load

This commit is contained in:
Kilu 2024-07-29 23:11:09 +08:00
parent 38784b9d73
commit d6e339158c
33 changed files with 171 additions and 134 deletions

View File

@ -31,7 +31,6 @@
<body id="body"> <body id="body">
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -64,8 +63,7 @@
} }
}); });
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
</body> </body>
</html> </html>

View File

@ -49,6 +49,7 @@
"emoji-regex": "^10.2.1", "emoji-regex": "^10.2.1",
"events": "^3.3.0", "events": "^3.3.0",
"google-protobuf": "^3.15.12", "google-protobuf": "^3.15.12",
"highlight.js": "^11.10.0",
"i18next": "^22.4.10", "i18next": "^22.4.10",
"i18next-browser-languagedetector": "^7.0.1", "i18next-browser-languagedetector": "^7.0.1",
"i18next-resources-to-backend": "^1.1.4", "i18next-resources-to-backend": "^1.1.4",

View File

@ -80,6 +80,9 @@ dependencies:
google-protobuf: google-protobuf:
specifier: ^3.15.12 specifier: ^3.15.12
version: 3.21.2 version: 3.21.2
highlight.js:
specifier: ^11.10.0
version: 11.10.0
i18next: i18next:
specifier: ^22.4.10 specifier: ^22.4.10
version: 22.5.1 version: 22.5.1
@ -7068,6 +7071,11 @@ packages:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
dev: true dev: true
/highlight.js@11.10.0:
resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==}
engines: {node: '>=12.0.0'}
dev: false
/hoist-non-react-statics@3.3.2: /hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies: dependencies:

View File

@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13Z"
stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 5V8L10 9" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 2.5L13.5 4.5" stroke="currentColor" stroke-linecap="round"/>
<path d="M4.5 2.5L2.5 4.5" stroke="currentColor" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 562 B

View File

@ -10,7 +10,7 @@ const defaultProps: Partial<PopoverComponentProps> = {
}, },
}; };
function Popover({ children, ...props }: PopoverComponentProps) { export function Popover({ children, ...props }: PopoverComponentProps) {
return ( return (
<PopoverComponent {...defaultProps} {...props}> <PopoverComponent {...defaultProps} {...props}>
{children} {children}

View File

@ -1,4 +1,2 @@
import { lazy } from 'react'; export * from './Popover';
export * from './RichTooltip';
export const RichTooltip = lazy(() => import('./RichTooltip'));
export const Popover = lazy(() => import('./Popover'));

View File

@ -2,8 +2,9 @@ import { clearData } from '@/application/db';
import { EventType, on } from '@/application/session'; import { EventType, on } from '@/application/session';
import { isTokenValid } from '@/application/session/token'; import { isTokenValid } from '@/application/session/token';
import { useAppLanguage } from '@/components/app/useAppLanguage'; import { useAppLanguage } from '@/components/app/useAppLanguage';
import { LoginModal } from '@/components/login';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import React, { createContext, useEffect, useState } from 'react'; import React, { createContext, useCallback, useEffect, useState } from 'react';
import { AFService, AFServiceConfig } from '@/application/services/services.type'; import { AFService, AFServiceConfig } from '@/application/services/services.type';
import { getService } from '@/application/services'; import { getService } from '@/application/services';
import { InfoSnackbarProps } from '@/components/_shared/notify'; import { InfoSnackbarProps } from '@/components/_shared/notify';
@ -26,6 +27,7 @@ export const AFConfigContext = createContext<
service: AFService | undefined; service: AFService | undefined;
isAuthenticated: boolean; isAuthenticated: boolean;
currentUser?: User; currentUser?: User;
openLoginModal: (redirectTo?: string) => void;
} }
| undefined | undefined
>(undefined); >(undefined);
@ -35,6 +37,13 @@ function AppConfig({ children }: { children: React.ReactNode }) {
const [service, setService] = useState<AFService>(); const [service, setService] = useState<AFService>();
const [isAuthenticated, setIsAuthenticated] = React.useState<boolean>(isTokenValid()); const [isAuthenticated, setIsAuthenticated] = React.useState<boolean>(isTokenValid());
const [currentUser, setCurrentUser] = React.useState<User>(); const [currentUser, setCurrentUser] = React.useState<User>();
const [loginOpen, setLoginOpen] = React.useState(false);
const [loginCompletedRedirectTo, setLoginCompletedRedirectTo] = React.useState<string>('');
const openLoginModal = useCallback((redirectTo?: string) => {
setLoginOpen(true);
setLoginCompletedRedirectTo(redirectTo || '');
}, []);
useEffect(() => { useEffect(() => {
return on(EventType.SESSION_VALID, () => { return on(EventType.SESSION_VALID, () => {
@ -135,9 +144,19 @@ function AppConfig({ children }: { children: React.ReactNode }) {
service, service,
isAuthenticated, isAuthenticated,
currentUser, currentUser,
openLoginModal,
}} }}
> >
{children} {children}
{loginOpen && (
<LoginModal
redirectTo={loginCompletedRedirectTo}
open={loginOpen}
onClose={() => {
setLoginOpen(false);
}}
/>
)}
</AFConfigContext.Provider> </AFConfigContext.Provider>
); );
} }

View File

@ -14,9 +14,6 @@ export function Calendar() {
toolbar: (props) => <Toolbar {...props} emptyEvents={emptyEvents} />, toolbar: (props) => <Toolbar {...props} emptyEvents={emptyEvents} />,
eventWrapper: Event, eventWrapper: Event,
}} }}
style={{
marginBottom: '24px',
}}
events={events} events={events}
views={['month']} views={['month']}
localizer={localizer} localizer={localizer}

View File

@ -30,6 +30,7 @@ $today-highlight-bg: transparent;
.rbc-month-view { .rbc-month-view {
border: none; border: none;
.rbc-month-row { .rbc-month-row {
border: 1px solid var(--line-divider); border: 1px solid var(--line-divider);
border-top: none; border-top: none;

View File

@ -2,7 +2,7 @@ import { FieldType } from '@/application/database-yjs';
import { useDateTypeCellDispatcher } from '@/components/database/components/cell/Cell.hooks'; import { useDateTypeCellDispatcher } from '@/components/database/components/cell/Cell.hooks';
import { CellProps, DateTimeCell as DateTimeCellType } from '@/application/database-yjs/cell.type'; import { CellProps, DateTimeCell as DateTimeCellType } from '@/application/database-yjs/cell.type';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { ReactComponent as ReminderSvg } from '$icons/16x/clock_alarm.svg'; import { ReactComponent as ReminderSvg } from '@/assets/clock_alarm.svg';
export function DateTimeCell({ cell, fieldId, style, placeholder }: CellProps<DateTimeCellType>) { export function DateTimeCell({ cell, fieldId, style, placeholder }: CellProps<DateTimeCellType>) {
const { getDateTimeStr } = useDateTypeCellDispatcher(fieldId); const { getDateTimeStr } = useDateTypeCellDispatcher(fieldId);

View File

@ -4,7 +4,7 @@ import Cell from '@/components/database/components/cell/Cell';
import React, { CSSProperties, useMemo } from 'react'; import React, { CSSProperties, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
function CardField({ rowId, fieldId }: { rowId: string; fieldId: string; index: number }) { export function CardField({ rowId, fieldId }: { rowId: string; fieldId: string; index: number }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { field } = useFieldSelector(fieldId); const { field } = useFieldSelector(fieldId);
const cell = useCellSelector({ const cell = useCellSelector({

View File

@ -3,24 +3,33 @@ import { useEditorContext } from '@/components/editor/EditorContext';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { ReactEditor, useSlateStatic } from 'slate-react'; import { ReactEditor, useSlateStatic } from 'slate-react';
import { Element as SlateElement, Transforms } from 'slate'; import { Element as SlateElement, Transforms } from 'slate';
import Prism from 'prismjs';
const Prism = window.Prism;
const hljs = window.hljs;
export function useCodeBlock(node: CodeNode) { export function useCodeBlock(node: CodeNode) {
const language = node.data.language; const language = node.data.language;
const editor = useSlateStatic() as ReactEditor; const editor = useSlateStatic() as ReactEditor;
const addCodeGrammars = useEditorContext().addCodeGrammars; const addCodeGrammars = useEditorContext().addCodeGrammars;
useEffect(() => { useEffect(() => {
void (async () => {
const path = ReactEditor.findPath(editor, node); const path = ReactEditor.findPath(editor, node);
let detectedLanguage = language; let detectedLanguage = language;
if (!language) { if (!language) {
const codeSnippet = editor.string(path); const codeSnippet = editor.string(path);
const script = document.createElement('script');
detectedLanguage = hljs.highlightAuto(codeSnippet).language; script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js';
document.body.appendChild(script);
const promise = new Promise((resolve) => {
script.onload = () => {
resolve(true);
};
});
await promise;
detectedLanguage = window.hljs.highlightAuto(codeSnippet).language || 'plaintext';
} }
const prismLanguage = Prism.languages[detectedLanguage.toLowerCase()]; const prismLanguage = Prism.languages[detectedLanguage.toLowerCase()];
@ -29,13 +38,14 @@ export function useCodeBlock(node: CodeNode) {
const script = document.createElement('script'); const script = document.createElement('script');
script.src = `https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/prism-${detectedLanguage.toLowerCase()}.min.js`; script.src = `https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/prism-${detectedLanguage.toLowerCase()}.min.js`;
document.head.appendChild(script); document.body.appendChild(script);
script.onload = () => { script.onload = () => {
addCodeGrammars?.(node.blockId, detectedLanguage); addCodeGrammars?.(node.blockId, detectedLanguage);
}; };
} else { } else {
addCodeGrammars?.(node.blockId, detectedLanguage); addCodeGrammars?.(node.blockId, detectedLanguage);
} }
})();
}, [addCodeGrammars, editor, language, node]); }, [addCodeGrammars, editor, language, node]);
const handleChangeLanguage = useCallback( const handleChangeLanguage = useCallback(

View File

@ -1,8 +1,5 @@
import { BaseRange, NodeEntry, Text, Path } from 'slate'; import { BaseRange, NodeEntry, Text, Path } from 'slate';
import Prism, { Grammar } from 'prismjs';
const Prism = window.Prism;
Prism.plugins.autoloader.languages_path = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/';
const push_string = ( const push_string = (
token: string | Prism.Token, token: string | Prism.Token,
@ -86,7 +83,7 @@ export const decorateCode = ([node, path]: NodeEntry, language: string) => {
return ranges; return ranges;
} }
const highlightCode = (code: string, language: string) => { const highlightCode = (code: string, language: Grammar) => {
try { try {
const tokens = Prism.tokenize(code, language); const tokens = Prism.tokenize(code, language);

View File

@ -1,3 +1 @@
import { lazy } from 'react'; export * from './MathEquation';
export const MathEquation = lazy(() => import('./MathEquation'));

View File

@ -1,5 +1 @@
import { lazy } from 'react'; export * from './Formula';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
export const Formula = lazy(() => import('./Formula?chunkName=formula'));

View File

@ -1,7 +1,7 @@
import { renderDate } from '@/utils/time'; import { renderDate } from '@/utils/time';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { ReactComponent as DateSvg } from '@/assets/date.svg'; import { ReactComponent as DateSvg } from '@/assets/date.svg';
import { ReactComponent as ReminderSvg } from '$icons/16x/clock_alarm.svg'; import { ReactComponent as ReminderSvg } from '@/assets/clock_alarm.svg';
function MentionDate({ date, reminder }: { date: string; reminder?: { id: string; option: string } }) { function MentionDate({ date, reminder }: { date: string; reminder?: { id: string; option: string } }) {
const dateFormat = useMemo(() => { const dateFormat = useMemo(() => {

View File

@ -3,7 +3,6 @@ import { notify } from '@/components/_shared/notify';
import { AFConfigContext } from '@/components/app/AppConfig'; import { AFConfigContext } from '@/components/app/AppConfig';
import { useGlobalCommentContext } from '@/components/global-comment/GlobalComment.hooks'; import { useGlobalCommentContext } from '@/components/global-comment/GlobalComment.hooks';
import ReplyComment from '@/components/global-comment/ReplyComment'; import ReplyComment from '@/components/global-comment/ReplyComment';
import { LoginModal } from '@/components/login';
import { Button, TextareaAutosize } from '@mui/material'; import { Button, TextareaAutosize } from '@mui/material';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import React, { memo, useCallback, useContext, useEffect, useRef } from 'react'; import React, { memo, useCallback, useContext, useEffect, useRef } from 'react';
@ -16,24 +15,19 @@ function AddComment() {
const { t } = useTranslation(); const { t } = useTranslation();
const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated; const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated;
const openLoginModal = useContext(AFConfigContext)?.openLoginModal;
const createCommentOnPublishView = useContext(AFConfigContext)?.service?.createCommentOnPublishView; const createCommentOnPublishView = useContext(AFConfigContext)?.service?.createCommentOnPublishView;
const viewId = useContext(PublishContext)?.viewMeta?.view_id; const viewId = useContext(PublishContext)?.viewMeta?.view_id;
const [content, setContent] = React.useState(''); const [content, setContent] = React.useState('');
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const [loginOpen, setLoginOpen] = React.useState(false);
const [focus, setFocus] = React.useState(false); const [focus, setFocus] = React.useState(false);
const url = window.location.href + '#addComment'; const url = window.location.href + '#addComment';
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null); const inputRef = useRef<HTMLTextAreaElement>(null);
const handleOnFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => { const handleOnFocus = () => {
setFocus(true); setFocus(true);
if (!isAuthenticated) {
e.preventDefault();
setLoginOpen(true);
}
}; };
useEffect(() => { useEffect(() => {
@ -118,6 +112,12 @@ function AddComment() {
ref={inputRef} ref={inputRef}
autoComplete={'off'} autoComplete={'off'}
spellCheck={false} spellCheck={false}
onMouseDown={() => {
if (!isAuthenticated && openLoginModal) {
openLoginModal(url);
}
}}
readOnly={!isAuthenticated}
onFocus={handleOnFocus} onFocus={handleOnFocus}
onBlur={() => setFocus(false)} onBlur={() => setFocus(false)}
value={content} value={content}
@ -128,8 +128,9 @@ function AddComment() {
placeholder={t('globalComment.addComment')} placeholder={t('globalComment.addComment')}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) { if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) {
if (!content) return;
e.preventDefault(); e.preventDefault();
if (!content) return;
void handleSubmit(); void handleSubmit();
} }
@ -167,15 +168,6 @@ function AddComment() {
</Button> </Button>
</div> </div>
)} )}
{loginOpen && (
<LoginModal
redirectTo={url}
open={loginOpen}
onClose={() => {
setLoginOpen(false);
}}
/>
)}
</div> </div>
); );
} }

View File

@ -1,8 +1,9 @@
import { AFConfigContext } from '@/components/app/AppConfig';
import CommentActions from '@/components/global-comment/actions/CommentActions'; import CommentActions from '@/components/global-comment/actions/CommentActions';
import Comment from './Comment'; import Comment from './Comment';
import { useGlobalCommentContext } from '@/components/global-comment/GlobalComment.hooks'; import { useGlobalCommentContext } from '@/components/global-comment/GlobalComment.hooks';
import ReplyComment from '@/components/global-comment/ReplyComment'; import ReplyComment from '@/components/global-comment/ReplyComment';
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useCallback, useContext, useEffect, useMemo } from 'react';
import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'; import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed';
export interface CommentWrapProps { export interface CommentWrapProps {
@ -16,6 +17,7 @@ export function CommentWrap({ commentId, isHovered, onHovered }: CommentWrapProp
const comment = useMemo(() => getComment(commentId), [commentId, getComment]); const comment = useMemo(() => getComment(commentId), [commentId, getComment]);
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
const [highLight, setHighLight] = React.useState(false); const [highLight, setHighLight] = React.useState(false);
const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated;
useEffect(() => { useEffect(() => {
const hashHasComment = window.location.hash.includes(`#comment-${commentId}`); const hashHasComment = window.location.hash.includes(`#comment-${commentId}`);
@ -29,7 +31,8 @@ export function CommentWrap({ commentId, isHovered, onHovered }: CommentWrapProp
void (async () => { void (async () => {
window.location.hash = ''; window.location.hash = '';
await smoothScrollIntoViewIfNeeded(element, { timeout = setTimeout(() => {
void smoothScrollIntoViewIfNeeded(element, {
behavior: 'smooth', behavior: 'smooth',
block: 'center', block: 'center',
}); });
@ -38,6 +41,7 @@ export function CommentWrap({ commentId, isHovered, onHovered }: CommentWrapProp
timeout = setTimeout(() => { timeout = setTimeout(() => {
setHighLight(false); setHighLight(false);
}, 10000); }, 10000);
}, 500);
})(); })();
return () => { return () => {
@ -70,7 +74,7 @@ export function CommentWrap({ commentId, isHovered, onHovered }: CommentWrapProp
}} }}
> >
<Comment comment={comment} /> <Comment comment={comment} />
{isHovered && !comment.isDeleted && ( {isHovered && isAuthenticated && !comment.isDeleted && (
<div className={'absolute right-2 top-2'}> <div className={'absolute right-2 top-2'}>
<CommentActions comment={comment} /> <CommentActions comment={comment} />
</div> </div>

View File

@ -1,3 +1 @@
import { lazy } from 'react'; export * from './GlobalCommentProvider';
export const GlobalCommentProvider = lazy(() => import('./GlobaclCommentProvider'));

View File

@ -6,7 +6,9 @@ import { useTranslation } from 'react-i18next';
function Reaction({ reaction, onClick }: { reaction: ReactionType; onClick: (reaction: ReactionType) => void }) { function Reaction({ reaction, onClick }: { reaction: ReactionType; onClick: (reaction: ReactionType) => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated;
const openLoginModal = useContext(AFConfigContext)?.openLoginModal;
const url = window.location.href + '#comment-' + reaction.commentId;
const reactCount = useMemo(() => { const reactCount = useMemo(() => {
return reaction.reactUsers.length; return reaction.reactUsers.length;
}, [reaction.reactUsers]); }, [reaction.reactUsers]);
@ -64,7 +66,14 @@ function Reaction({ reaction, onClick }: { reaction: ReactionType; onClick: (rea
> >
<div <div
style={style} style={style}
onClick={() => onClick(reaction)} onClick={() => {
if (!isAuthenticated && openLoginModal) {
openLoginModal(url);
return;
}
onClick(reaction);
}}
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)} onMouseLeave={() => setHover(false)}
className={ className={

View File

@ -3,7 +3,7 @@ import { Dialog, IconButton } from '@mui/material';
import { Login } from './Login'; import { Login } from './Login';
import { ReactComponent as CloseIcon } from '@/assets/close.svg'; import { ReactComponent as CloseIcon } from '@/assets/close.svg';
function LoginModal({ redirectTo, open, onClose }: { redirectTo: string; open: boolean; onClose: () => void }) { export function LoginModal({ redirectTo, open, onClose }: { redirectTo: string; open: boolean; onClose: () => void }) {
return ( return (
<Dialog open={open} onClose={onClose}> <Dialog open={open} onClose={onClose}>
<div className={'relative px-6'}> <div className={'relative px-6'}>

View File

@ -1,5 +1,3 @@
import { lazy } from 'react'; export * from './LoginModal';
export const Login = lazy(() => import('./Login')); export * from './Login';
export const LoginModal = lazy(() => import('./LoginModal'));

View File

@ -45,7 +45,7 @@ function DatabaseView({ viewMeta, ...props }: DatabaseProps) {
return ( return (
<div <div
style={{ style={{
height: 'calc(100vh - 48px)', minHeight: 'calc(100vh - 48px)',
}} }}
className={'relative flex h-full w-full flex-col px-16 max-md:px-4'} className={'relative flex h-full w-full flex-col px-16 max-md:px-4'}
> >

View File

@ -86,13 +86,13 @@ export function PublishView({ namespace, publishName }: PublishViewProps) {
/> />
<CollabView doc={doc} /> <CollabView doc={doc} />
{doc && (
<Suspense fallback={<ComponentLoading />}> <Suspense fallback={<ComponentLoading />}>
<GlobalCommentProvider /> <GlobalCommentProvider />
</Suspense> </Suspense>
)}
</AFScroller> </AFScroller>
<Suspense fallback={null}>
{open && <OutlineDrawer width={drawerWidth} open={open} onClose={() => setOpen(false)} />} {open && <OutlineDrawer width={drawerWidth} open={open} onClose={() => setOpen(false)} />}
</Suspense>
</div> </div>
</PublishProvider> </PublishProvider>
); );

View File

@ -66,7 +66,6 @@ export function PublishViewHeader({ onOpenDrawer, openDrawer }: { onOpenDrawer:
className={'appflowy-top-bar sticky top-0 z-10 flex px-5'} className={'appflowy-top-bar sticky top-0 z-10 flex px-5'}
> >
<div className={'flex w-full items-center justify-between gap-2 overflow-hidden'}> <div className={'flex w-full items-center justify-between gap-2 overflow-hidden'}>
<Suspense fallback={null}>
{!openDrawer && openPopover && ( {!openDrawer && openPopover && (
<OutlinePopover <OutlinePopover
onMouseEnter={handleOpenPopover} onMouseEnter={handleOpenPopover}
@ -87,7 +86,6 @@ export function PublishViewHeader({ onOpenDrawer, openDrawer }: { onOpenDrawer:
</IconButton> </IconButton>
</OutlinePopover> </OutlinePopover>
)} )}
</Suspense>
<div className={'h-full flex-1 overflow-hidden'}> <div className={'h-full flex-1 overflow-hidden'}>
<Breadcrumb crumbs={crumbs} /> <Breadcrumb crumbs={crumbs} />

View File

@ -6,7 +6,7 @@ import { useSearchParams } from 'react-router-dom';
import { useDuplicate } from '@/components/publish/header/duplicate/useDuplicate'; import { useDuplicate } from '@/components/publish/header/duplicate/useDuplicate';
import DuplicateModal from '@/components/publish/header/duplicate/DuplicateModal'; import DuplicateModal from '@/components/publish/header/duplicate/DuplicateModal';
function Duplicate() { export function Duplicate() {
const { t } = useTranslation(); const { t } = useTranslation();
const { loginOpen, duplicateOpen, handleDuplicateClose, handleLoginClose, url } = useDuplicate(); const { loginOpen, duplicateOpen, handleDuplicateClose, handleLoginClose, url } = useDuplicate();
const [search, setSearch] = useSearchParams(); const [search, setSearch] = useSearchParams();

View File

@ -1,3 +1 @@
import { lazy } from 'react'; export * from './Duplicate';
export const Duplicate = lazy(() => import('./Duplicate'));

View File

@ -7,7 +7,7 @@ import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys';
import { Drawer, IconButton, Tooltip } from '@mui/material'; import { Drawer, IconButton, Tooltip } from '@mui/material';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
function OutlineDrawer({ open, width, onClose }: { open: boolean; width: number; onClose: () => void }) { export function OutlineDrawer({ open, width, onClose }: { open: boolean; width: number; onClose: () => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const viewMeta = usePublishContext()?.viewMeta; const viewMeta = usePublishContext()?.viewMeta;

View File

@ -1,4 +1,2 @@
import { lazy } from 'react'; export * from './OutlinePopover';
export * from './OutlineDrawer';
export const OutlineDrawer = lazy(() => import('./OutlineDrawer'));
export const OutlinePopover = lazy(() => import('./OutlinePopover'));

View File

@ -1,6 +1,7 @@
import { usePublishContext } from '@/application/publish'; import { usePublishContext } from '@/application/publish';
import { EditorLayoutStyle } from '@/components/editor/EditorContext'; import { EditorLayoutStyle } from '@/components/editor/EditorContext';
import { ViewMetaCover } from '@/components/view-meta'; import { ViewMetaCover } from '@/components/view-meta';
import { getFontFamily } from '@/utils/font';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
export function useViewMeta() { export function useViewMeta() {
@ -56,11 +57,7 @@ export function useViewMeta() {
useEffect(() => { useEffect(() => {
if (!layoutStyle.font) return; if (!layoutStyle.font) return;
void window.WebFont?.load({ void getFontFamily(layoutStyle.font);
google: {
families: [layoutStyle.font],
},
});
}, [layoutStyle.font]); }, [layoutStyle.font]);
const icon = viewMeta?.icon || undefined; const icon = viewMeta?.icon || undefined;

View File

@ -7,10 +7,22 @@ export function getFontFamily(attribute: string) {
return fontFamily; return fontFamily;
} }
void (async () => {
const script = document.createElement('script');
script.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';
document.body.appendChild(script);
await new Promise((resolve) => {
script.onload = () => {
resolve(true);
};
});
window.WebFont?.load({ window.WebFont?.load({
google: { google: {
families: [fontFamily], families: [fontFamily],
}, },
}); });
})();
return fontFamily; return fontFamily;
} }

View File

@ -114,7 +114,10 @@ export default defineConfig({
id.includes('/y-indexeddb@') || id.includes('/y-indexeddb@') ||
id.includes('/dexie') || id.includes('/dexie') ||
id.includes('/redux') || id.includes('/redux') ||
id.includes('/react-custom-scrollbars') id.includes('/react-custom-scrollbars') ||
id.includes('/dayjs') ||
id.includes('/smooth-scroll-into-view-if-needed') ||
id.includes('/react-virtualized-auto-sizer')
) { ) {
return 'common'; return 'common';
} }