all of the linting
parent
df44a9f49e
commit
0e6b7c7d3a
15
src/App.js
15
src/App.js
|
@ -4,9 +4,9 @@ import React, { useState } from 'react';
|
|||
import Login from './components/views/login';
|
||||
import UserAdmin from './components/views/useradmin';
|
||||
import GlobalSnack from './components/widgets/globalSnack';
|
||||
import DeviceStore from './context/devices';
|
||||
import ToastStore from './context/toast';
|
||||
import { UserProvider } from './context/users';
|
||||
import DevicesProvider from './context/devices';
|
||||
import ToastProvider from './context/toast';
|
||||
import UserProvider from './context/users';
|
||||
import * as authenticationController from './controllers/authentication';
|
||||
|
||||
// Connection opened
|
||||
|
@ -34,16 +34,15 @@ function App() {
|
|||
<CssBaseline />
|
||||
|
||||
<UserProvider>
|
||||
<DeviceStore>
|
||||
<ToastStore>
|
||||
<DevicesProvider>
|
||||
<ToastProvider>
|
||||
|
||||
<GlobalSnack />
|
||||
|
||||
{session ? <UserAdmin /> : <Login />}
|
||||
|
||||
</ToastStore>
|
||||
</DeviceStore>
|
||||
|
||||
</ToastProvider>
|
||||
</DevicesProvider>
|
||||
</UserProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import GoogleMapReact from 'google-map-react';
|
||||
import React from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars';
|
||||
|
||||
import DrivesTable from './tabPane';
|
||||
|
||||
function DeviceLastSeenMap() {
|
||||
return (
|
||||
<div style={{ height: '500px', width: 'calc(100%)' }}>
|
||||
|
||||
<GoogleMapReact
|
||||
height="100px"
|
||||
bootstrapURLKeys={{ key: process.env.REACT_APP_GMAPS_API_KEY }}
|
||||
|
@ -103,14 +104,16 @@ function DeviceLastSeenMap() {
|
|||
);
|
||||
}
|
||||
|
||||
export default function SignIn(props) {
|
||||
return (
|
||||
<div style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
function DeviceData(props) {
|
||||
const { device } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Scrollbars autoHeightMin="100%" autoHeightMax="100%">
|
||||
<Grid container style={{ padding: 30 }}>
|
||||
<Grid item xs={12}>
|
||||
|
@ -118,12 +121,18 @@ export default function SignIn(props) {
|
|||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<DrivesTable dongleId={props.device.dongle_id} />
|
||||
<DrivesTable dongleId={device.dongle_id} />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Scrollbars>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
DeviceData.propTypes = {
|
||||
device: PropTypes.shape({
|
||||
dongle_id: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default DeviceData;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ButtonBase from '@mui/material/ButtonBase';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
const stylezz = {
|
||||
margin: '0px 2px 0px 0px',
|
||||
|
@ -22,10 +23,11 @@ function timeSince(date) {
|
|||
return 'just now';
|
||||
}
|
||||
|
||||
export default function SignIn(props) {
|
||||
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
|
||||
function DeviceOverview(props) {
|
||||
const { device } = props;
|
||||
|
||||
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
|
||||
|
||||
// Reloads component to update X time ago
|
||||
// TODO prevent X time ago from being refreshed when the device has been
|
||||
// updated to show offline.
|
||||
|
@ -72,7 +74,6 @@ export default function SignIn(props) {
|
|||
</Typography>
|
||||
|
||||
<div>
|
||||
|
||||
{device.online
|
||||
? <Chip style={{ background: '#004d40', ...stylezz }} label="Online" size="small" variant="outlined" />
|
||||
: <Chip style={{ background: '#b71c1c', ...stylezz }} label={`Offline ${deviceLastSeen}`} size="small" variant="outlined" />}
|
||||
|
@ -84,6 +85,15 @@ export default function SignIn(props) {
|
|||
</ButtonBase>
|
||||
<Divider />
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
DeviceOverview.propTypes = {
|
||||
device: PropTypes.shape({
|
||||
dongle_id: PropTypes.string.isRequired,
|
||||
last_seen: PropTypes.string.isRequired,
|
||||
online: PropTypes.bool.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default DeviceOverview;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Skeleton } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
@ -12,9 +11,12 @@ import TableContainer from '@mui/material/TableContainer';
|
|||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { context as DeviceContext } from '../../../context/devices';
|
||||
import { context as SnackbarContext } from '../../../context/toast';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
import { DevicesContext } from '../../../context/devices';
|
||||
import { ToastContext } from '../../../context/toast';
|
||||
import * as deviceController from '../../../controllers/devices';
|
||||
import * as helpers from '../../../controllers/helpers';
|
||||
|
||||
|
@ -29,20 +31,26 @@ function loading() {
|
|||
);
|
||||
}
|
||||
|
||||
export default function EnhancedTable(props) {
|
||||
const [state, dispatch] = useContext(DeviceContext);
|
||||
function BootLogsTable(props) {
|
||||
const { dongleId } = props;
|
||||
|
||||
const [, notifDispatch] = useContext(SnackbarContext);
|
||||
const [state, dispatch] = useContext(DevicesContext);
|
||||
const [, notifDispatch] = useContext(ToastContext);
|
||||
|
||||
useEffect(() => {
|
||||
deviceController.getBootlogs(props.dongleId).then((res) => {
|
||||
deviceController.getBootlogs(dongleId).then((res) => {
|
||||
// TODO: why set timeout 1?
|
||||
setTimeout(() => {
|
||||
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
|
||||
dispatch({
|
||||
type: 'update_dongle_bootlogs',
|
||||
dongle_id: dongleId,
|
||||
bootlogs: res.data,
|
||||
});
|
||||
}, 1);
|
||||
}).catch(() => {
|
||||
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load bootlogs' });
|
||||
});
|
||||
}, [dispatch, notifDispatch, props.dongleId]);
|
||||
}, [dispatch, notifDispatch, dongleId]);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
|
@ -65,14 +73,13 @@ export default function EnhancedTable(props) {
|
|||
<TableBody>
|
||||
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
|
||||
rows.slice().sort(getComparator(order, orderBy)) */}
|
||||
{state.dongles[props.dongleId].boot ? state.dongles[props.dongleId].boot.map((row) => (
|
||||
{state.dongles[dongleId].boot ? state.dongles[dongleId].boot.map((row) => (
|
||||
<TableRow hover>
|
||||
<TableCell>{helpers.formatDate(row.date)}</TableCell>
|
||||
<TableCell>{row.name}</TableCell>
|
||||
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip title="Open in new window">
|
||||
|
||||
<IconButton size="small" onClick={() => window.open(row.permalink, '_blank')}>
|
||||
<OpenInNewIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
|
@ -100,3 +107,9 @@ export default function EnhancedTable(props) {
|
|||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
BootLogsTable.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default BootLogsTable;
|
||||
|
|
|
@ -54,31 +54,26 @@ function RichContent(props) {
|
|||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
export default function Console() {
|
||||
|
||||
|
||||
function ConsoleTable() {
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
|
||||
{/*
|
||||
Object.keys(state).map(key => {
|
||||
console.log(key, state[key])
|
||||
return (<RichContent content={state[key]} key1={key} />)
|
||||
}
|
||||
|
||||
|
||||
)
|
||||
*/}
|
||||
|
||||
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConsoleTable;
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Skeleton } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
@ -14,17 +11,17 @@ import TableContainer from '@mui/material/TableContainer';
|
|||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { context as DeviceContext } from '../../../context/devices';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
import { ACTIONS, DevicesContext } from '../../../context/devices';
|
||||
import * as deviceController from '../../../controllers/devices';
|
||||
import * as helpers from '../../../controllers/helpers';
|
||||
|
||||
function buildContent(row) {
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
>
|
||||
|
||||
<TableRow hover>
|
||||
<TableCell>{helpers.formatDate(row.date)}</TableCell>
|
||||
<TableCell>{row.name}</TableCell>
|
||||
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
|
||||
|
@ -47,7 +44,6 @@ function buildContent(row) {
|
|||
<DeleteIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
@ -64,17 +60,23 @@ function loading() {
|
|||
);
|
||||
}
|
||||
|
||||
export default function EnhancedTable(props) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [state, dispatch] = useContext(DeviceContext);
|
||||
useEffect(() => {
|
||||
deviceController.getCrashlogs(props.dongleId).then((res) => {
|
||||
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
|
||||
});
|
||||
}, [dispatch, props.dongleId]);
|
||||
function CrashLogsTable(props) {
|
||||
const { dongleId } = props;
|
||||
|
||||
console.log('drives', state.dongles[props.dongleId]);
|
||||
console.log('drives', typeof state.dongles[props.dongleId]);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [state, dispatch] = useContext(DevicesContext);
|
||||
useEffect(() => {
|
||||
deviceController.getCrashlogs(dongleId).then((res) => {
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_DONGLE_BOOTLOGS,
|
||||
dongle_id: dongleId,
|
||||
bootlogs: res.data,
|
||||
});
|
||||
});
|
||||
}, [dispatch, dongleId]);
|
||||
|
||||
console.log('drives', state.dongles[dongleId]);
|
||||
console.log('drives', typeof state.dongles[dongleId]);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
|
@ -95,10 +97,11 @@ export default function EnhancedTable(props) {
|
|||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{state.dongles[props.dongleId].crash
|
||||
? state.dongles[props.dongleId].crash.length > 0 ? state.dongles[props.dongleId].crash.map(buildContent) : <p> No drives </p>
|
||||
{state.dongles[dongleId].crash
|
||||
? (state.dongles[dongleId].crash.length > 0
|
||||
? state.dongles[dongleId].crash.map(buildContent)
|
||||
: <p> No drives </p>)
|
||||
: [1, 1, 1, 1, 1].map(loading)}
|
||||
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
@ -107,3 +110,9 @@ export default function EnhancedTable(props) {
|
|||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
CrashLogsTable.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default CrashLogsTable;
|
||||
|
|
|
@ -1,37 +1,43 @@
|
|||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React, { useContext } from 'react';
|
||||
import { context as DeviceContext } from '../../../context/devices';
|
||||
import { context as SnackbarContext } from '../../../context/toast';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
import { DevicesContext } from '../../../context/devices';
|
||||
import { ACTIONS as ToastActions, ToastContext } from '../../../context/toast';
|
||||
import * as helpers from '../../../controllers/helpers';
|
||||
|
||||
export default function SignIn(props) {
|
||||
const [state] = useContext(DeviceContext);
|
||||
const [, notifDispatch] = useContext(SnackbarContext);
|
||||
function DeviceInfo(props) {
|
||||
const { dongleId } = props;
|
||||
|
||||
const [devicesState] = useContext(DevicesContext);
|
||||
const [, toastDispatch] = useContext(ToastContext);
|
||||
|
||||
if (!devicesState.dongles[dongleId]) {
|
||||
return (<p>no</p>);
|
||||
}
|
||||
|
||||
function pubKeyClipboard(newClip) {
|
||||
navigator.clipboard.writeText(newClip).then(() => {
|
||||
notifDispatch({
|
||||
type: 'NEW_TOAST',
|
||||
toastDispatch({
|
||||
type: ToastActions.NEW_TOAST,
|
||||
open: true,
|
||||
msg: 'Successfully copied to clipboard!',
|
||||
});
|
||||
}, () => {
|
||||
notifDispatch({
|
||||
type: 'NEW_TOAST',
|
||||
toastDispatch({
|
||||
type: ToastActions.NEW_TOAST,
|
||||
open: true,
|
||||
msg: 'Failed to write to clipboard!',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!state.dongles[props.dongleId]) { return (<p>no</p>); }
|
||||
const dongle = state.dongles[props.dongleId];
|
||||
|
||||
const dongle = devicesState.dongles[dongleId];
|
||||
if (!dongle) {
|
||||
return (
|
||||
<Grid container>
|
||||
|
@ -40,15 +46,13 @@ export default function SignIn(props) {
|
|||
<Skeleton animation="wave" />
|
||||
<Skeleton animation="wave" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wrapper" style={{ marginTop: '10px' }}>
|
||||
<Typography variant="body1">{state.dongles[props.dongleId].dongle_id}</Typography>
|
||||
<Typography variant="body1">{devicesState.dongles[dongleId].dongle_id}</Typography>
|
||||
|
||||
<Grid container>
|
||||
<Grid item xs={3}>
|
||||
|
@ -97,3 +101,9 @@ export default function SignIn(props) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
DeviceInfo.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default DeviceInfo;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Skeleton } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
@ -12,27 +11,32 @@ import TableContainer from '@mui/material/TableContainer';
|
|||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { context as DeviceContext } from '../../../context/devices';
|
||||
import { context as SnackbarContext } from '../../../context/toast';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
import { DevicesContext } from '../../../context/devices';
|
||||
import { ToastContext } from '../../../context/toast';
|
||||
import * as deviceController from '../../../controllers/devices';
|
||||
import * as helpers from '../../../controllers/helpers';
|
||||
import ViewDrive from './view_drive';
|
||||
|
||||
export default function EnhancedTable(props) {
|
||||
const [deviceState, dispatch] = useContext(DeviceContext);
|
||||
const [, notifDispatch] = useContext(SnackbarContext);
|
||||
function DrivesLogTable(props) {
|
||||
const { dongleId } = props;
|
||||
|
||||
const [devicesState, dispatch] = useContext(DevicesContext);
|
||||
const [, notifDispatch] = useContext(ToastContext);
|
||||
const [state, setState] = useState({ selectedSegment: null });
|
||||
|
||||
useEffect(() => {
|
||||
deviceController.getDrives(props.dongleId).then((res) => {
|
||||
deviceController.getDrives(dongleId).then((res) => {
|
||||
setTimeout(() => {
|
||||
dispatch({ type: 'update_dongle_drive', dongle_id: props.dongleId, drives: res.data });
|
||||
dispatch({ type: 'update_dongle_drive', dongle_id: dongleId, drives: res.data });
|
||||
}, 1);
|
||||
}).catch(() => {
|
||||
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load drives' });
|
||||
});
|
||||
}, [dispatch, notifDispatch, props.dongleId]);
|
||||
}, [dispatch, notifDispatch, dongleId]);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
|
@ -61,57 +65,58 @@ export default function EnhancedTable(props) {
|
|||
<TableBody>
|
||||
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
|
||||
rows.slice().sort(getComparator(order, orderBy)) */}
|
||||
{deviceState.dongles[props.dongleId].drives ? deviceState.dongles[props.dongleId].drives.map((row, index) => {
|
||||
let metadata;
|
||||
{devicesState.dongles[dongleId].drives
|
||||
? devicesState.dongles[dongleId].drives.map((row, index) => {
|
||||
let metadata;
|
||||
|
||||
try {
|
||||
metadata = JSON.parse(row.metadata);
|
||||
} catch (err) { metadata = {}; }
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={() => { state.selectedSegment === index ? setState({ ...state, selectedSegment: null }) : setState({ ...state, selectedSegment: index }); }}
|
||||
>
|
||||
<TableCell
|
||||
scope="row"
|
||||
try {
|
||||
metadata = JSON.parse(row.metadata);
|
||||
} catch (err) { metadata = {}; }
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={() => (state.selectedSegment === index
|
||||
? setState({ ...state, selectedSegment: null })
|
||||
: setState({ ...state, selectedSegment: index }))}
|
||||
>
|
||||
{row.identifier}
|
||||
</TableCell>
|
||||
<TableCell scope="row">
|
||||
{row.identifier}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{metadata.hasOwnProperty('CarParams1') ? metadata.CarParams.CarName : 'Glorious Skoda'}</TableCell>
|
||||
<TableCell>{metadata.hasOwnProperty('InitData1') ? metadata.InitData.Version : 'Lemon boy'}</TableCell>
|
||||
<TableCell>{`${Math.round(row.filesize / 1024)} MiB`}</TableCell>
|
||||
<TableCell>{helpers.formatDuration(row.duration)}</TableCell>
|
||||
<TableCell>{Math.round(row.distance_meters / 1000)}</TableCell>
|
||||
<TableCell>{row.upload_complete.toString()}</TableCell>
|
||||
<TableCell>{row.is_processed.toString()}</TableCell>
|
||||
<TableCell>{helpers.formatDate(row.drive_date)}</TableCell>
|
||||
<TableCell>{metadata.CarParams ? metadata.CarParams.CarName : 'Glorious Skoda'}</TableCell>
|
||||
<TableCell>{metadata.InitData ? metadata.InitData.Version : 'Lemon boy'}</TableCell>
|
||||
<TableCell>{`${Math.round(row.filesize / 1024)} MiB`}</TableCell>
|
||||
<TableCell>{helpers.formatDuration(row.duration)}</TableCell>
|
||||
<TableCell>{Math.round(row.distance_meters / 1000)}</TableCell>
|
||||
<TableCell>{row.upload_complete.toString()}</TableCell>
|
||||
<TableCell>{row.is_processed.toString()}</TableCell>
|
||||
<TableCell>{helpers.formatDate(row.drive_date)}</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Tooltip title="Open in new window">
|
||||
<IconButton size="small">
|
||||
<OpenInNewIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<TableCell>
|
||||
<Tooltip title="Open in new window">
|
||||
<IconButton size="small">
|
||||
<OpenInNewIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Preserve">
|
||||
<IconButton size="small">
|
||||
<FavoriteBorderIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Preserve">
|
||||
<IconButton size="small">
|
||||
<FavoriteBorderIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Delete">
|
||||
<IconButton size="small">
|
||||
<DeleteIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete">
|
||||
<IconButton size="small">
|
||||
<DeleteIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
|
||||
: [1, 1, 1, 1, 1].map((v) => (
|
||||
: [1, 1, 1, 1, 1].map(() => (
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Skeleton animation="wave" />
|
||||
|
@ -138,7 +143,15 @@ export default function EnhancedTable(props) {
|
|||
</TableContainer>
|
||||
</Paper>
|
||||
|
||||
{state.selectedSegment ? <ViewDrive dongleId={props.dongleId} drive={state.selectedSegment} /> : null }
|
||||
{state.selectedSegment
|
||||
? <ViewDrive dongleId={dongleId} drive={state.selectedSegment} />
|
||||
: null }
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
DrivesLogTable.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default DrivesLogTable;
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Box from '@mui/material/Box';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import React from 'react';
|
||||
|
||||
import BootLogsTable from './boot';
|
||||
import Console from './console';
|
||||
import ConsoleTable from './console';
|
||||
import CrashLogsTable from './crash';
|
||||
import DeviceInfo from './device';
|
||||
import DrivesLogTable from './drives';
|
||||
|
||||
function TabPanel(props) {
|
||||
const {
|
||||
children, value, index, ...other
|
||||
children,
|
||||
value,
|
||||
index,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
@ -19,7 +23,6 @@ function TabPanel(props) {
|
|||
hidden={value !== index}
|
||||
id={`simple-tabpanel-${index}`}
|
||||
aria-labelledby={`simple-tab-${index}`}
|
||||
{...other}
|
||||
>
|
||||
{value === index && (
|
||||
<div style={{ padding: '5px' }}>
|
||||
|
@ -30,7 +33,15 @@ function TabPanel(props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function SignIn(props) {
|
||||
TabPanel.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
value: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
function TabPane(props) {
|
||||
const { dongleId } = props;
|
||||
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
|
@ -39,7 +50,6 @@ export default function SignIn(props) {
|
|||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs
|
||||
value={value}
|
||||
|
@ -56,24 +66,26 @@ export default function SignIn(props) {
|
|||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={value} index={0}>
|
||||
<DeviceInfo dongleId={props.dongleId} />
|
||||
<DeviceInfo dongleId={dongleId} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1}>
|
||||
<DrivesLogTable dongleId={props.dongleId} />
|
||||
<DrivesLogTable dongleId={dongleId} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={2}>
|
||||
<CrashLogsTable dongleId={props.dongleId} />
|
||||
<CrashLogsTable dongleId={dongleId} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={3}>
|
||||
<BootLogsTable dongleId={props.dongleId} />
|
||||
<BootLogsTable dongleId={dongleId} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={4}>
|
||||
<Console dongleId={props.dongleId} />
|
||||
<ConsoleTable dongleId={dongleId} />
|
||||
</TabPanel>
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TabPane.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default TabPane;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import Box from '@mui/material/Box';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
@ -9,38 +11,41 @@ import TableContainer from '@mui/material/TableContainer';
|
|||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { context as DeviceContext } from '../../../context/devices';
|
||||
|
||||
import { DevicesContext } from '../../../context/devices';
|
||||
import * as deviceController from '../../../controllers/devices';
|
||||
|
||||
export default function EnhancedTable(props) {
|
||||
const [deviceState] = useContext(DeviceContext);
|
||||
function ViewDrive(props) {
|
||||
const {
|
||||
dongleId,
|
||||
drive: driveId,
|
||||
} = props;
|
||||
|
||||
const [deviceState] = useContext(DevicesContext);
|
||||
|
||||
const [state, setState] = useState({
|
||||
loading: true, firstReqSent: false, segment: null, drive: null,
|
||||
loading: true, firstReqSent: false, segment: null, driveId: null,
|
||||
});
|
||||
|
||||
if (state.drive === null) {
|
||||
setState({ ...state, drive: props.drive });
|
||||
setState({ ...state, driveId });
|
||||
}
|
||||
|
||||
if (props.drive !== state.drive) {
|
||||
if (driveId !== state.drive) {
|
||||
setState({
|
||||
...state, loading: true, firstReqSent: false, segment: null, drive: props.drive,
|
||||
...state, loading: true, firstReqSent: false, segment: null, driveId,
|
||||
});
|
||||
}
|
||||
|
||||
const dongle_id = props.dongleId;
|
||||
const drive_id = props.drive;
|
||||
const dongle = deviceState.dongles[dongle_id];
|
||||
const dongle = deviceState.dongles[dongleId];
|
||||
console.log('view drive', dongle);
|
||||
console.log('drives', dongle.drives);
|
||||
if (!dongle || !dongle.drives) return (<p>loading</p>);
|
||||
|
||||
if (state.segment === null) {
|
||||
// TODO Make this not run multiple times
|
||||
deviceController.getDriveSegments(dongle_id, dongle.drives[drive_id].identifier).then((res) => {
|
||||
deviceController.getDriveSegments(dongleId, dongle.drives[driveId].identifier).then((res) => {
|
||||
console.log('my res', res.data);
|
||||
if (res.data === null) {
|
||||
setState({
|
||||
|
@ -56,7 +61,7 @@ export default function EnhancedTable(props) {
|
|||
|
||||
// test
|
||||
|
||||
const drive = dongle.drives[drive_id];
|
||||
const drive = dongle.drives[driveId];
|
||||
|
||||
let vehicle = '';
|
||||
let version = '';
|
||||
|
@ -76,8 +81,12 @@ export default function EnhancedTable(props) {
|
|||
}
|
||||
|
||||
if (metadata.CarParams) {
|
||||
if (metadata.CarParams.CarName !== undefined) vehicle += `${metadata.CarParams.CarName.toUpperCase()} `;
|
||||
if (metadata.CarParams.CarFingerprint !== undefined) vehicle += (metadata.CarParams.CarFingerprint.toUpperCase());
|
||||
if (metadata.CarParams.CarName !== undefined) {
|
||||
vehicle += `${metadata.CarParams.CarName.toUpperCase()} `;
|
||||
}
|
||||
if (metadata.CarParams.CarFingerprint !== undefined) {
|
||||
vehicle += (metadata.CarParams.CarFingerprint.toUpperCase());
|
||||
}
|
||||
}
|
||||
} catch (exception) { console.log(exception); }
|
||||
|
||||
|
@ -99,7 +108,6 @@ export default function EnhancedTable(props) {
|
|||
url: `${driveUrl}/${segment}/${directoryTree.children[i].children[c].name}`,
|
||||
name: directoryTree.children[i].children[c].name,
|
||||
fileSize: directoryTree.children[i].children[c].size,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -112,7 +120,6 @@ export default function EnhancedTable(props) {
|
|||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2, padding: '20px' }}>
|
||||
|
||||
<Typography variant="body1">
|
||||
<b>Vehicle:</b>
|
||||
{' '}
|
||||
|
@ -157,33 +164,41 @@ export default function EnhancedTable(props) {
|
|||
</TableHead>
|
||||
<TableBody>
|
||||
{
|
||||
directorySegments ? Object.keys(directorySegments).map((key, index) => Object.keys(directorySegments[key]).map((key1, index1) => (
|
||||
<TableRow hover>
|
||||
<TableCell>{key}</TableCell>
|
||||
<TableCell>{directorySegments[key][key1].name}</TableCell>
|
||||
<TableCell>{`${Math.round(directorySegments[key][key1].fileSize / 1024)} MiB`}</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Tooltip title="Open in new window">
|
||||
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, '_blank')}>
|
||||
<OpenInNewIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
|
||||
</TableRow>
|
||||
))) : null
|
||||
}
|
||||
directorySegments
|
||||
? Object.keys(directorySegments)
|
||||
.map((key) => Object.keys(directorySegments[key])
|
||||
.map((key1) => (
|
||||
<TableRow hover>
|
||||
<TableCell>{key}</TableCell>
|
||||
<TableCell>{directorySegments[key][key1].name}</TableCell>
|
||||
<TableCell>{`${Math.round(directorySegments[key][key1].fileSize / 1024)} MiB`}</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Tooltip title="Open in new window">
|
||||
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, '_blank')}>
|
||||
<OpenInNewIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)))
|
||||
: null
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
ViewDrive.propTypes = {
|
||||
dongleId: PropTypes.string.isRequired,
|
||||
drive: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default ViewDrive;
|
||||
|
||||
/*
|
||||
|
||||
var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(device.dongle_id).digest('hex');
|
||||
|
|
|
@ -1,28 +1,10 @@
|
|||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { UserContext } from '../../context/users';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
export default function SignIn() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [state, dispatch] = useContext(UserContext);
|
||||
console.log('component', state);
|
||||
const handleSubmit = (event) => {
|
||||
dispatch({ type: 'toggle_button' });
|
||||
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log({
|
||||
email: data.get('email'),
|
||||
password: data.get('password'),
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<p>hello</p>
|
||||
|
@ -32,3 +14,5 @@ export default function SignIn() {
|
|||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Box from '@mui/material/Box';
|
||||
import Container from '@mui/material/Container';
|
||||
|
@ -6,7 +7,7 @@ import Link from '@mui/material/Link';
|
|||
import Paper from '@mui/material/Paper';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import { UserContext } from '../../context/users';
|
||||
|
||||
export default function SignIn() {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import React, { useContext } from 'react';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import { Scrollbars } from 'rc-scrollbars';
|
||||
import React, { useContext } from 'react';
|
||||
import { context as DeviceContext } from '../../context/devices';
|
||||
|
||||
import { DevicesContext } from '../../context/devices';
|
||||
import DeviceData from '../device/deviceData';
|
||||
import DeviceOverview from '../device/overview';
|
||||
|
||||
export default function SignIn() {
|
||||
const [deviceState] = useContext(DeviceContext);
|
||||
const [deviceState] = useContext(DevicesContext);
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
|
||||
<Grid
|
||||
container
|
||||
spacing={0}
|
||||
|
@ -23,18 +23,17 @@ export default function SignIn() {
|
|||
<Paper style={{ minHeight: '100%', maxHeight: '100%', margin: '0' }}>
|
||||
<Scrollbars autoHeight autoHeightMin="calc(100vh - 14px)" autoHeightMax="calc(100% - 14px)">
|
||||
<div style={{ padding: '5px' }}>
|
||||
{deviceState ? Object.keys(deviceState.dongles).map((key) => <DeviceOverview device={deviceState.dongles[key]} />) : <p>no</p>}
|
||||
|
||||
{deviceState
|
||||
? (Object.keys(deviceState.dongles)
|
||||
.map((key) => <DeviceOverview device={deviceState.dongles[key]} />))
|
||||
: <p>no</p>}
|
||||
</div>
|
||||
|
||||
</Scrollbars>
|
||||
|
||||
</Paper>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={8} lg={9} sm={6} xl={10}>
|
||||
{deviceState.dongles['53331425'] ? <DeviceData device={deviceState.dongles['53331425']} /> : <p>no</p>}
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import Snackbar from '@mui/material/Snackbar';
|
||||
import React, { useContext } from 'react';
|
||||
import { context as DeviceContext } from '../../context/toast';
|
||||
import Snackbar from '@mui/material/Snackbar';
|
||||
|
||||
export default function Toast(props) {
|
||||
const [state, dispatch] = useContext(DeviceContext);
|
||||
import { ACTIONS, ToastContext } from '../../context/toast';
|
||||
|
||||
function Toast() {
|
||||
const [state, dispatch] = useContext(ToastContext);
|
||||
|
||||
const handleClose = () => {
|
||||
dispatch({ type: 'CLOSE_TOAST' });
|
||||
dispatch({ type: ACTIONS.CLOSE_TOAST });
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -19,3 +20,5 @@ export default function Toast(props) {
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Toast;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
const ACTIONS = {
|
||||
ADD_DATA: 'add_data',
|
||||
FETCH_ALL_DONGLES: 'fetch_all_dongles',
|
||||
UPDATE_DONGLE_DRIVE: 'update_dongle_drive',
|
||||
UPDATE_DONGLE_BOOTLOGS: 'update_dongle_bootlogs',
|
||||
UPDATE_DONGLE_CRASHLOGS: 'update_dongle_crashlogs',
|
||||
USER_AUTHENTICATION: 'user_authentication',
|
||||
};
|
||||
|
||||
export default ACTIONS;
|
|
@ -1,125 +1,68 @@
|
|||
import React, { createContext, useEffect, useReducer } from 'react';
|
||||
import React, {
|
||||
createContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import * as deviceController from '../../controllers/devices';
|
||||
|
||||
function process(state, action) {
|
||||
if (action.type !== 'ADD_DATA') { return state; }
|
||||
|
||||
switch (action.data.command) {
|
||||
case 'dongle_status':
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.data.data.dongle_id]: {
|
||||
...state.dongles[action.data.data.dongle_id],
|
||||
online: action.data.data.online,
|
||||
last_seen: action.data.data.time,
|
||||
dongle_id: action.data.data.dongle_id,
|
||||
},
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const Reducer = (state, action) => {
|
||||
console.log('input', state, action);
|
||||
switch (action.type) {
|
||||
case 'ADD_DATA':
|
||||
return process(state, action);
|
||||
case 'fetch_all_dongles':
|
||||
console.log('fetch', action);
|
||||
return {
|
||||
...state,
|
||||
dongles: action.data,
|
||||
};
|
||||
|
||||
case 'update_dongle_drive':
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
drives: action.drives,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case 'update_dongle_bootlogs':
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
boot: action.bootlogs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case 'update_dongle_crashlogs':
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
crash: action.crashlogs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case 'user_authentication':
|
||||
return {
|
||||
...state,
|
||||
user: action.user,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
import ACTIONS from './actions';
|
||||
import reducer from './reducer';
|
||||
|
||||
const initialState = {
|
||||
dongles: {},
|
||||
};
|
||||
|
||||
function Store({ children }) {
|
||||
console.log('STORE HAS BEEN RERENDERED');
|
||||
const [state, dispatch] = useReducer(Reducer, initialState);
|
||||
const DevicesContext = createContext(initialState);
|
||||
|
||||
function DevicesProvider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket('ws://localhost:81');
|
||||
|
||||
ws.onmessage = ({ data }) => {
|
||||
data = JSON.parse(data);
|
||||
console.log('Message');
|
||||
if (data.id) {
|
||||
dispatch({ type: 'ADD_DATA', id: data.id, data });
|
||||
const payload = JSON.parse(data);
|
||||
console.log('devices onmessage', payload);
|
||||
|
||||
if (!payload.id) {
|
||||
dispatch({ type: ACTIONS.ADD_DATA, data: payload });
|
||||
}
|
||||
};
|
||||
|
||||
deviceController.getAllDevices().then((devices) => {
|
||||
console.log('store', devices);
|
||||
console.log('devices store', devices);
|
||||
|
||||
dispatch({ type: 'fetch_all_dongles', data: devices });
|
||||
dispatch({ type: ACTIONS.FETCH_ALL_DONGLES, data: devices });
|
||||
});
|
||||
|
||||
return () => {
|
||||
// Clean up the websocket
|
||||
try {
|
||||
ws.close();
|
||||
} catch (e) { }
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
|
||||
|
||||
return (
|
||||
<context.Provider value={[state, dispatch]}>
|
||||
{children}
|
||||
</context.Provider>
|
||||
<DevicesContext.Provider value={contextValue}>
|
||||
{ children }
|
||||
</DevicesContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const context = createContext(initialState);
|
||||
export default Store;
|
||||
DevicesProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default DevicesProvider;
|
||||
export {
|
||||
ACTIONS,
|
||||
DevicesContext,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import ACTIONS from './actions';
|
||||
|
||||
function process(state, action) {
|
||||
if (action.type !== ACTIONS.ADD_DATA) {
|
||||
return state;
|
||||
}
|
||||
|
||||
switch (action.data.command) {
|
||||
case 'dongle_status':
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.data.data.dongle_id]: {
|
||||
...state.dongles[action.data.data.dongle_id],
|
||||
online: action.data.data.online,
|
||||
last_seen: action.data.data.time,
|
||||
dongle_id: action.data.data.dongle_id,
|
||||
},
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function reducer(state, action) {
|
||||
console.log('input', state, action);
|
||||
switch (action.type) {
|
||||
case ACTIONS.ADD_DATA:
|
||||
return process(state, action);
|
||||
|
||||
case ACTIONS.FETCH_ALL_DONGLES:
|
||||
console.log('fetch', action);
|
||||
return {
|
||||
...state,
|
||||
dongles: action.data,
|
||||
};
|
||||
|
||||
case ACTIONS.UPDATE_DONGLE_DRIVE:
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
drives: action.drives,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ACTIONS.UPDATE_DONGLE_BOOTLOGS:
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
boot: action.bootlogs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ACTIONS.UPDATE_DONGLE_CRASHLOGS:
|
||||
return {
|
||||
...state,
|
||||
dongles: {
|
||||
...state.dongles,
|
||||
[action.dongle_id]: {
|
||||
...state.dongles[action.dongle_id],
|
||||
crash: action.crashlogs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ACTIONS.USER_AUTHENTICATION:
|
||||
return {
|
||||
...state,
|
||||
user: action.user,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default reducer;
|
|
@ -0,0 +1,6 @@
|
|||
const ACTIONS = {
|
||||
NEW_TOAST: 'new_toast',
|
||||
CLOSE_TOAST: 'close_toast',
|
||||
};
|
||||
|
||||
export default ACTIONS;
|
|
@ -1,19 +1,34 @@
|
|||
import React, { createContext, useReducer } from 'react';
|
||||
import Reducer from './reducer';
|
||||
import React, { createContext, useMemo, useReducer } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import ACTIONS from './actions';
|
||||
import reducer from './reducer';
|
||||
|
||||
const initialState = {
|
||||
open: false,
|
||||
message: null,
|
||||
};
|
||||
|
||||
function Store({ children }) {
|
||||
const [state, dispatch] = useReducer(Reducer, initialState);
|
||||
const ToastContext = createContext(initialState);
|
||||
|
||||
function ToastProvider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
|
||||
|
||||
return (
|
||||
<context.Provider value={[state, dispatch]}>
|
||||
<ToastContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</context.Provider>
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const context = createContext(initialState);
|
||||
export default Store;
|
||||
ToastProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default ToastProvider;
|
||||
export {
|
||||
ACTIONS,
|
||||
ToastContext,
|
||||
};
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
const Reducer = (state, action) => {
|
||||
import ACTIONS from './actions';
|
||||
|
||||
function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case 'NEW_TOAST':
|
||||
case ACTIONS.NEW_TOAST:
|
||||
return {
|
||||
...state,
|
||||
open: action.open,
|
||||
msg: action.message,
|
||||
};
|
||||
case 'CLOSE_TOAST':
|
||||
case ACTIONS.CLOSE_TOAST:
|
||||
return {
|
||||
...state,
|
||||
open: false,
|
||||
|
@ -15,6 +17,6 @@ const Reducer = (state, action) => {
|
|||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default Reducer;
|
||||
export default reducer;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
const ACTIONS = {
|
||||
SIGN_OUT: 'sign_out',
|
||||
};
|
||||
|
||||
export default ACTIONS;
|
|
@ -1,17 +1,38 @@
|
|||
import React from 'react';
|
||||
import { reducer, initialState } from './reducer';
|
||||
import React, { createContext, useMemo, useReducer } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const UserContext = React.createContext({
|
||||
state: initialState,
|
||||
dispatch: () => null,
|
||||
});
|
||||
import ACTIONS from './actions';
|
||||
import reducer from './reducer';
|
||||
|
||||
export function UserProvider({ children }) {
|
||||
const [state, dispatch] = React.useReducer(reducer, initialState);
|
||||
const initialState = {
|
||||
signedIn: false,
|
||||
user: {
|
||||
id: null,
|
||||
username: null,
|
||||
JWT: null,
|
||||
},
|
||||
};
|
||||
|
||||
const UserContext = createContext(initialState);
|
||||
|
||||
function UserProvider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={[state, dispatch]}>
|
||||
<UserContext.Provider value={contextValue}>
|
||||
{ children }
|
||||
</UserContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
UserProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default UserProvider;
|
||||
export {
|
||||
ACTIONS,
|
||||
UserContext,
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
export const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'sign_out':
|
||||
import ACTIONS from './actions';
|
||||
|
||||
function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case ACTIONS.SIGN_OUT:
|
||||
return {
|
||||
...state,
|
||||
active: !state.active,
|
||||
|
@ -10,13 +11,6 @@ export const reducer = (state, action) => {
|
|||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const initialState = {
|
||||
signedIn: false,
|
||||
user: {
|
||||
id: null,
|
||||
username: null,
|
||||
JWT: null,
|
||||
},
|
||||
};
|
||||
export default reducer;
|
||||
|
|
Loading…
Reference in New Issue