feat: refactor provider

This commit is contained in:
QiShaoXuan
2022-11-30 20:16:34 +08:00
parent 592ba156b8
commit 01bca1d082
18 changed files with 148 additions and 137 deletions
@@ -0,0 +1,46 @@
import type { EditorContainer } from '@blocksuite/editor';
import { createContext, useContext, useEffect, useState } from 'react';
import type { PropsWithChildren } from 'react';
import dynamic from 'next/dynamic';
import Loading from './loading';
const DynamicEditor = dynamic(() => import('./initial-editor'), {
ssr: false,
});
type EditorContextValue = {
editor: EditorContainer | null;
mode: EditorContainer['mode'];
setMode: (mode: EditorContainer['mode']) => void;
};
type EditorContextProps = PropsWithChildren<{}>;
export const EditorContext = createContext<EditorContextValue>({
editor: null,
mode: 'page',
setMode: () => {},
});
export const useEditor = () => useContext(EditorContext);
export const EditorProvider = ({
children,
}: PropsWithChildren<EditorContextProps>) => {
const [editor, setEditor] = useState<EditorContainer | null>(null);
const [mode, setMode] = useState<EditorContainer['mode']>('page');
useEffect(() => {
const event = new CustomEvent('affine.switch-mode', { detail: mode });
window.dispatchEvent(event);
}, [mode]);
return (
<EditorContext.Provider value={{ editor, mode, setMode }}>
<DynamicEditor setEditor={setEditor} />
{editor ? children : <Loading />}
</EditorContext.Provider>
);
};
export default EditorProvider;
@@ -0,0 +1,62 @@
export const exampleMarkdown = `The AFFiNE Alpha is here! You can also view our [Official Website](https://affine.pro/)!
**What's different in AFFiNE Alpha?**
1. A much ~smoother editing~ experience, with much ~greater stability~;
2. More complete ~Markdown~ support and improved ~keyboard shortcuts~;
3. New features such as ~dark mode~;
* **Switch between view styles using the** ☀ **and** 🌙.
4. ~Clean and modern UI/UX~ design.
**Looking for Markdown syntax or keyboard shortcuts?**
* Find the (?) in the bottom right, then the ️⌨️, to view a full list of \`Keyboard Shortcuts\`
**Have an enjoyable editing experience !!!** 😃
Have some feedback or just want to get in touch? Use the (?), then 🎧 to get in touch and join our communities.
**Still in development**
There are also some things you should consider about this AFFiNE Alpha including some ~limitations~:
* Single page editing - currently editing multiple docs/pages is not supported;
* Changes are not automatically stored, to save changes you should export your data. Options can be found by going to the top right and finding the \`\` icon;
* Without an import/open feature you are still able to access your data by copying it back in.
**Keyboard Shortcuts:**
1. Undo: \`⌘+Z\` or \`Ctrl+Z\`
2. Redo: \`⌘+⇧+Z\` or \`Ctrl+Shift+Z\`
3. Bold: \`⌘+B\` or \`Ctrl+B\`
4. Italic: \`⌘+I\` or \`Ctrl+I\`
5. Underline: \`⌘+U\` or \`Ctrl+U\`
6. Strikethrough: \`⌘+⇧+S\` or \`Ctrl+Shift+S\`
7. Inline code: \`⌘+E\` or \`Ctrl+E\`
8. Link: \`⌘+K\` or \`Ctrl+K\`
9. Body text: \`⌘+⌥+0\` or \`Ctrl+Shift+0\`
10. Heading 1: \`⌘+⌥+1\` or \`Ctrl+Shift+1\`
11. Heading 2: \`⌘+⌥+2\` or \`Ctrl+Shift+2\`
12. Heading 3: \`⌘+⌥+3\` or \`Ctrl+Shift+3\`
13. Heading 4: \`⌘+⌥+4\` or \`Ctrl+Shift+4\`
14. Heading 5: \`⌘+⌥+5\` or \`Ctrl+Shift+5\`
15. Heading 6: \`⌘+⌥+6\` or \`Ctrl+Shift+6\`
16. Increase indent: \`Tab\`
17. Reduce indent: \`⇧+Tab\` or \`Shift+Tab\`
**Markdown Syntax:**
1. Bold: \`**text**\`
2. Italic: \`*text*\`
3. Underline: \`~text~\`
4. Strikethrough: \`~~text~~\`
5. Inline code: \`\` \`text\` \`\`
6. Heading 1: \`# text\`
7. Heading 2: \`## text\`
8. Heading 3: \`### text\`
9. Heading 4: \`#### text\`
10. Heading 5: \`##### text\`
11. Heading 6: \`###### text\`
`;
export default exampleMarkdown;
@@ -0,0 +1,3 @@
export * from './editor-provider';
export * from './example-markdown';
export * from './utils';
@@ -0,0 +1,51 @@
import '@blocksuite/blocks';
import '@blocksuite/blocks/style';
import type { EditorContainer } from '@blocksuite/editor';
import { BlockSchema, createEditor } from '@blocksuite/editor';
import { useEffect } from 'react';
import pkg from '../../../package.json';
import { createWebsocketDocProvider, Store } from '@blocksuite/store';
const getEditorParams = () => {
const providers = [];
const params = new URLSearchParams(location.search);
if (params.get('syncModes') === 'websocket') {
const WebsocketDocProvider = createWebsocketDocProvider(
'ws://127.0.0.1:3000/collaboration/AFFiNE'
);
providers.push(WebsocketDocProvider);
}
return {
providers,
};
};
const InitialEditor = ({
setEditor,
}: {
setEditor: (editor: EditorContainer) => void;
}) => {
useEffect(() => {
const store = new Store({
...getEditorParams(),
});
const space = store.createSpace('page0').register(BlockSchema);
const editor = createEditor(space);
setEditor(editor);
return () => {
editor.remove();
};
}, [setEditor]);
useEffect(() => {
const version = pkg.dependencies['@blocksuite/editor'].substring(1);
console.log(`BlockSuite live demo ${version}`);
}, []);
return <div />;
};
export default InitialEditor;
@@ -0,0 +1,30 @@
import { styled } from '@/styles';
import CommonLoading from '@/components/loading';
const StyledLoadingContainer = styled('div')(({ theme }) => {
return {
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
color: '#6880FF',
h1: {
fontSize: '2em',
marginTop: '150px',
fontWeight: '600',
},
};
});
export const Loading = () => {
return (
<StyledLoadingContainer>
<div className="wrapper">
<CommonLoading />
<h1>Loading...</h1>
</div>
</StyledLoadingContainer>
);
};
export default Loading;
@@ -0,0 +1,13 @@
import { EditorContainer } from '@blocksuite/editor';
import exampleMarkdown from '@/providers/editor-provider/example-markdown';
export const initDefaultContent = (editor: EditorContainer) => {
const { space } = editor;
const pageId = space.addBlock({
flavour: 'affine:page',
title: 'Welcome to the AFFiNE Alpha',
});
const groupId = space.addBlock({ flavour: 'affine:group' }, pageId);
editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`);
space.resetHistory();
};
@@ -0,0 +1,53 @@
import { createContext, useContext, useState } from 'react';
import type { PropsWithChildren } from 'react';
import ShortcutsModal from '@/components/shortcuts-modal';
import ContactModal from '@/components/contact-modal';
type ModalContextValue = {
shortcutsModalHandler: (visible: boolean) => void;
contactModalHandler: (visible: boolean) => void;
};
type ModalContextProps = PropsWithChildren<{}>;
export const ModalContext = createContext<ModalContextValue>({
shortcutsModalHandler: () => {},
contactModalHandler: () => {},
});
export const useModal = () => useContext(ModalContext);
export const ModalProvider = ({
children,
}: PropsWithChildren<ModalContextProps>) => {
const [openContactModal, setOpenContactModal] = useState(false);
const [openShortcutsModal, setOpenShortcutsModal] = useState(false);
return (
<ModalContext.Provider
value={{
shortcutsModalHandler: visible => {
setOpenShortcutsModal(visible);
},
contactModalHandler: visible => {
setOpenContactModal(visible);
},
}}
>
<ContactModal
open={openContactModal}
onClose={() => {
setOpenContactModal(false);
}}
></ContactModal>
<ShortcutsModal
open={openShortcutsModal}
onClose={() => {
setOpenShortcutsModal(false);
}}
></ShortcutsModal>
{children}
</ModalContext.Provider>
);
};
export default ModalProvider;
@@ -0,0 +1,103 @@
import {
ThemeProvider as EmotionThemeProvider,
Global,
css,
} from '@emotion/react';
import { createContext, useContext, useEffect, useState } from 'react';
import type { PropsWithChildren } from 'react';
import {
Theme,
ThemeMode,
ThemeProviderProps,
ThemeProviderValue,
} from '@/styles/types';
import {
getLightTheme,
getDarkTheme,
globalThemeVariables,
} from '@/styles/theme';
import { SystemThemeHelper, localStorageThemeHelper } from '@/styles/utils';
import { useEditor } from '@/providers/editor-provider';
export const ThemeContext = createContext<ThemeProviderValue>({
mode: 'light',
changeMode: () => {},
theme: getLightTheme('page'),
});
export const useTheme = () => useContext(ThemeContext);
export const ThemeProvider = ({
defaultTheme = 'light',
children,
}: PropsWithChildren<ThemeProviderProps>) => {
const [theme, setTheme] = useState<Theme>(defaultTheme);
const [mode, setMode] = useState<ThemeMode>('auto');
const { mode: editorMode } = useEditor();
const themeStyle =
theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode);
const changeMode = (themeMode: ThemeMode) => {
themeMode !== mode && setMode(themeMode);
// Remember the theme mode which user selected for next time
localStorageThemeHelper.set(themeMode);
};
// ===================== A temporary solution, just use system theme and not remember the user selected ====================
useEffect(() => {
const systemThemeHelper = new SystemThemeHelper();
const systemTheme = systemThemeHelper.get();
setMode(systemTheme);
systemThemeHelper.onChange(() => {
setMode(systemThemeHelper.get());
});
}, []);
useEffect(() => {
setTheme(mode === 'auto' ? theme : mode);
}, [mode, setTheme, theme]);
// ===================== ====================
// useEffect(() => {
// setMode(localStorageThemeHelper.get() || 'auto');
// }, []);
//
// useEffect(() => {
// const systemThemeHelper = new SystemThemeHelper();
// const selectedThemeMode = localStorageThemeHelper.get();
//
// const themeMode = selectedThemeMode || mode;
// if (themeMode === 'auto') {
// setTheme(systemThemeHelper.get());
// } else {
// setTheme(themeMode);
// }
//
// // When system theme changed, change the theme mode
// systemThemeHelper.onChange(() => {
// // TODO: There may be should be provided a way to let user choose whether to
// if (mode === 'auto') {
// setTheme(systemThemeHelper.get());
// }
// });
//
// return () => {
// systemThemeHelper.dispose();
// };
// }, [mode]);
return (
<ThemeContext.Provider value={{ mode, changeMode, theme: themeStyle }}>
<Global
styles={css`
:root {
${globalThemeVariables(mode, themeStyle) as {}}
}
`}
/>
<EmotionThemeProvider theme={themeStyle}>{children}</EmotionThemeProvider>
</ThemeContext.Provider>
);
};
export default ThemeProvider;