Fix tune loading and routing (#378)
This commit is contained in:
parent
affdae49e8
commit
c53cd0dcc3
27
src/App.tsx
27
src/App.tsx
|
@ -18,7 +18,6 @@ import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import useBrowserStorage from './hooks/useBrowserStorage';
|
|
||||||
import TopBar from './components/TopBar';
|
import TopBar from './components/TopBar';
|
||||||
import StatusBar from './components/StatusBar';
|
import StatusBar from './components/StatusBar';
|
||||||
import { Routes } from './routes';
|
import { Routes } from './routes';
|
||||||
|
@ -35,6 +34,7 @@ import {
|
||||||
} from './types/state';
|
} from './types/state';
|
||||||
import useDb from './hooks/useDb';
|
import useDb from './hooks/useDb';
|
||||||
import useServerStorage from './hooks/useServerStorage';
|
import useServerStorage from './hooks/useServerStorage';
|
||||||
|
import Info from './pages/Info';
|
||||||
|
|
||||||
// TODO: fix this
|
// TODO: fix this
|
||||||
// lazy loading this component causes a weird Curve canvas scaling
|
// lazy loading this component causes a weird Curve canvas scaling
|
||||||
|
@ -59,7 +59,6 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
||||||
const margin = ui.sidebarCollapsed ? 80 : 250;
|
const margin = ui.sidebarCollapsed ? 80 : 250;
|
||||||
const { getTune } = useDb();
|
const { getTune } = useDb();
|
||||||
const { getFile } = useServerStorage();
|
const { getFile } = useServerStorage();
|
||||||
const { storageSet } = useBrowserStorage();
|
|
||||||
|
|
||||||
// const [lastDialogPath, setLastDialogPath] = useState<string|null>();
|
// const [lastDialogPath, setLastDialogPath] = useState<string|null>();
|
||||||
// const lastDialogPath = storageGetSync('lastDialog');
|
// const lastDialogPath = storageGetSync('lastDialog');
|
||||||
|
@ -69,21 +68,21 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
||||||
path: Routes.TUNE_ROOT,
|
path: Routes.TUNE_ROOT,
|
||||||
}), [pathname]);
|
}), [pathname]);
|
||||||
|
|
||||||
useEffect(() => {
|
const tuneId = (matchedTunePath?.params as any)?.tuneId;
|
||||||
const tuneId = (matchedTunePath?.params as any)?.tuneId;
|
|
||||||
if (tuneId) {
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tuneId) {
|
||||||
getTune(tuneId).then(async (tuneData) => {
|
getTune(tuneId).then(async (tuneData) => {
|
||||||
const [tuneRaw, iniRaw] = await Promise.all([
|
const [tuneRaw, iniRaw] = await Promise.all([
|
||||||
await getFile(tuneData.tuneFile!),
|
getFile(tuneData.tuneFile!),
|
||||||
await getFile(tuneData.customIniFile!),
|
getFile(tuneData.customIniFile!),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
store.dispatch({ type: 'tuneData/load', payload: tuneData });
|
||||||
|
|
||||||
loadTune(tuneRaw, iniRaw);
|
loadTune(tuneRaw, iniRaw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
storageSet('lastTuneId', tuneId);
|
|
||||||
store.dispatch({ type: 'navigation/tuneId', payload: tuneId });
|
store.dispatch({ type: 'navigation/tuneId', payload: tuneId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +95,7 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [tuneId]);
|
||||||
|
|
||||||
const ContentFor = useCallback((props: { children: ReactNode, marginLeft?: number }) => {
|
const ContentFor = useCallback((props: { children: ReactNode, marginLeft?: number }) => {
|
||||||
const { children, marginLeft } = props;
|
const { children, marginLeft } = props;
|
||||||
|
@ -127,9 +126,6 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
||||||
<TopBar tuneId={navigation.tuneId} />
|
<TopBar tuneId={navigation.tuneId} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={Routes.ROOT} exact>
|
<Route path={Routes.ROOT} exact>
|
||||||
{/* <Route path={Routes.ROOT} exact>
|
|
||||||
<Redirect to={lastDialogPath || Routes.TUNE_ROOT} />
|
|
||||||
</Route> */}
|
|
||||||
<ContentFor>
|
<ContentFor>
|
||||||
<Result
|
<Result
|
||||||
status="info"
|
status="info"
|
||||||
|
@ -138,6 +134,11 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
||||||
/>
|
/>
|
||||||
</ContentFor>
|
</ContentFor>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path={Routes.TUNE_ROOT} exact>
|
||||||
|
<ContentFor>
|
||||||
|
<Info />
|
||||||
|
</ContentFor>
|
||||||
|
</Route>
|
||||||
<Route path={Routes.TUNE_TUNE}>
|
<Route path={Routes.TUNE_TUNE}>
|
||||||
<ContentFor marginLeft={margin}>
|
<ContentFor marginLeft={margin}>
|
||||||
<Tune />
|
<Tune />
|
||||||
|
|
|
@ -86,7 +86,7 @@ const SideBar = ({
|
||||||
const { isConfigReady } = useConfig(config);
|
const { isConfigReady } = useConfig(config);
|
||||||
const checkCondition = useCallback((condition: string) => evaluateExpression(condition, tune.constants, config), [tune.constants, config]);
|
const checkCondition = useCallback((condition: string) => evaluateExpression(condition, tune.constants, config), [tune.constants, config]);
|
||||||
const buildUrl = useCallback((main: string, sub: string) => generatePath(Routes.TUNE_DIALOG, {
|
const buildUrl = useCallback((main: string, sub: string) => generatePath(Routes.TUNE_DIALOG, {
|
||||||
tuneId: navigation.tuneId || 'not-ready',
|
tuneId: navigation.tuneId!,
|
||||||
category: main,
|
category: main,
|
||||||
dialog: sub,
|
dialog: sub,
|
||||||
}), [navigation.tuneId]);
|
}), [navigation.tuneId]);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import {
|
||||||
FundOutlined,
|
FundOutlined,
|
||||||
UserAddOutlined,
|
UserAddOutlined,
|
||||||
LogoutOutlined,
|
LogoutOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -69,6 +70,9 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
||||||
const { currentUser, logout } = useAuth();
|
const { currentUser, logout } = useAuth();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const buildTuneUrl = (route: string) => tuneId ? generatePath(route, { tuneId }) : null;
|
const buildTuneUrl = (route: string) => tuneId ? generatePath(route, { tuneId }) : null;
|
||||||
|
const matchedTuneRootPath = useMemo(() => matchPath(pathname, {
|
||||||
|
path: Routes.TUNE_ROOT,
|
||||||
|
}), [pathname]);
|
||||||
const matchedTabPath = useMemo(() => matchPath(pathname, {
|
const matchedTabPath = useMemo(() => matchPath(pathname, {
|
||||||
path: Routes.TUNE_TAB,
|
path: Routes.TUNE_TAB,
|
||||||
}), [pathname]);
|
}), [pathname]);
|
||||||
|
@ -106,11 +110,17 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
||||||
<Col span={10} md={10} sm={16} style={{ textAlign: 'center' }}>
|
<Col span={10} md={10} sm={16} style={{ textAlign: 'center' }}>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
key={pathname}
|
key={pathname}
|
||||||
defaultValue={matchedTabPath?.url}
|
defaultValue={matchedTabPath?.url || matchedTuneRootPath?.url}
|
||||||
optionType="button"
|
optionType="button"
|
||||||
buttonStyle="solid"
|
buttonStyle="solid"
|
||||||
onChange={(e) => history.push(e.target.value)}
|
onChange={(e) => history.push(e.target.value)}
|
||||||
>
|
>
|
||||||
|
<Radio.Button value={buildTuneUrl(Routes.TUNE_ROOT)}>
|
||||||
|
<Space>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
{sm && 'Info'}
|
||||||
|
</Space>
|
||||||
|
</Radio.Button>
|
||||||
<Radio.Button value={buildTuneUrl(Routes.TUNE_TUNE)}>
|
<Radio.Button value={buildTuneUrl(Routes.TUNE_TUNE)}>
|
||||||
<Space>
|
<Space>
|
||||||
<ToolOutlined />
|
<ToolOutlined />
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { notification } from 'antd';
|
import { notification } from 'antd';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
|
import { Timestamp } from 'firebase/firestore';
|
||||||
import {
|
import {
|
||||||
fireStoreDoc,
|
fireStoreDoc,
|
||||||
getDoc,
|
getDoc,
|
||||||
|
@ -16,8 +17,13 @@ const useDb = () => {
|
||||||
const getData = async (tuneId: string) => {
|
const getData = async (tuneId: string) => {
|
||||||
try {
|
try {
|
||||||
const tune = (await getDoc(fireStoreDoc(db, TUNES_PATH, tuneId))).data() as TuneDbData;
|
const tune = (await getDoc(fireStoreDoc(db, TUNES_PATH, tuneId))).data() as TuneDbData;
|
||||||
|
const processed = {
|
||||||
|
...tune,
|
||||||
|
createdAt: (tune?.createdAt as Timestamp)?.toDate().toISOString(),
|
||||||
|
updatedAt: (tune?.updatedAt as Timestamp)?.toDate().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
return Promise.resolve(tune);
|
return Promise.resolve(processed);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import {
|
||||||
|
Col,
|
||||||
|
Divider,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Skeleton,
|
||||||
|
} from 'antd';
|
||||||
|
import {
|
||||||
|
AppState,
|
||||||
|
TuneDataState,
|
||||||
|
} from '../types/state';
|
||||||
|
|
||||||
|
const { Item } = Form;
|
||||||
|
const containerStyle = {
|
||||||
|
padding: 20,
|
||||||
|
maxWidth: 600,
|
||||||
|
margin: '0 auto',
|
||||||
|
};
|
||||||
|
const rowProps = { gutter: 10 };
|
||||||
|
|
||||||
|
const mapStateToProps = (state: AppState) => ({
|
||||||
|
tuneData: state.tuneData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Info = ({ tuneData }: { tuneData: TuneDataState }) => {
|
||||||
|
if (!tuneData.details) {
|
||||||
|
return (
|
||||||
|
<div style={containerStyle}>
|
||||||
|
<Skeleton active />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={containerStyle}>
|
||||||
|
<Divider>Details</Divider>
|
||||||
|
<Form>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.make!} addonBefore="Make" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.model!} addonBefore="Model" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.year!} addonBefore="Year" style={{ width: '100%' }} />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.displacement!} addonBefore="Displacement" addonAfter="l" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.hp!} addonBefore="HP" style={{ width: '100%' }} />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.stockHp!} addonBefore="Stock HP" style={{ width: '100%' }} />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.engineCode!} addonBefore="Engine code" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.cylindersCount!} addonBefore="No of cylinders" style={{ width: '100%' }} />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Select placeholder="Aspiration" style={{ width: '100%' }} value={tuneData.details.aspiration}>
|
||||||
|
<Select.Option value="na">Naturally aspirated</Select.Option>
|
||||||
|
<Select.Option value="turbocharger">Turbocharged</Select.Option>
|
||||||
|
<Select.Option value="supercharger">Supercharged</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.fuel!} addonBefore="Fuel" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row {...rowProps}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.injectorsSize!} addonBefore="Injectors size" addonAfter="cc" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Item>
|
||||||
|
<Input value={tuneData.details.coils!} addonBefore="Coils" />
|
||||||
|
</Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
<Divider>README</Divider>
|
||||||
|
<div className="markdown-preview" style={{ height: '100%' }}>
|
||||||
|
{tuneData.details?.readme && <ReactMarkdown>
|
||||||
|
{`${tuneData.details?.readme}`}
|
||||||
|
</ReactMarkdown>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Info);
|
|
@ -42,7 +42,7 @@ const Tune = ({ navigation, config }: { navigation: NavigationState, config: Con
|
||||||
const firstCategory = Object.keys(config.menus)[0];
|
const firstCategory = Object.keys(config.menus)[0];
|
||||||
const firstDialog = Object.keys(config.menus[firstCategory].subMenus)[0];
|
const firstDialog = Object.keys(config.menus[firstCategory].subMenus)[0];
|
||||||
return generatePath(Routes.TUNE_DIALOG, {
|
return generatePath(Routes.TUNE_DIALOG, {
|
||||||
tuneId: navigation.tuneId || 'not-ready',
|
tuneId: navigation.tuneId!,
|
||||||
category: firstCategory,
|
category: firstCategory,
|
||||||
dialog: firstDialog,
|
dialog: firstDialog,
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,7 +34,10 @@ import * as Sentry from '@sentry/browser';
|
||||||
import { INI } from '@speedy-tuner/ini';
|
import { INI } from '@speedy-tuner/ini';
|
||||||
import { UploadRequestOption } from 'rc-upload/lib/interface';
|
import { UploadRequestOption } from 'rc-upload/lib/interface';
|
||||||
import { UploadFile } from 'antd/lib/upload/interface';
|
import { UploadFile } from 'antd/lib/upload/interface';
|
||||||
import { useHistory } from 'react-router-dom';
|
import {
|
||||||
|
generatePath,
|
||||||
|
useHistory,
|
||||||
|
} from 'react-router-dom';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import pako from 'pako';
|
import pako from 'pako';
|
||||||
import {
|
import {
|
||||||
|
@ -47,7 +50,6 @@ import {
|
||||||
} from './auth/notifications';
|
} from './auth/notifications';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { Routes } from '../routes';
|
import { Routes } from '../routes';
|
||||||
import useBrowserStorage from '../hooks/useBrowserStorage';
|
|
||||||
import TuneParser from '../utils/tune/TuneParser';
|
import TuneParser from '../utils/tune/TuneParser';
|
||||||
import TriggerLogsParser from '../utils/logs/TriggerLogsParser';
|
import TriggerLogsParser from '../utils/logs/TriggerLogsParser';
|
||||||
import LogParser from '../utils/logs/LogParser';
|
import LogParser from '../utils/logs/LogParser';
|
||||||
|
@ -87,7 +89,6 @@ const containerStyle = {
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
};
|
};
|
||||||
|
|
||||||
const newTuneIdKey = 'newTuneId';
|
|
||||||
const maxFileSizeMB = 10;
|
const maxFileSizeMB = 10;
|
||||||
const descriptionEditorHeight = 260;
|
const descriptionEditorHeight = 260;
|
||||||
const rowProps = { gutter: 10 };
|
const rowProps = { gutter: 10 };
|
||||||
|
@ -113,7 +114,6 @@ const UploadPage = () => {
|
||||||
const hasNavigatorShare = navigator.share !== undefined;
|
const hasNavigatorShare = navigator.share !== undefined;
|
||||||
const { currentUser, refreshToken } = useAuth();
|
const { currentUser, refreshToken } = useAuth();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { storageSet, storageGet, storageDelete } = useBrowserStorage();
|
|
||||||
const { removeFile, uploadFile, basePathForFile } = useServerStorage();
|
const { removeFile, uploadFile, basePathForFile } = useServerStorage();
|
||||||
const { updateData, getTune } = useDb();
|
const { updateData, getTune } = useDb();
|
||||||
const requiredRules = [{ required: true, message: 'This field is required!' }];
|
const requiredRules = [{ required: true, message: 'This field is required!' }];
|
||||||
|
@ -121,6 +121,10 @@ const UploadPage = () => {
|
||||||
|
|
||||||
const noop = () => { };
|
const noop = () => { };
|
||||||
|
|
||||||
|
const goToNewTune = () => history.push(generatePath(Routes.TUNE_ROOT, {
|
||||||
|
tuneId: newTuneId!,
|
||||||
|
}));
|
||||||
|
|
||||||
const copyToClipboard = async () => {
|
const copyToClipboard = async () => {
|
||||||
if (navigator.clipboard) {
|
if (navigator.clipboard) {
|
||||||
await navigator.clipboard.writeText(shareUrl!);
|
await navigator.clipboard.writeText(shareUrl!);
|
||||||
|
@ -156,7 +160,6 @@ const UploadPage = () => {
|
||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setIsPublished(true);
|
setIsPublished(true);
|
||||||
storageDelete(newTuneIdKey);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateSize = (file: File) => Promise.resolve({
|
const validateSize = (file: File) => Promise.resolve({
|
||||||
|
@ -425,18 +428,12 @@ const UploadPage = () => {
|
||||||
setIsUserAuthorized(true);
|
setIsUserAuthorized(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
storageDelete(newTuneIdKey);
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
genericError(error as Error);
|
genericError(error as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
let newTuneIdTemp = await storageGet(newTuneIdKey);
|
setNewTuneId(nanoidCustom());
|
||||||
if (!newTuneIdTemp) {
|
}, [currentUser, history, refreshToken]);
|
||||||
newTuneIdTemp = nanoidCustom();
|
|
||||||
await storageSet(newTuneIdKey, newTuneIdTemp);
|
|
||||||
}
|
|
||||||
setNewTuneId(newTuneIdTemp);
|
|
||||||
}, [currentUser, history, refreshToken, storageDelete, storageGet, storageSet]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
prepareData();
|
prepareData();
|
||||||
|
@ -480,9 +477,7 @@ const UploadPage = () => {
|
||||||
</Button> : <Button
|
</Button> : <Button
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
onClick={() => {
|
onClick={goToNewTune}
|
||||||
window.location.href = shareUrl as string;
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Open
|
Open
|
||||||
</Button>}
|
</Button>}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
AppState,
|
AppState,
|
||||||
ConfigState,
|
ConfigState,
|
||||||
LogsState,
|
LogsState,
|
||||||
|
TuneDataState,
|
||||||
TuneState,
|
TuneState,
|
||||||
UpdateTunePayload,
|
UpdateTunePayload,
|
||||||
} from './types/state';
|
} from './types/state';
|
||||||
|
@ -16,6 +17,7 @@ import {
|
||||||
// tune and config
|
// tune and config
|
||||||
const updateTune = createAction<UpdateTunePayload>('tune/update');
|
const updateTune = createAction<UpdateTunePayload>('tune/update');
|
||||||
const loadTune = createAction<TuneState>('tune/load');
|
const loadTune = createAction<TuneState>('tune/load');
|
||||||
|
const loadTuneData = createAction<TuneDataState>('tuneData/load');
|
||||||
const loadConfig = createAction<ConfigState>('config/load');
|
const loadConfig = createAction<ConfigState>('config/load');
|
||||||
|
|
||||||
// navigation
|
// navigation
|
||||||
|
@ -35,6 +37,7 @@ const initialState: AppState = {
|
||||||
tune: {
|
tune: {
|
||||||
constants: {},
|
constants: {},
|
||||||
},
|
},
|
||||||
|
tuneData: {},
|
||||||
logs: [],
|
logs: [],
|
||||||
config: {} as any,
|
config: {} as any,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -56,6 +59,9 @@ const rootReducer = createReducer(initialState, (builder) => {
|
||||||
.addCase(loadTune, (state: AppState, action) => {
|
.addCase(loadTune, (state: AppState, action) => {
|
||||||
state.tune = action.payload;
|
state.tune = action.payload;
|
||||||
})
|
})
|
||||||
|
.addCase(loadTuneData, (state: AppState, action) => {
|
||||||
|
state.tuneData = action.payload;
|
||||||
|
})
|
||||||
.addCase(loadLogs, (state: AppState, action) => {
|
.addCase(loadLogs, (state: AppState, action) => {
|
||||||
state.logs = action.payload;
|
state.logs = action.payload;
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Timestamp } from 'firebase/firestore';
|
||||||
|
|
||||||
export interface TuneDataDetails {
|
export interface TuneDataDetails {
|
||||||
readme?: string | null;
|
readme?: string | null;
|
||||||
make?: string | null;
|
make?: string | null;
|
||||||
|
@ -16,8 +18,8 @@ export interface TuneDataDetails {
|
||||||
|
|
||||||
export interface TuneDbData {
|
export interface TuneDbData {
|
||||||
userUid?: string;
|
userUid?: string;
|
||||||
createdAt?: Date;
|
createdAt?: Date | Timestamp | string;
|
||||||
updatedAt?: Date;
|
updatedAt?: Date | Timestamp | string;
|
||||||
isPublished?: boolean;
|
isPublished?: boolean;
|
||||||
isListed?: boolean;
|
isListed?: boolean;
|
||||||
isPublic?: boolean;
|
isPublic?: boolean;
|
||||||
|
|
|
@ -3,11 +3,14 @@ import {
|
||||||
Logs,
|
Logs,
|
||||||
Tune,
|
Tune,
|
||||||
} from '@speedy-tuner/types';
|
} from '@speedy-tuner/types';
|
||||||
|
import { TuneDbData } from './dbData';
|
||||||
|
|
||||||
export interface ConfigState extends Config {}
|
export interface ConfigState extends Config {}
|
||||||
|
|
||||||
export interface TuneState extends Tune {}
|
export interface TuneState extends Tune {}
|
||||||
|
|
||||||
|
export interface TuneDataState extends TuneDbData {}
|
||||||
|
|
||||||
export interface LogsState extends Logs {}
|
export interface LogsState extends Logs {}
|
||||||
|
|
||||||
export interface UIState {
|
export interface UIState {
|
||||||
|
@ -24,6 +27,7 @@ export interface NavigationState {
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
tune: TuneState;
|
tune: TuneState;
|
||||||
|
tuneData: TuneDataState;
|
||||||
config: ConfigState;
|
config: ConfigState;
|
||||||
logs: LogsState,
|
logs: LogsState,
|
||||||
ui: UIState;
|
ui: UIState;
|
||||||
|
|
Loading…
Reference in New Issue