Remove markdown editor, lazy load most of the components (#366)
This commit is contained in:
parent
bb84f85ef6
commit
808590e81e
File diff suppressed because it is too large
Load Diff
|
@ -37,18 +37,17 @@
|
|||
"@speedy-tuner/ini": "^0.2.2",
|
||||
"@speedy-tuner/types": "^0.2.1",
|
||||
"antd": "^4.18.2",
|
||||
"easymde": "^2.15.0",
|
||||
"firebase": "^9.6.1",
|
||||
"mlg-converter": "^0.5.1",
|
||||
"nanoid": "^3.1.30",
|
||||
"pako": "^2.0.4",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-markdown": "^7.1.2",
|
||||
"react-perfect-scrollbar": "^1.5.8",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-router-dom": "^5.2.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-simplemde-editor": "^5.0.2",
|
||||
"uplot": "^1.6.18",
|
||||
"uplot-react": "^1.1.1"
|
||||
},
|
||||
|
|
|
@ -154,3 +154,12 @@ select:-webkit-autofill:focus {
|
|||
.CodeMirror-wrap {
|
||||
background-color: @main-light !important;
|
||||
}
|
||||
|
||||
.markdown-preview {
|
||||
background-color: @main-light;
|
||||
padding: 5px 11px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: @border-radius-base;
|
||||
border-color: @main-dark;
|
||||
}
|
||||
|
|
80
src/App.tsx
80
src/App.tsx
|
@ -1,44 +1,46 @@
|
|||
|
||||
import {
|
||||
useLocation,
|
||||
Switch,
|
||||
Route,
|
||||
matchPath,
|
||||
Redirect,
|
||||
generatePath,
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
Layout,
|
||||
Result,
|
||||
Skeleton,
|
||||
} from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
lazy,
|
||||
ReactNode,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import {
|
||||
AppState,
|
||||
UIState,
|
||||
Config as ConfigType,
|
||||
} from '@speedy-tuner/types';
|
||||
import Dialog from './components/Dialog';
|
||||
import { loadAll } from './utils/api';
|
||||
import SideBar, { DialogMatchedPathType } from './components/SideBar';
|
||||
import TopBar from './components/TopBar';
|
||||
import StatusBar from './components/StatusBar';
|
||||
import { Routes } from './routes';
|
||||
import useStorage from './hooks/useStorage';
|
||||
import { loadAll } from './utils/api';
|
||||
import Log from './pages/Log';
|
||||
|
||||
import 'react-perfect-scrollbar/dist/css/styles.css';
|
||||
import './App.less';
|
||||
import { Routes } from './routes';
|
||||
import Log from './pages/Log';
|
||||
import Diagnose from './pages/Diagnose';
|
||||
import useStorage from './hooks/useStorage';
|
||||
import useConfig from './hooks/useConfig';
|
||||
import Login from './pages/auth/Login';
|
||||
import SignUp from './pages/auth/SignUp';
|
||||
import ResetPassword from './pages/auth/ResetPassword';
|
||||
import Upload from './pages/Upload';
|
||||
|
||||
// TODO: fix this
|
||||
// lazy loading this component causes a weird Curve canvas scaling
|
||||
// const Log = lazy(() => import('./pages/Log'));
|
||||
|
||||
const Tune = lazy(() => import('./pages/Tune'));
|
||||
const Diagnose = lazy(() => import('./pages/Diagnose'));
|
||||
const Login = lazy(() => import('./pages/auth/Login'));
|
||||
const SignUp = lazy(() => import('./pages/auth/SignUp'));
|
||||
const ResetPassword = lazy(() => import('./pages/auth/ResetPassword'));
|
||||
const Upload = lazy(() => import('./pages/Upload'));
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
@ -51,24 +53,8 @@ const mapStateToProps = (state: AppState) => ({
|
|||
const App = ({ ui, config }: { ui: UIState, config: ConfigType }) => {
|
||||
const margin = ui.sidebarCollapsed ? 80 : 250;
|
||||
// const [lastDialogPath, setLastDialogPath] = useState<string|null>();
|
||||
const { pathname } = useLocation();
|
||||
const { storageGetSync } = useStorage();
|
||||
const { isConfigReady } = useConfig(config);
|
||||
const lastDialogPath = storageGetSync('lastDialog');
|
||||
const dialogMatchedPath: DialogMatchedPathType = useMemo(() => matchPath(pathname, {
|
||||
path: Routes.DIALOG,
|
||||
exact: true,
|
||||
}) || { url: '', params: { category: '', dialog: '' } }, [pathname]);
|
||||
|
||||
const firstDialogPath = useMemo(() => {
|
||||
if (!isConfigReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const firstCategory = Object.keys(config.menus)[0];
|
||||
const firstDialog = Object.keys(config.menus[firstCategory].subMenus)[0];
|
||||
return generatePath(Routes.DIALOG, { category: firstCategory, dialog: firstDialog });
|
||||
}, [config.menus, isConfigReady]);
|
||||
|
||||
useEffect(() => {
|
||||
loadAll();
|
||||
|
@ -90,7 +76,16 @@ const App = ({ ui, config }: { ui: UIState, config: ConfigType }) => {
|
|||
<Layout style={{ marginLeft }}>
|
||||
<Layout className="app-content">
|
||||
<Content>
|
||||
{children}
|
||||
<Suspense fallback={<Skeleton
|
||||
active
|
||||
style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
padding: 20,
|
||||
}}
|
||||
/>}>
|
||||
{children}
|
||||
</Suspense>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
@ -106,20 +101,9 @@ const App = ({ ui, config }: { ui: UIState, config: ConfigType }) => {
|
|||
<Redirect to={lastDialogPath || Routes.TUNE} />
|
||||
</Route>
|
||||
<Route path={Routes.TUNE}>
|
||||
<Route path={Routes.TUNE} exact>
|
||||
{firstDialogPath && <Redirect to={lastDialogPath || firstDialogPath} />}
|
||||
</Route>
|
||||
<Layout style={{ marginLeft: margin }}>
|
||||
<SideBar matchedPath={dialogMatchedPath} />
|
||||
<Layout className="app-content">
|
||||
<Content>
|
||||
<Dialog
|
||||
name={dialogMatchedPath.params.dialog}
|
||||
url={dialogMatchedPath.url}
|
||||
/>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
<ContentFor marginLeft={margin}>
|
||||
<Tune />
|
||||
</ContentFor>
|
||||
</Route>
|
||||
<Route path={Routes.LOG}>
|
||||
<ContentFor marginLeft={margin}>
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
colorHsl,
|
||||
formatNumber,
|
||||
} from '../../utils/number';
|
||||
import LandscapeNotice from '../Dialog/LandscapeNotice';
|
||||
import LandscapeNotice from '../Tune/Dialog/LandscapeNotice';
|
||||
import { Colors } from '../../utils/colors';
|
||||
import touchZoomPlugin from '../../utils/uPlot/touchZoomPlugin';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Grid } from 'antd';
|
|||
import UplotReact from 'uplot-react';
|
||||
import uPlot from 'uplot';
|
||||
import touchZoomPlugin from '../../utils/uPlot/touchZoomPlugin';
|
||||
import LandscapeNotice from '../Dialog/LandscapeNotice';
|
||||
import LandscapeNotice from '../Tune/Dialog/LandscapeNotice';
|
||||
import { CompositeLogEntry } from '../../utils/logs/TriggerLogsParser';
|
||||
import { Colors } from '../../utils/colors';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Grid } from 'antd';
|
|||
import UplotReact from 'uplot-react';
|
||||
import uPlot from 'uplot';
|
||||
import touchZoomPlugin from '../../utils/uPlot/touchZoomPlugin';
|
||||
import LandscapeNotice from '../Dialog/LandscapeNotice';
|
||||
import LandscapeNotice from '../Tune/Dialog/LandscapeNotice';
|
||||
import {
|
||||
ToothLogEntry,
|
||||
EntryType,
|
||||
|
|
|
@ -36,11 +36,11 @@ import Curve from './Dialog/Curve';
|
|||
import {
|
||||
parseXy,
|
||||
parseZ,
|
||||
} from '../utils/tune/table';
|
||||
} from '../../utils/tune/table';
|
||||
import Map from './Dialog/Map';
|
||||
import { evaluateExpression } from '../utils/tune/expression';
|
||||
import useStorage from '../hooks/useStorage';
|
||||
import useConfig from '../hooks/useConfig';
|
||||
import { evaluateExpression } from '../../utils/tune/expression';
|
||||
import useStorage from '../../hooks/useStorage';
|
||||
import useConfig from '../../hooks/useConfig';
|
||||
|
||||
interface DialogsAndCurves {
|
||||
[name: string]: DialogType | CurveType | TableType,
|
||||
|
@ -358,7 +358,7 @@ const Dialog = ({
|
|||
if (isDataReady) {
|
||||
setPanelsComponents(generatePanelsComponents());
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isDataReady, url, ui.sidebarCollapsed]);
|
||||
|
||||
if (!isDataReady) {
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from 'react';
|
||||
import UplotReact from 'uplot-react';
|
||||
import uPlot from 'uplot';
|
||||
import { Colors } from '../../utils/colors';
|
||||
import { Colors } from '../../../utils/colors';
|
||||
import LandscapeNotice from './LandscapeNotice';
|
||||
import Table from './Curve/Table';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable react/no-array-index-key */
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { colorHsl } from '../../../utils/number';
|
||||
import { colorHsl } from '../../../../utils/number';
|
||||
|
||||
const titleProps = { disabled: true };
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { Grid } from 'antd';
|
||||
import LandscapeNotice from './LandscapeNotice';
|
||||
import { colorHsl } from '../../utils/number';
|
||||
import { colorHsl } from '../../../utils/number';
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
|
@ -121,10 +121,7 @@ const Log = ({
|
|||
'afr',
|
||||
'dwell',
|
||||
]);
|
||||
const {
|
||||
isConfigReady,
|
||||
findOutputChannel,
|
||||
} = useConfig(config);
|
||||
const { isConfigReady, findOutputChannel } = useConfig(config);
|
||||
const prepareSelectedFields = useCallback((selectedFields: CheckboxValueType[]) => {
|
||||
if (!isConfigReady) {
|
||||
return [];
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
useLocation,
|
||||
Route,
|
||||
matchPath,
|
||||
Redirect,
|
||||
generatePath,
|
||||
} from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
AppState,
|
||||
UIState,
|
||||
Config as ConfigType,
|
||||
} from '@speedy-tuner/types';
|
||||
import Dialog from '../components/Tune/Dialog';
|
||||
import SideBar, { DialogMatchedPathType } from '../components/SideBar';
|
||||
import { Routes } from '../routes';
|
||||
import useStorage from '../hooks/useStorage';
|
||||
import useConfig from '../hooks/useConfig';
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
ui: state.ui,
|
||||
status: state.status,
|
||||
config: state.config,
|
||||
});
|
||||
|
||||
const Tune = ({ ui, config }: { ui: UIState, config: ConfigType }) => {
|
||||
const { pathname } = useLocation();
|
||||
const { storageGetSync } = useStorage();
|
||||
const lastDialogPath = storageGetSync('lastDialog');
|
||||
const { isConfigReady } = useConfig(config);
|
||||
const dialogMatchedPath: DialogMatchedPathType = useMemo(() => matchPath(pathname, {
|
||||
path: Routes.DIALOG,
|
||||
exact: true,
|
||||
}) || { url: '', params: { category: '', dialog: '' } }, [pathname]);
|
||||
|
||||
const firstDialogPath = useMemo(() => {
|
||||
if (!isConfigReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const firstCategory = Object.keys(config.menus)[0];
|
||||
const firstDialog = Object.keys(config.menus[firstCategory].subMenus)[0];
|
||||
return generatePath(Routes.DIALOG, { category: firstCategory, dialog: firstDialog });
|
||||
}, [config.menus, isConfigReady]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Route path={Routes.TUNE} exact>
|
||||
{firstDialogPath && <Redirect to={lastDialogPath || firstDialogPath} />}
|
||||
</Route>
|
||||
<SideBar matchedPath={dialogMatchedPath} />
|
||||
<Dialog
|
||||
name={dialogMatchedPath.params.dialog}
|
||||
url={dialogMatchedPath.url}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(Tune);
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
|
@ -13,6 +12,7 @@ import {
|
|||
Skeleton,
|
||||
Space,
|
||||
Switch,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
Typography,
|
||||
Upload,
|
||||
|
@ -30,12 +30,12 @@ import { INI } from '@speedy-tuner/ini';
|
|||
import { UploadRequestOption } from 'rc-upload/lib/interface';
|
||||
import { UploadFile } from 'antd/lib/upload/interface';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import pako from 'pako';
|
||||
import {
|
||||
customAlphabet,
|
||||
nanoid,
|
||||
} from 'nanoid';
|
||||
import SimpleMdeReact from 'react-simplemde-editor';
|
||||
import {
|
||||
emailNotVerified,
|
||||
restrictedPage,
|
||||
|
@ -54,8 +54,6 @@ import {
|
|||
} from '../firebase';
|
||||
import useStorage from '../hooks/useStorage';
|
||||
import TuneParser from '../utils/tune/TuneParser';
|
||||
|
||||
import 'easymde/dist/easymde.min.css';
|
||||
import TriggerLogsParser from '../utils/logs/TriggerLogsParser';
|
||||
import LogParser from '../utils/logs/LogParser';
|
||||
|
||||
|
@ -124,7 +122,7 @@ const UploadPage = () => {
|
|||
const [isPublished, setIsPublished] = useState(false);
|
||||
const [isPublic, setIsPublic] = useState(true);
|
||||
const [isListed, setIsListed] = useState(true);
|
||||
const [description, setDescription] = useState('# My Tune \ndescription');
|
||||
const [description, setDescription] = useState('# My Tune\n\ndescription');
|
||||
const [tuneFile, setTuneFile] = useState<UploadedFile | null | false>(null);
|
||||
const [logFiles, setLogFiles] = useState<UploadedFile>({});
|
||||
const [toothLogFiles, setToothLogFiles] = useState<UploadedFile>({});
|
||||
|
@ -134,7 +132,7 @@ const UploadPage = () => {
|
|||
const history = useHistory();
|
||||
const { storageSet, storageGet, storageDelete } = useStorage();
|
||||
|
||||
const noop = () => {};
|
||||
const noop = () => { };
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
if (navigator.clipboard) {
|
||||
|
@ -146,11 +144,6 @@ const UploadPage = () => {
|
|||
|
||||
const genericError = (error: Error) => notification.error({ message: 'Error', description: error.message });
|
||||
|
||||
const editorOptions = useMemo(() => ({
|
||||
toolbar: false,
|
||||
autofocus: true,
|
||||
}), []);
|
||||
|
||||
const updateDbData = (tuneId: string, dbData: TuneDbData) => {
|
||||
try {
|
||||
return setDoc(fireStoreDoc(db, 'tunes', tuneId), dbData, { merge: true });
|
||||
|
@ -559,11 +552,24 @@ const UploadPage = () => {
|
|||
<Typography.Text type="secondary">(markdown)</Typography.Text>
|
||||
</Space>
|
||||
</Divider>
|
||||
<SimpleMdeReact
|
||||
onChange={setDescription}
|
||||
value={description}
|
||||
options={editorOptions}
|
||||
/>
|
||||
<Tabs defaultActiveKey="source">
|
||||
<Tabs.TabPane tab="Edit" key="source">
|
||||
<Input.TextArea
|
||||
rows={10}
|
||||
showCount
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
maxLength={3_000}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Preview" key="preview">
|
||||
<div className="markdown-preview" style={{ minHeight: 230 }}>
|
||||
<ReactMarkdown>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<Space style={{ marginTop: 30 }}>
|
||||
Show more:
|
||||
<Switch checked={showOptions} onChange={setShowOptions} />
|
||||
|
|
Loading…
Reference in New Issue