mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: lazy load
This commit is contained in:
parent
38784b9d73
commit
d6e339158c
@ -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>
|
||||||
|
@ -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",
|
||||||
|
@ -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:
|
||||||
|
7
frontend/appflowy_web_app/src/assets/clock_alarm.svg
Normal file
7
frontend/appflowy_web_app/src/assets/clock_alarm.svg
Normal 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 |
@ -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}
|
||||||
|
@ -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'));
|
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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({
|
||||||
|
@ -3,39 +3,49 @@ 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(() => {
|
||||||
const path = ReactEditor.findPath(editor, node);
|
void (async () => {
|
||||||
let detectedLanguage = language;
|
const path = ReactEditor.findPath(editor, node);
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const prismLanguage = Prism.languages[detectedLanguage.toLowerCase()];
|
await promise;
|
||||||
|
|
||||||
if (!prismLanguage) {
|
detectedLanguage = window.hljs.highlightAuto(codeSnippet).language || 'plaintext';
|
||||||
const script = document.createElement('script');
|
}
|
||||||
|
|
||||||
script.src = `https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/prism-${detectedLanguage.toLowerCase()}.min.js`;
|
const prismLanguage = Prism.languages[detectedLanguage.toLowerCase()];
|
||||||
document.head.appendChild(script);
|
|
||||||
script.onload = () => {
|
if (!prismLanguage) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
|
||||||
|
script.src = `https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/prism-${detectedLanguage.toLowerCase()}.min.js`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
script.onload = () => {
|
||||||
|
addCodeGrammars?.(node.blockId, detectedLanguage);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
addCodeGrammars?.(node.blockId, detectedLanguage);
|
addCodeGrammars?.(node.blockId, detectedLanguage);
|
||||||
};
|
}
|
||||||
} else {
|
})();
|
||||||
addCodeGrammars?.(node.blockId, detectedLanguage);
|
|
||||||
}
|
|
||||||
}, [addCodeGrammars, editor, language, node]);
|
}, [addCodeGrammars, editor, language, node]);
|
||||||
|
|
||||||
const handleChangeLanguage = useCallback(
|
const handleChangeLanguage = useCallback(
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
import { lazy } from 'react';
|
export * from './MathEquation';
|
||||||
|
|
||||||
export const MathEquation = lazy(() => import('./MathEquation'));
|
|
||||||
|
@ -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'));
|
|
||||||
|
@ -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(() => {
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,15 +31,17 @@ export function CommentWrap({ commentId, isHovered, onHovered }: CommentWrapProp
|
|||||||
void (async () => {
|
void (async () => {
|
||||||
window.location.hash = '';
|
window.location.hash = '';
|
||||||
|
|
||||||
await smoothScrollIntoViewIfNeeded(element, {
|
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'center',
|
|
||||||
});
|
|
||||||
setHighLight(true);
|
|
||||||
|
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
setHighLight(false);
|
void smoothScrollIntoViewIfNeeded(element, {
|
||||||
}, 10000);
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
});
|
||||||
|
setHighLight(true);
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setHighLight(false);
|
||||||
|
}, 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>
|
||||||
|
@ -1,3 +1 @@
|
|||||||
import { lazy } from 'react';
|
export * from './GlobalCommentProvider';
|
||||||
|
|
||||||
export const GlobalCommentProvider = lazy(() => import('./GlobaclCommentProvider'));
|
|
||||||
|
@ -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={
|
||||||
|
@ -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'}>
|
||||||
|
@ -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'));
|
|
||||||
|
@ -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'}
|
||||||
>
|
>
|
||||||
|
@ -86,13 +86,13 @@ export function PublishView({ namespace, publishName }: PublishViewProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<CollabView doc={doc} />
|
<CollabView doc={doc} />
|
||||||
<Suspense fallback={<ComponentLoading />}>
|
{doc && (
|
||||||
<GlobalCommentProvider />
|
<Suspense fallback={<ComponentLoading />}>
|
||||||
</Suspense>
|
<GlobalCommentProvider />
|
||||||
|
</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>
|
||||||
);
|
);
|
||||||
|
@ -66,28 +66,26 @@ 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}
|
||||||
|
onMouseLeave={debounceClosePopover}
|
||||||
|
open={openPopover}
|
||||||
|
onClose={debounceClosePopover}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
className={'hidden'}
|
||||||
|
onClick={() => {
|
||||||
|
setOpenPopover(false);
|
||||||
|
onOpenDrawer();
|
||||||
|
}}
|
||||||
onMouseEnter={handleOpenPopover}
|
onMouseEnter={handleOpenPopover}
|
||||||
onMouseLeave={debounceClosePopover}
|
onMouseLeave={debounceClosePopover}
|
||||||
open={openPopover}
|
|
||||||
onClose={debounceClosePopover}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<SideOutlined className={'h-4 w-4'} />
|
||||||
className={'hidden'}
|
</IconButton>
|
||||||
onClick={() => {
|
</OutlinePopover>
|
||||||
setOpenPopover(false);
|
)}
|
||||||
onOpenDrawer();
|
|
||||||
}}
|
|
||||||
onMouseEnter={handleOpenPopover}
|
|
||||||
onMouseLeave={debounceClosePopover}
|
|
||||||
>
|
|
||||||
<SideOutlined className={'h-4 w-4'} />
|
|
||||||
</IconButton>
|
|
||||||
</OutlinePopover>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
|
|
||||||
<div className={'h-full flex-1 overflow-hidden'}>
|
<div className={'h-full flex-1 overflow-hidden'}>
|
||||||
<Breadcrumb crumbs={crumbs} />
|
<Breadcrumb crumbs={crumbs} />
|
||||||
|
@ -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();
|
||||||
|
@ -1,3 +1 @@
|
|||||||
import { lazy } from 'react';
|
export * from './Duplicate';
|
||||||
|
|
||||||
export const Duplicate = lazy(() => import('./Duplicate'));
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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'));
|
|
||||||
|
@ -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() {
|
||||||
@ -21,7 +22,7 @@ export function useViewMeta() {
|
|||||||
lineHeightLayout: extra?.lineHeightLayout,
|
lineHeightLayout: extra?.lineHeightLayout,
|
||||||
};
|
};
|
||||||
}, [extra]);
|
}, [extra]);
|
||||||
|
|
||||||
const layout = viewMeta?.layout;
|
const layout = viewMeta?.layout;
|
||||||
const style = useMemo(() => {
|
const style = useMemo(() => {
|
||||||
const fontSizeMap = {
|
const fontSizeMap = {
|
||||||
@ -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;
|
||||||
|
@ -7,10 +7,22 @@ export function getFontFamily(attribute: string) {
|
|||||||
return fontFamily;
|
return fontFamily;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.WebFont?.load({
|
void (async () => {
|
||||||
google: {
|
const script = document.createElement('script');
|
||||||
families: [fontFamily],
|
|
||||||
},
|
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({
|
||||||
|
google: {
|
||||||
|
families: [fontFamily],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
return fontFamily;
|
return fontFamily;
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user