mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-09-19 23:47:57 +03:00
feat(core): add global loading state (#4840)
This commit is contained in:
parent
1fe5a0fffa
commit
af7d331610
@ -0,0 +1,29 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const globalLoadingWrapperStyle = style({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: '100%',
|
||||
zIndex: 5,
|
||||
backgroundColor: 'var(--affine-background-modal-color)',
|
||||
opacity: 0,
|
||||
transition: 'opacity .3s',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'var(--affine-processing-color)',
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
zIndex: -1,
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
'&[data-loading="true"]': {
|
||||
right: 0,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
import { atom } from 'jotai';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export type GlobalLoadingEvent = {
|
||||
key?: string;
|
||||
};
|
||||
|
||||
const globalLoadingEventsBaseAtom = atom<GlobalLoadingEvent[]>([]);
|
||||
|
||||
export const globalLoadingEventsAtom = atom<GlobalLoadingEvent[]>(get =>
|
||||
get(globalLoadingEventsBaseAtom)
|
||||
);
|
||||
|
||||
export const resolveGlobalLoadingEventAtom = atom(
|
||||
null,
|
||||
(_, set, key: string) => {
|
||||
set(globalLoadingEventsBaseAtom, globalLoadingEvent =>
|
||||
globalLoadingEvent.filter(notification => notification.key !== key)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const pushGlobalLoadingEventAtom = atom<
|
||||
null,
|
||||
[GlobalLoadingEvent],
|
||||
void
|
||||
>(null, (_, set, newGlobalLoadingEvent) => {
|
||||
newGlobalLoadingEvent.key = newGlobalLoadingEvent.key || nanoid();
|
||||
|
||||
set(globalLoadingEventsBaseAtom, globalLoadingEvents => [
|
||||
// push to the top
|
||||
{ ...newGlobalLoadingEvent },
|
||||
...globalLoadingEvents,
|
||||
]);
|
||||
});
|
@ -0,0 +1,34 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { type ReactNode, useEffect, useState } from 'react';
|
||||
|
||||
import { Loading } from '../../ui/loading';
|
||||
import * as styles from './index.css';
|
||||
import { globalLoadingEventsAtom } from './index.jotai';
|
||||
|
||||
export {
|
||||
type GlobalLoadingEvent,
|
||||
pushGlobalLoadingEventAtom,
|
||||
resolveGlobalLoadingEventAtom,
|
||||
} from './index.jotai';
|
||||
|
||||
export function GlobalLoading(): ReactNode {
|
||||
const globalLoadingEvents = useAtomValue(globalLoadingEventsAtom);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (globalLoadingEvents.length) {
|
||||
setLoading(true);
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [globalLoadingEvents]);
|
||||
|
||||
if (!globalLoadingEvents.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.globalLoadingWrapperStyle} data-loading={loading}>
|
||||
<Loading size={20} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -3,6 +3,7 @@ import '@affine/component/theme/theme.css';
|
||||
import '@toeverything/components/style.css';
|
||||
|
||||
import { AffineContext } from '@affine/component/context';
|
||||
import { GlobalLoading } from '@affine/component/global-loading';
|
||||
import { NotificationCenter } from '@affine/component/notification-center';
|
||||
import { WorkspaceFallback } from '@affine/component/workspace';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
@ -62,6 +63,7 @@ export const App = memo(function App() {
|
||||
<AffineContext store={getCurrentStore()}>
|
||||
<CloudSessionProvider>
|
||||
<DebugProvider>
|
||||
<GlobalLoading />
|
||||
{runtimeConfig.enableNotificationCenter && <NotificationCenter />}
|
||||
<RouterProvider
|
||||
fallbackElement={<WorkspaceFallback key="RouterFallback" />}
|
||||
|
@ -1,9 +1,14 @@
|
||||
import {
|
||||
pushGlobalLoadingEventAtom,
|
||||
resolveGlobalLoadingEventAtom,
|
||||
} from '@affine/component/global-loading';
|
||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { PageBlockModel } from '@blocksuite/blocks';
|
||||
import { ContentParser } from '@blocksuite/blocks/content-parser';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
type ExportType = 'pdf' | 'html' | 'png' | 'markdown';
|
||||
@ -49,10 +54,16 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
|
||||
|
||||
export const useExportPage = (page: Page) => {
|
||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||
const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom);
|
||||
const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom);
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const onClickHandler = useCallback(
|
||||
async (type: ExportType) => {
|
||||
const globalLoadingID = nanoid();
|
||||
pushGlobalLoadingEvent({
|
||||
key: globalLoadingID,
|
||||
});
|
||||
try {
|
||||
await exportHandler({
|
||||
page,
|
||||
@ -70,9 +81,17 @@ export const useExportPage = (page: Page) => {
|
||||
message: t['com.affine.export.error.message'](),
|
||||
type: 'error',
|
||||
});
|
||||
} finally {
|
||||
resolveGlobalLoadingEvent(globalLoadingID);
|
||||
}
|
||||
},
|
||||
[page, pushNotification, t]
|
||||
[
|
||||
page,
|
||||
pushGlobalLoadingEvent,
|
||||
pushNotification,
|
||||
resolveGlobalLoadingEvent,
|
||||
t,
|
||||
]
|
||||
);
|
||||
|
||||
return onClickHandler;
|
||||
|
Loading…
Reference in New Issue
Block a user