Compare commits
32 Commits
3b0a212ad4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a1dc088a23 | |||
| d4a19060e8 | |||
| fe9f7e81e9 | |||
| 6e07c2be85 | |||
| 03db7575fd | |||
| df7470f147 | |||
| 17a3436017 | |||
| 4bdcd49e1f | |||
| 031c1ede21 | |||
| 08e5c2e3d2 | |||
| 2c80c4b279 | |||
| 66d688291c | |||
| 49430114fe | |||
| cfd0056197 | |||
| facd8d912f | |||
| b743cbc9a5 | |||
| ace77028c4 | |||
| dd215a8acc | |||
| 90a03dc17c | |||
| e1c42c90d5 | |||
| cb8f6daccc | |||
| 822eff6c5d | |||
| ce86f1e029 | |||
| 7ed58e94d8 | |||
| 446afb939a | |||
| cae5d1c95a | |||
| fde5018da8 | |||
| fcc81cf7e5 | |||
| 459d334732 | |||
| c5a475f9c5 | |||
| 40e3cc9d7e | |||
| 90ba2410b9 |
@@ -34,7 +34,8 @@ module.exports = {
|
|||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always"
|
"always"
|
||||||
]
|
],
|
||||||
|
"no-unused-vars": [1]
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,3 +21,6 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
src/config.js
|
||||||
|
|||||||
@@ -7,12 +7,15 @@
|
|||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"grommet": "^2.16.2",
|
"grommet": "^2.16.2",
|
||||||
|
"grommet-icons": "^4.5.0",
|
||||||
"polished": "^4.0.5",
|
"polished": "^4.0.5",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"react-scripts": "4.0.1",
|
"react-scripts": "4.0.1",
|
||||||
|
"socket.io-client": "^3.0.4",
|
||||||
"styled-components": "^5.2.1"
|
"styled-components": "^5.2.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
39
src/App.css
39
src/App.css
@@ -1,39 +0,0 @@
|
|||||||
.App {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-float infinite 3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: rgb(112, 76, 182);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes App-logo-float {
|
|
||||||
0% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(10px)
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateY(0px)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
src/App.js
18
src/App.js
@@ -1,17 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Counter } from "./features/counter/Counter";
|
import { Grommet } from "grommet";
|
||||||
import "./App.css";
|
import Core from "./Core";
|
||||||
|
import WebSocketProvider from "./WebSocket";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<WebSocketProvider>
|
||||||
<header className="App-header">
|
<Grommet plain>
|
||||||
<Counter />
|
<Core />
|
||||||
<p>
|
</Grommet>
|
||||||
Edit <code>src/App.js</code> and save to reload.
|
</WebSocketProvider>
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
src/Core.js
Normal file
53
src/Core.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
import { Box, Button, Clock, Text } from "grommet";
|
||||||
|
|
||||||
|
import { setPlugin, selectCore } from "./coreSlice";
|
||||||
|
import { selectVoice } from "./voiceSlice";
|
||||||
|
import VoiceBars from "./components/VoiceBars";
|
||||||
|
import plugins from "./plugins";
|
||||||
|
|
||||||
|
function Core() {
|
||||||
|
const coreState = useSelector(selectCore);
|
||||||
|
const voiceState = useSelector(selectVoice);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const plugin = plugins[coreState.plugin];
|
||||||
|
const props = {
|
||||||
|
data: coreState.data,
|
||||||
|
close: () => dispatch(setPlugin(false))
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Box height="5vh" background="light-1" justify="center" direction="row">
|
||||||
|
{voiceState.recording ? <VoiceBars style={{height: "100%"}}/>:
|
||||||
|
voiceState.text === null ? null:
|
||||||
|
<Text style={{paddingTop: "1em"}}>{voiceState.text}</Text>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
{plugin ? React.createElement(plugin, props, null) : (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
>
|
||||||
|
<Clock size="xxlarge" />
|
||||||
|
<Clock size="xxlarge" type="digital" />
|
||||||
|
<Box direction="row" margin="1.5em" wrap={true}>
|
||||||
|
<Button onClick={() => dispatch(setPlugin("petMode"))} primary
|
||||||
|
label="Pet Mode" />
|
||||||
|
<Button onClick={() => dispatch(setPlugin("smartHome"))} primary
|
||||||
|
label="Smart Home" margin="0 0 0 1em" />
|
||||||
|
<Button onClick={() => dispatch(setPlugin("maps"))} primary
|
||||||
|
label="Maps" margin="0 0 0 1em" />
|
||||||
|
<Button onClick={() => dispatch(setPlugin("marketplace"))} primary
|
||||||
|
label="Marketplace" margin="0 0 0 1em" />
|
||||||
|
</Box>
|
||||||
|
</Box>)
|
||||||
|
}
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Core;
|
||||||
57
src/WebSocket.js
Normal file
57
src/WebSocket.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import React, { createContext } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import io from "socket.io-client";
|
||||||
|
import { WS_BASE } from "./config";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setData, setPlugin } from "./coreSlice";
|
||||||
|
import { setRecording, setText } from "./voiceSlice";
|
||||||
|
|
||||||
|
const WebSocketContext = createContext(null);
|
||||||
|
|
||||||
|
export { WebSocketContext };
|
||||||
|
|
||||||
|
function WebSocketProvider({ children }) {
|
||||||
|
let socket;
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
if (!socket) {
|
||||||
|
socket = io.connect(WS_BASE);
|
||||||
|
|
||||||
|
socket.on("switchPlugin", (payload) => {
|
||||||
|
dispatch(setData(payload.data || {}));
|
||||||
|
dispatch(setPlugin(payload.plugin));
|
||||||
|
if (payload.time) {
|
||||||
|
setTimeout(() => {
|
||||||
|
dispatch(setData({}));
|
||||||
|
dispatch(setPlugin(false));
|
||||||
|
}, payload.time);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("voice:wakeword", () => {
|
||||||
|
dispatch(setRecording(true));
|
||||||
|
dispatch(setText(null));
|
||||||
|
});
|
||||||
|
socket.on("voice:record_end", () => {
|
||||||
|
dispatch(setRecording(false));
|
||||||
|
dispatch(setText(null));
|
||||||
|
});
|
||||||
|
socket.on("voice:utterance", payload => {
|
||||||
|
dispatch(setRecording(false));
|
||||||
|
dispatch(setText(payload.text));
|
||||||
|
setTimeout(() => dispatch(setText(null)), 3000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WebSocketContext.Provider value={socket}>
|
||||||
|
{children}
|
||||||
|
</WebSocketContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketProvider.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WebSocketProvider;
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit";
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
import counterReducer from "../features/counter/counterSlice";
|
import coreReducer from "../coreSlice";
|
||||||
|
import voiceReducer from "../voiceSlice";
|
||||||
|
|
||||||
export default configureStore({
|
export default configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
counter: counterReducer,
|
core: coreReducer,
|
||||||
|
voice: voiceReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
79
src/components/VoiceBars.js
Normal file
79
src/components/VoiceBars.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function VoiceBars() {
|
||||||
|
return (
|
||||||
|
<div className="waves" style={{transform: "rotate(180deg)", marginTop: "130px", transition: "1s"}}>
|
||||||
|
<svg width="100vw" fill="none" version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop offset="0%" stopColor="#00B4DB" />
|
||||||
|
<stop offset="50%" stopColor="#224488" />
|
||||||
|
<stop offset="100%" stopColor="#0083B0" />
|
||||||
|
</linearGradient>
|
||||||
|
<path
|
||||||
|
fill="url(#grad1)"
|
||||||
|
d="
|
||||||
|
M0 67
|
||||||
|
C 273,183
|
||||||
|
822,-40
|
||||||
|
1920.00,106
|
||||||
|
|
||||||
|
V 359
|
||||||
|
H 0
|
||||||
|
V 67
|
||||||
|
Z">
|
||||||
|
<animate
|
||||||
|
repeatCount="indefinite"
|
||||||
|
fill="url(#grad1)"
|
||||||
|
attributeName="d"
|
||||||
|
dur="5s"
|
||||||
|
attributeType="XML"
|
||||||
|
values="
|
||||||
|
M0 77
|
||||||
|
C 473,283
|
||||||
|
822,-40
|
||||||
|
1920,116
|
||||||
|
|
||||||
|
V 359
|
||||||
|
H 0
|
||||||
|
V 67
|
||||||
|
Z;
|
||||||
|
|
||||||
|
M0 77
|
||||||
|
C 473,-40
|
||||||
|
1222,283
|
||||||
|
1920,136
|
||||||
|
|
||||||
|
V 359
|
||||||
|
H 0
|
||||||
|
V 67
|
||||||
|
Z;
|
||||||
|
|
||||||
|
M0 77
|
||||||
|
C 973,260
|
||||||
|
1722,-53
|
||||||
|
1920,120
|
||||||
|
|
||||||
|
V 359
|
||||||
|
H 0
|
||||||
|
V 67
|
||||||
|
Z;
|
||||||
|
|
||||||
|
M0 77
|
||||||
|
C 473,283
|
||||||
|
822,-40
|
||||||
|
1920,116
|
||||||
|
|
||||||
|
V 359
|
||||||
|
H 0
|
||||||
|
V 67
|
||||||
|
Z
|
||||||
|
">
|
||||||
|
</animate>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VoiceBars;
|
||||||
17
src/coreSlice.js
Normal file
17
src/coreSlice.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export const coreSlice = createSlice({
|
||||||
|
name: "core",
|
||||||
|
initialState: {
|
||||||
|
plugin: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
setPlugin: (state, action) => ({...state, plugin: action.payload}),
|
||||||
|
setData: (state, action) => ({...state, data: action.payload}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setPlugin, setData } = coreSlice.actions;
|
||||||
|
export const selectCore = state => state.core;
|
||||||
|
export default coreSlice.reducer;
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import {
|
|
||||||
decrement,
|
|
||||||
increment,
|
|
||||||
incrementByAmount,
|
|
||||||
incrementAsync,
|
|
||||||
selectCount,
|
|
||||||
} from "./counterSlice";
|
|
||||||
import styles from "./Counter.module.css";
|
|
||||||
|
|
||||||
export function Counter() {
|
|
||||||
const count = useSelector(selectCount);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [incrementAmount, setIncrementAmount] = useState("2");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Increment value"
|
|
||||||
onClick={() => dispatch(increment())}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<span className={styles.value}>{count}</span>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Decrement value"
|
|
||||||
onClick={() => dispatch(decrement())}
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<input
|
|
||||||
className={styles.textbox}
|
|
||||||
aria-label="Set increment amount"
|
|
||||||
value={incrementAmount}
|
|
||||||
onChange={e => setIncrementAmount(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
onClick={() => dispatch(incrementByAmount(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Amount
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className={styles.asyncButton}
|
|
||||||
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Async
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:not(:last-child) {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 78px;
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
margin-top: 2px;
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
font-size: 32px;
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 12px;
|
|
||||||
outline: none;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
color: rgb(112, 76, 182);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(112, 76, 182, 0.1);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textbox {
|
|
||||||
font-size: 32px;
|
|
||||||
padding: 2px;
|
|
||||||
width: 64px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover, .button:focus {
|
|
||||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:active {
|
|
||||||
background-color: rgba(112, 76, 182, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton {
|
|
||||||
composes: button;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:after {
|
|
||||||
content: "";
|
|
||||||
background-color: rgba(112, 76, 182, 0.15);
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:active:after {
|
|
||||||
width: 0%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: 0s
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
|
||||||
|
|
||||||
export const counterSlice = createSlice({
|
|
||||||
name: "counter",
|
|
||||||
initialState: {
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
reducers: {
|
|
||||||
increment: state => {
|
|
||||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
|
||||||
// doesn"t actually mutate the state because it uses the Immer library,
|
|
||||||
// which detects changes to a "draft state" and produces a brand new
|
|
||||||
// immutable state based off those changes
|
|
||||||
state.value += 1;
|
|
||||||
},
|
|
||||||
decrement: state => {
|
|
||||||
state.value -= 1;
|
|
||||||
},
|
|
||||||
incrementByAmount: (state, action) => {
|
|
||||||
state.value += action.payload;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
|
|
||||||
|
|
||||||
// The function below is called a thunk and allows us to perform async logic. It
|
|
||||||
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
|
|
||||||
// will call the thunk with the `dispatch` function as the first argument. Async
|
|
||||||
// code can then be executed and other actions can be dispatched
|
|
||||||
export const incrementAsync = amount => dispatch => {
|
|
||||||
setTimeout(() => {
|
|
||||||
dispatch(incrementByAmount(amount));
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The function below is called a selector and allows us to select a value from
|
|
||||||
// the state. Selectors can also be defined inline where they"re used instead of
|
|
||||||
// in the slice file. For example: `useSelector((state) => state.counter.value)`
|
|
||||||
export const selectCount = state => state.counter.value;
|
|
||||||
|
|
||||||
export default counterSlice.reducer;
|
|
||||||
85
src/plugins/Accident.js
Normal file
85
src/plugins/Accident.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { Box, Button, Heading, Text } from "grommet";
|
||||||
|
import { Aid, Impact } from "grommet-icons";
|
||||||
|
|
||||||
|
import Telegram from "../utils/telegram";
|
||||||
|
import { TG_API, TG_USERID } from "../config";
|
||||||
|
|
||||||
|
function sendNotification(close) {
|
||||||
|
if (window.sendingAccidentAlert)
|
||||||
|
return;
|
||||||
|
window.sendingAccidentAlert = true;
|
||||||
|
const bot = new Telegram(TG_API);
|
||||||
|
bot.sendMessage(TG_USERID, "User detected in an accident. Location has been attached below.");
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
position => {
|
||||||
|
window.sendingAccidentAlert = false;
|
||||||
|
bot.sendLocation( TG_USERID,
|
||||||
|
position.coords.latitude, position.coords.longitude);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
window.sendingAccidentAlert = false;
|
||||||
|
bot.sendMessage(TG_USERID, "Error retrieving location");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setTimeout(close, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Accident(props) {
|
||||||
|
const threshold = 20;
|
||||||
|
const [ time, setTime ] = useState(threshold);
|
||||||
|
const countRef = useRef(null);
|
||||||
|
|
||||||
|
function decrementTime(time) {
|
||||||
|
if (time === 0) {
|
||||||
|
clearInterval(countRef.current);
|
||||||
|
sendNotification(props.close);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return time - 1;
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
countRef.current = setInterval(() => setTime(decrementTime), 1000);
|
||||||
|
return () => clearInterval(countRef.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
clearTimeout(countRef.current);
|
||||||
|
props.close();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
time === 0 ?
|
||||||
|
<Aid color="plain" size="xlarge" />
|
||||||
|
:<Impact color="status-critical" size="xlarge" />
|
||||||
|
}
|
||||||
|
<Heading>{
|
||||||
|
time === 0 ?
|
||||||
|
"Calling Ambulance!"
|
||||||
|
:"Accident Detected!"
|
||||||
|
}</Heading>
|
||||||
|
<Text>{
|
||||||
|
time === 0 ? "Calling Ambulance":
|
||||||
|
`To cancel calling the ambulance, press the button below. (${time})`
|
||||||
|
}</Text>
|
||||||
|
<Button primary size="large" onClick={dismiss}
|
||||||
|
label="Dismiss" margin="1.5em" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Accident.propTypes = {
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Accident;
|
||||||
35
src/plugins/GenericPageWithIcon.js
Normal file
35
src/plugins/GenericPageWithIcon.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { Box, Button, Heading, Text } from "grommet";
|
||||||
|
|
||||||
|
function GenericPageWithIcon(props) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
style={{
|
||||||
|
padding: "0 3em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.icon}
|
||||||
|
<Heading>{props.title}</Heading>
|
||||||
|
<Text style={{ textAlign: "center" }}>{props.description}</Text>
|
||||||
|
<Button primary size="large" onClick={props.close}
|
||||||
|
label="Dismiss" margin="1.5em" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericPageWithIcon.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
description: PropTypes.string,
|
||||||
|
icon: PropTypes.node,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenericPageWithIcon;
|
||||||
23
src/plugins/Manual.js
Normal file
23
src/plugins/Manual.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import GenericPageWithIcon from "./GenericPageWithIcon";
|
||||||
|
import { StatusInfo } from "grommet-icons";
|
||||||
|
|
||||||
|
function Manual(props) {
|
||||||
|
return (
|
||||||
|
<GenericPageWithIcon
|
||||||
|
title={props.data.title}
|
||||||
|
description={props.data.description}
|
||||||
|
icon={<StatusInfo color="plain" size="xlarge" />}
|
||||||
|
close={props.close}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
Manual.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Manual.pluginName = "Manual";
|
||||||
|
|
||||||
|
export default Manual;
|
||||||
53
src/plugins/Maps.js
Normal file
53
src/plugins/Maps.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { Box, Button } from "grommet";
|
||||||
|
|
||||||
|
import { MAPS_API } from "../config";
|
||||||
|
|
||||||
|
function Maps(props) {
|
||||||
|
const [ location, setLocation ] = useState([null, null]);
|
||||||
|
useEffect(() => {
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
position => setLocation([position.coords.latitude, position.coords.longitude]),
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapsEmbedUrl = new URL("https://www.google.com/maps/embed/v1/view");
|
||||||
|
mapsEmbedUrl.searchParams.append("key", MAPS_API);
|
||||||
|
mapsEmbedUrl.searchParams.append("center", `${location[0]},${location[1]}`);
|
||||||
|
mapsEmbedUrl.searchParams.append("zoom", 14);
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
>
|
||||||
|
{location[0] === null ?
|
||||||
|
"Loading Location":
|
||||||
|
<iframe
|
||||||
|
width="650"
|
||||||
|
height="300"
|
||||||
|
frameBorder="0"
|
||||||
|
style={{ border: 0 }}
|
||||||
|
src={mapsEmbedUrl.href}>
|
||||||
|
</iframe>
|
||||||
|
}
|
||||||
|
<Button primary size="large" onClick={props.close}
|
||||||
|
label="Dismiss" margin="1.5em" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Maps.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Maps.pluginName = "Maps";
|
||||||
|
|
||||||
|
export default Maps;
|
||||||
67
src/plugins/Marketplace.js
Normal file
67
src/plugins/Marketplace.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { Box, Button, Card, CardBody, CardFooter, CardHeader, Heading } from "grommet";
|
||||||
|
import { InstallOption, Trash } from "grommet-icons";
|
||||||
|
|
||||||
|
|
||||||
|
function Marketplace(props) {
|
||||||
|
const plugins = [
|
||||||
|
{
|
||||||
|
name: "Maps",
|
||||||
|
description: "Google Maps is a mapping service. It offers satellite imagery, aerial photography, street maps, 360° interactive panoramic views of streets, real-time traffic conditions, and route planning for traveling.",
|
||||||
|
installed: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Smart Home",
|
||||||
|
description: "Smart Home Plugin lets you integrate with various well known smart devices allowing you to stream video from various devices.",
|
||||||
|
installed: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Spotify",
|
||||||
|
description: "With Spotify, you have access to a world of music. You can listen to artists and albums, or create your own playlist of your favourite songs.",
|
||||||
|
installed: false
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
>
|
||||||
|
<Box wrap="wrap" direction="row">
|
||||||
|
{
|
||||||
|
plugins.map(i =>
|
||||||
|
<Card key={i.name} style={{margin: "1em", width: "30%"}}>
|
||||||
|
<CardHeader pad="medium"><Heading>{i.name}</Heading></CardHeader>
|
||||||
|
<CardBody pad="medium">{i.description}</CardBody>
|
||||||
|
<CardFooter pad="medium" justify="center">
|
||||||
|
<Button
|
||||||
|
label={i.installed ? "Uninstall": "Install"}
|
||||||
|
icon={i.installed ?
|
||||||
|
<Trash color="status-error" /> :
|
||||||
|
<InstallOption color="status-ok" />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
<Button primary size="large" onClick={props.close}
|
||||||
|
label="Dismiss" margin="1.5em" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Marketplace.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Marketplace.pluginName = "Marketplace";
|
||||||
|
|
||||||
|
export default Marketplace;
|
||||||
59
src/plugins/PetMode.js
Normal file
59
src/plugins/PetMode.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import GenericPageWithIcon from "./GenericPageWithIcon";
|
||||||
|
import { Sun } from "grommet-icons";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
import Telegram from "../utils/telegram";
|
||||||
|
import { CAR_API, TG_API, TG_USERID } from "../config";
|
||||||
|
|
||||||
|
function sendNotification(temp) {
|
||||||
|
if (window.sendingPetAlert)
|
||||||
|
return;
|
||||||
|
window.sendingPetAlert = true;
|
||||||
|
const bot = new Telegram(TG_API);
|
||||||
|
bot.sendMessage(TG_USERID, `The temperature in the car is ${temp}°C. Please check your child/pet.`);
|
||||||
|
window.sendingPetAlert = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PetMode(props) {
|
||||||
|
const [ temp, setTemp ] = useState(null);
|
||||||
|
const [ , setNotified ] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
axios.get(`${CAR_API}data/InsideTemperature`)
|
||||||
|
.then(resp => setTemp(resp.data.value));
|
||||||
|
const id = setInterval(
|
||||||
|
() => axios.get(`${CAR_API}data/InsideTemperature`)
|
||||||
|
.then(resp => {
|
||||||
|
setTemp(resp.data.value);
|
||||||
|
if (resp.data.value > 35 || resp.data.value < 5) {
|
||||||
|
setNotified(notified => {
|
||||||
|
if (notified)
|
||||||
|
return true;
|
||||||
|
sendNotification(resp.data.value);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
if (temp === null)
|
||||||
|
return <></>;
|
||||||
|
return (
|
||||||
|
<GenericPageWithIcon
|
||||||
|
title={temp === null ? "Fetching temperature": `${temp}°C`}
|
||||||
|
description={`The temperature inside is ${temp}°C. The pet / child is ${temp > 35 ? "un": ""}safe.`}
|
||||||
|
icon={<Sun color={temp > 35 ? "status-critical": "plain"} size="xlarge" />}
|
||||||
|
close={props.close}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
PetMode.propTypes = {
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
PetMode.pluginName = "Temperature";
|
||||||
|
|
||||||
|
export default PetMode;
|
||||||
50
src/plugins/SmartHome.js
Normal file
50
src/plugins/SmartHome.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import React, {useState, useEffect} from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { Box, Button } from "grommet";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
import { CAMERA_URL, LIGHTS_URL } from "../config";
|
||||||
|
|
||||||
|
function SmartHome(props) {
|
||||||
|
const [ idx, setIdx ] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => setIdx(i => i + 1), 20);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
function turnOn() {
|
||||||
|
axios.get(`${LIGHTS_URL}/on`);
|
||||||
|
}
|
||||||
|
function turnOff() {
|
||||||
|
axios.get(`${LIGHTS_URL}/off`);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
align="center"
|
||||||
|
background="light-1"
|
||||||
|
width="100vw"
|
||||||
|
justify="center"
|
||||||
|
direction="column"
|
||||||
|
alignContent="center"
|
||||||
|
height="95vh"
|
||||||
|
>
|
||||||
|
<img src={`${CAMERA_URL}?id=${idx}`} />
|
||||||
|
<Box direction="row" margin="1em 0">
|
||||||
|
<Button primary size="large" onClick={turnOn}
|
||||||
|
label="Turn On Lights" margin="0 0 0 0" />
|
||||||
|
<Button primary size="large" onClick={turnOff}
|
||||||
|
label="Turn Off Lights" margin="0 0 0 1em" />
|
||||||
|
</Box>
|
||||||
|
<Button primary size="large" onClick={props.close}
|
||||||
|
label="Dismiss" margin="0" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartHome.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
SmartHome.pluginName = "Smart Home";
|
||||||
|
|
||||||
|
export default SmartHome;
|
||||||
23
src/plugins/Temperature.js
Normal file
23
src/plugins/Temperature.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import GenericPageWithIcon from "./GenericPageWithIcon";
|
||||||
|
import { Sun } from "grommet-icons";
|
||||||
|
|
||||||
|
function Temperature(props) {
|
||||||
|
return (
|
||||||
|
<GenericPageWithIcon
|
||||||
|
title={props.data.temperature}
|
||||||
|
description={`The current temperature outside is ${props.data.temperature}℃`}
|
||||||
|
icon={<Sun color="plain" size="xlarge" />}
|
||||||
|
close={props.close}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
Temperature.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Temperature.pluginName = "Temperature";
|
||||||
|
|
||||||
|
export default Temperature;
|
||||||
23
src/plugins/Warning.js
Normal file
23
src/plugins/Warning.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import GenericPageWithIcon from "./GenericPageWithIcon";
|
||||||
|
import { Alert } from "grommet-icons";
|
||||||
|
|
||||||
|
function Warning(props) {
|
||||||
|
return (
|
||||||
|
<GenericPageWithIcon
|
||||||
|
title={props.data.title}
|
||||||
|
description={props.data.description}
|
||||||
|
icon={<Alert color="status-critical" size="xlarge" />}
|
||||||
|
close={props.close}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
Warning.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Warning.pluginName = "Warning";
|
||||||
|
|
||||||
|
export default Warning;
|
||||||
19
src/plugins/index.js
Normal file
19
src/plugins/index.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Warning from "./Warning";
|
||||||
|
import Manual from "./Manual";
|
||||||
|
import Temperature from "./Temperature";
|
||||||
|
import Accident from "./Accident";
|
||||||
|
import PetMode from "./PetMode";
|
||||||
|
import SmartHome from "./SmartHome";
|
||||||
|
import Maps from "./Maps";
|
||||||
|
import Marketplace from "./Marketplace";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
warning: Warning,
|
||||||
|
manual: Manual,
|
||||||
|
temperature: Temperature,
|
||||||
|
accident: Accident,
|
||||||
|
petMode: PetMode,
|
||||||
|
smartHome: SmartHome,
|
||||||
|
maps: Maps,
|
||||||
|
marketplace: Marketplace,
|
||||||
|
};
|
||||||
9
src/sample.config.js
Normal file
9
src/sample.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const WS_BASE = "http://localhost:5050/";
|
||||||
|
const TG_API = "xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxx";
|
||||||
|
const TG_USERID = 123456789;
|
||||||
|
const CAR_API = "http://localhost:5000/";
|
||||||
|
const CAMERA_URL = "http://path.to/still/image.jpg";
|
||||||
|
const MAPS_API = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
|
||||||
|
const LIGHTS_URL = "http://192.168.1.50:5000/";
|
||||||
|
|
||||||
|
export { WS_BASE, TG_API, TG_USERID, CAR_API, CAMERA_URL, MAPS_API, LIGHTS_URL };
|
||||||
0
src/utils/index.js
Normal file
0
src/utils/index.js
Normal file
19
src/utils/telegram.js
Normal file
19
src/utils/telegram.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
class Telegram {
|
||||||
|
constructor(token) {
|
||||||
|
this.axios = axios.create({
|
||||||
|
baseURL: `https://api.telegram.org/bot${token}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(chat_id, text) {
|
||||||
|
this.axios.post("/sendMessage", {chat_id, text});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendLocation(chat_id, latitude, longitude) {
|
||||||
|
this.axios.post("/sendLocation", {chat_id, latitude, longitude});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Telegram;
|
||||||
17
src/voiceSlice.js
Normal file
17
src/voiceSlice.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export const voiceSlice = createSlice({
|
||||||
|
name: "voice",
|
||||||
|
initialState: {
|
||||||
|
recording: false,
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
setRecording: (state, action) => ({...state, recording: action.payload}),
|
||||||
|
setText: (state, action) => ({...state, text: action.payload}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setRecording, setText } = voiceSlice.actions;
|
||||||
|
export const selectVoice = state => state.voice;
|
||||||
|
export default voiceSlice.reducer;
|
||||||
114
yarn.lock
114
yarn.lock
@@ -1735,6 +1735,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.3.0"
|
"@babel/types" "^7.3.0"
|
||||||
|
|
||||||
|
"@types/component-emitter@^1.2.10":
|
||||||
|
version "1.2.10"
|
||||||
|
resolved "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea"
|
||||||
|
integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==
|
||||||
|
|
||||||
"@types/eslint@^7.2.4":
|
"@types/eslint@^7.2.4":
|
||||||
version "7.2.6"
|
version "7.2.6"
|
||||||
resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c"
|
resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c"
|
||||||
@@ -2585,6 +2590,13 @@ axe-core@^4.0.2:
|
|||||||
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf"
|
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf"
|
||||||
integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==
|
integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==
|
||||||
|
|
||||||
|
axios@^0.21.1:
|
||||||
|
version "0.21.1"
|
||||||
|
resolved "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
axobject-query@^2.2.0:
|
axobject-query@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||||
@@ -2769,11 +2781,21 @@ babylon@^6.18.0:
|
|||||||
resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
||||||
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
|
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
|
||||||
|
|
||||||
|
backo2@1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||||
|
integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-arraybuffer@0.1.4:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
||||||
|
integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
|
||||||
|
|
||||||
base64-js@^1.0.2:
|
base64-js@^1.0.2:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
@@ -3456,7 +3478,12 @@ commondir@^1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||||
|
|
||||||
component-emitter@^1.2.1:
|
component-bind@1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
||||||
|
integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=
|
||||||
|
|
||||||
|
component-emitter@^1.2.1, component-emitter@~1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||||
@@ -4012,6 +4039,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@~4.1.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||||
|
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||||
|
dependencies:
|
||||||
|
ms "^2.1.1"
|
||||||
|
|
||||||
decamelize@^1.2.0:
|
decamelize@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
@@ -4401,6 +4435,29 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
|
engine.io-client@~4.0.0:
|
||||||
|
version "4.0.5"
|
||||||
|
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.5.tgz#e12b05a11a7a3cccec6d69f9af8435146e3d507e"
|
||||||
|
integrity sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer "0.1.4"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
engine.io-parser "~4.0.1"
|
||||||
|
has-cors "1.1.0"
|
||||||
|
parseqs "0.0.6"
|
||||||
|
parseuri "0.0.6"
|
||||||
|
ws "~7.2.1"
|
||||||
|
xmlhttprequest-ssl "~1.5.4"
|
||||||
|
yeast "0.1.2"
|
||||||
|
|
||||||
|
engine.io-parser@~4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz#e41d0b3fb66f7bf4a3671d2038a154024edb501e"
|
||||||
|
integrity sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer "0.1.4"
|
||||||
|
|
||||||
enhanced-resolve@^4.3.0:
|
enhanced-resolve@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126"
|
resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126"
|
||||||
@@ -5154,7 +5211,7 @@ flush-write-stream@^1.0.0:
|
|||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
|
||||||
version "1.13.1"
|
version "1.13.1"
|
||||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||||
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
||||||
@@ -5492,6 +5549,11 @@ harmony-reflect@^1.4.6:
|
|||||||
resolved "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
|
resolved "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
|
||||||
integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==
|
integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==
|
||||||
|
|
||||||
|
has-cors@1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
|
||||||
|
integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
has-flag@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
@@ -8033,6 +8095,16 @@ parse5@5.1.1:
|
|||||||
resolved "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
|
resolved "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
|
||||||
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
|
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
|
||||||
|
|
||||||
|
parseqs@0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5"
|
||||||
|
integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==
|
||||||
|
|
||||||
|
parseuri@0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a"
|
||||||
|
integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==
|
||||||
|
|
||||||
parseurl@~1.3.2, parseurl@~1.3.3:
|
parseurl@~1.3.2, parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
@@ -10105,6 +10177,29 @@ snapdragon@^0.8.1:
|
|||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.0"
|
use "^3.1.0"
|
||||||
|
|
||||||
|
socket.io-client@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.4.tgz#c0203419a9f71e1360ef92a31301e80260e94bb9"
|
||||||
|
integrity sha512-qMvBuS+W9JIN2mkfAWDCxuIt+jpIKDf8C0604zEqx1JrPaPSS6cN0F3B2GYWC83TqBeVJXW66GFxWV3KD88n0Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/component-emitter" "^1.2.10"
|
||||||
|
backo2 "1.0.2"
|
||||||
|
component-bind "1.0.0"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
engine.io-client "~4.0.0"
|
||||||
|
parseuri "0.0.6"
|
||||||
|
socket.io-parser "~4.0.1"
|
||||||
|
|
||||||
|
socket.io-parser@~4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz#3d021a9c86671bb079e7c6c806db6a1d9b1bc780"
|
||||||
|
integrity sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==
|
||||||
|
dependencies:
|
||||||
|
"@types/component-emitter" "^1.2.10"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
|
||||||
sockjs-client@1.4.0:
|
sockjs-client@1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
|
resolved "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
|
||||||
@@ -11620,6 +11715,11 @@ ws@^7.2.3:
|
|||||||
resolved "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
|
resolved "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
|
||||||
integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
|
integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
|
||||||
|
|
||||||
|
ws@~7.2.1:
|
||||||
|
version "7.2.5"
|
||||||
|
resolved "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz#abb1370d4626a5a9cd79d8de404aa18b3465d10d"
|
||||||
|
integrity sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
@@ -11630,6 +11730,11 @@ xmlchars@^2.2.0:
|
|||||||
resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||||
|
|
||||||
|
xmlhttprequest-ssl@~1.5.4:
|
||||||
|
version "1.5.5"
|
||||||
|
resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
||||||
|
integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
@@ -11704,6 +11809,11 @@ yargs@^15.4.1:
|
|||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
yargs-parser "^18.1.2"
|
yargs-parser "^18.1.2"
|
||||||
|
|
||||||
|
yeast@0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
||||||
|
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
|
||||||
|
|
||||||
yocto-queue@^0.1.0:
|
yocto-queue@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
|
|||||||
Reference in New Issue
Block a user