Use a custom pub-sub system to handle receiving web socket responses
This commit is contained in:
parent
ab0161e0fa
commit
25230a35f7
13
package.json
13
package.json
@ -7,8 +7,9 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"appId": "net.timshomepage.film-exif",
|
"appId": "net.timshomepage.film-exif",
|
||||||
"files": [
|
"files": [
|
||||||
"build/*",
|
"build/**/*",
|
||||||
"node_modules/**/*"
|
"node_modules/**/*",
|
||||||
|
"src/**/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -18,9 +19,8 @@
|
|||||||
"inferno": "^5.0.1",
|
"inferno": "^5.0.1",
|
||||||
"inferno-bootstrap": "^5.0.0",
|
"inferno-bootstrap": "^5.0.0",
|
||||||
"inferno-dev-utils": "^5.3.0",
|
"inferno-dev-utils": "^5.3.0",
|
||||||
"inferno-redux": "^5.0.4",
|
|
||||||
"inferno-router": "^5.0.1",
|
"inferno-router": "^5.0.1",
|
||||||
"redux": "^4.0.0",
|
"lodash": "^4.17.10",
|
||||||
"sqlite3": "^4.0.0",
|
"sqlite3": "^4.0.0",
|
||||||
"ws": "^5.1.1"
|
"ws": "^5.1.1"
|
||||||
},
|
},
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"raf": "^3.4.0"
|
"raf": "^3.4.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "parcel build index.html --out-dir build --detailed-report",
|
"build": "parcel build index.html --out-dir build/app --detailed-report",
|
||||||
"dist": "yarn run build && build",
|
"dist": "yarn run build && build",
|
||||||
"electron-start": "node src/electron/wait-inferno",
|
"electron-start": "node src/electron/wait-inferno",
|
||||||
"electron": "electron .",
|
"electron": "electron .",
|
||||||
@ -69,6 +69,9 @@
|
|||||||
},
|
},
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
"main": "src/electron/index.js",
|
"main": "src/electron/index.js",
|
||||||
|
"mac": {
|
||||||
|
"category": "public.app-category.photography"
|
||||||
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"src/**/*.{js,jsx,mjs}"
|
"src/**/*.{js,jsx,mjs}"
|
||||||
|
@ -11,7 +11,7 @@ require('raf').polyfill(global);
|
|||||||
// ignoring them. In the future, promise rejections that are not handled will
|
// ignoring them. In the future, promise rejections that are not handled will
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
// terminate the Node.js process with a non-zero exit code.
|
||||||
process.on('unhandledRejection', err => {
|
process.on('unhandledRejection', err => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const jest = require('jest');
|
const jest = require('jest');
|
||||||
|
93
src/App.js
93
src/App.js
@ -1,7 +1,6 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { Component } from 'inferno';
|
import { Component } from 'inferno';
|
||||||
import { BrowserRouter, Link, NavLink } from 'inferno-router';
|
import { BrowserRouter, Link, NavLink } from 'inferno-router';
|
||||||
import { Loader } from '//components';
|
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Nav,
|
Nav,
|
||||||
@ -11,72 +10,26 @@ import {
|
|||||||
} from '//components/Bootstrap';
|
} from '//components/Bootstrap';
|
||||||
import { Routes } from '//Routes';
|
import { Routes } from '//Routes';
|
||||||
|
|
||||||
export class App extends Component {
|
export const App = () => {
|
||||||
constructor (props) {
|
return (
|
||||||
super(props);
|
<BrowserRouter>
|
||||||
|
<Container className="full-height" tag="bs-container">
|
||||||
this.state = {
|
<Navbar className="static-top" color="dark" dark expandable="sm">
|
||||||
webSocketLoaded: false,
|
<NavbarBrand to="/" tag={Link}>Film Exif</NavbarBrand>
|
||||||
};
|
<Nav fill pills>
|
||||||
|
<NavItem>
|
||||||
_.bindAll(this, [
|
<NavLink className="nav-link" to="/camera/list">Cameras</NavLink>
|
||||||
'onWebSocketOpen',
|
</NavItem>
|
||||||
'onWebSocketClose',
|
<NavItem>
|
||||||
]);
|
<NavLink className="nav-link" to="/film/add">Films</NavLink>
|
||||||
}
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
componentDidMount () {
|
<NavLink className="nav-link" to="/oops">Oops</NavLink>
|
||||||
window.clientWS = new WebSocket('ws://localhost:65432/');
|
</NavItem>
|
||||||
|
</Nav>
|
||||||
window.clientWS.addEventListener('open', this.onWebSocketOpen);
|
</Navbar>
|
||||||
window.clientWS.addEventListener('message', console);
|
<Routes />
|
||||||
window.clientWS.addEventListener('close', this.onWebSocketClose);
|
</Container>
|
||||||
|
</BrowserRouter>
|
||||||
console.info(this.context);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (window.clientWS) {
|
|
||||||
window.clientWS.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onWebSocketOpen () {
|
|
||||||
this.setState({
|
|
||||||
webSocketLoaded: true
|
|
||||||
});
|
|
||||||
|
|
||||||
window.clientWS.onmessage = message => {
|
|
||||||
console.info(JSON.parse(message.data));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onWebSocketClose () {
|
|
||||||
console.log('WebSocket closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<BrowserRouter>
|
|
||||||
<Container className="full-height" tag="bs-container">
|
|
||||||
<Navbar className="static-top" color="dark" dark expandable="sm">
|
|
||||||
<NavbarBrand to="/" tag={Link}>Film Exif</NavbarBrand>
|
|
||||||
<Nav fill pills>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink className="nav-link" to="/camera/list">Cameras</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink className="nav-link" to="/film/add">Films</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink className="nav-link" to="/oops">Oops</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
</Nav>
|
|
||||||
</Navbar>
|
|
||||||
<Routes />
|
|
||||||
<Loader title="Connecting to WebSocket" hidden={this.state.webSocketLoaded} />
|
|
||||||
</Container>
|
|
||||||
</BrowserRouter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
OopsView,
|
OopsView,
|
||||||
} from '//views';
|
} from '//views';
|
||||||
|
|
||||||
export const Routes = (props) => (
|
export const Routes = () => (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" component={HomeView} />
|
<Route exact path="/" component={HomeView} />
|
||||||
<Route path="/camera" component={CameraAddView} />
|
<Route path="/camera" component={CameraAddView} />
|
||||||
|
11
src/index.js
11
src/index.js
@ -3,11 +3,10 @@ import { Provider } from 'inferno-redux';
|
|||||||
|
|
||||||
import configureStore from './configureStore';
|
import configureStore from './configureStore';
|
||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
|
import WSCache from './wsCache';
|
||||||
|
|
||||||
const store = configureStore();
|
const WEB_SOCKET = new WebSocket('ws://localhost:65432/');
|
||||||
|
window.clientWS = WEB_SOCKET;
|
||||||
|
window.wsCache = new WSCache(WEB_SOCKET);
|
||||||
|
|
||||||
render((
|
render(<App />, document.getElementById('app'));
|
||||||
<Provider store={store}>
|
|
||||||
<App />
|
|
||||||
</Provider>
|
|
||||||
), document.getElementById('app'));
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
export * from '//reducers/receiveReducer';
|
|
||||||
export * from '//reducers/sendReducer';
|
|
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Reducer for received websocket messages
|
|
||||||
*
|
|
||||||
* @param {object} state
|
|
||||||
* @param {object} action
|
|
||||||
* @return {object} newState
|
|
||||||
*/
|
|
||||||
export const receiveReducer = (state = {}, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Reducer for websocket-based actions
|
|
||||||
*
|
|
||||||
* @param {object} state
|
|
||||||
* @param {object} action
|
|
||||||
* @return {object} newState
|
|
||||||
*/
|
|
||||||
export const sendReducer = (state = {}, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
@ -10,7 +10,6 @@ import {
|
|||||||
ModalHeader,
|
ModalHeader,
|
||||||
Row,
|
Row,
|
||||||
} from '//components/Bootstrap';
|
} from '//components/Bootstrap';
|
||||||
import { JSONMessage } from '//helpers/web-socket';
|
|
||||||
|
|
||||||
export class HomeView extends Component {
|
export class HomeView extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@ -40,22 +39,22 @@ export class HomeView extends Component {
|
|||||||
const newTransfer = { ...e.dataTransfer };
|
const newTransfer = { ...e.dataTransfer };
|
||||||
console.info(newTransfer);
|
console.info(newTransfer);
|
||||||
|
|
||||||
window.clientWS.send(JSONMessage('dropped-files', draggedFiles));
|
window.wsCache.sendJSON('dropped-files', draggedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
showErrorDialog () {
|
showErrorDialog () {
|
||||||
window.clientWS.send(JSONMessage(
|
window.wsCache.sendJSON(
|
||||||
'show-error-box',
|
'show-error-box',
|
||||||
'Looks like there was a problem. (╥﹏╥) \n (╯°□°)╯︵ ┻━┻'
|
'Looks like there was a problem. (╥﹏╥) \n (╯°□°)╯︵ ┻━┻'
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
showOpenDialog () {
|
showOpenDialog () {
|
||||||
window.clientWS.send(JSONMessage('show-open-dialog', {}));
|
window.wsCache.sendJSON('show-open-dialog');
|
||||||
}
|
}
|
||||||
|
|
||||||
showSaveDialog () {
|
showSaveDialog () {
|
||||||
window.clientWS.send(JSONMessage('show-save-dialog', {}));
|
window.wsCache.sendJSON('show-save-dialog');
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleErrorModal () {
|
toggleErrorModal () {
|
||||||
|
138
src/wsCache.js
Normal file
138
src/wsCache.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import * as _ from 'lodash'
|
||||||
|
import { JSONMessage } from '//helpers/web-socket';
|
||||||
|
|
||||||
|
export class wsCache {
|
||||||
|
constructor (ws) {
|
||||||
|
this.ws = ws
|
||||||
|
|
||||||
|
this.ws.addEventListener('open', this.onWebSocketOpen);
|
||||||
|
this.ws.addEventListener('message', this.onWebSocketMessage);
|
||||||
|
this.ws.addEventListener('close', this.onWebSocketClose);
|
||||||
|
|
||||||
|
// Websocket channels
|
||||||
|
// These hold previous messages if they are needed later
|
||||||
|
this.slots = {
|
||||||
|
'default': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send messages
|
||||||
|
this.sent = {
|
||||||
|
'default': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribers
|
||||||
|
this.listeners = {
|
||||||
|
'default': [console.info],
|
||||||
|
}
|
||||||
|
|
||||||
|
_.bindAll(this, [
|
||||||
|
'onWebSocketOpen',
|
||||||
|
'onWebSocketClose',
|
||||||
|
'onWebSocketMessage',
|
||||||
|
'publish',
|
||||||
|
'send',
|
||||||
|
'sendJSON',
|
||||||
|
'subscribe',
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
onWebSocketOpen () {
|
||||||
|
window.wsCache.publish('default', 'Websocket opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
onWebSocketClose () {
|
||||||
|
console.info('WebSocket closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for receiving a websocket message
|
||||||
|
*
|
||||||
|
* @param {mixed} message
|
||||||
|
*/
|
||||||
|
onWebSocketMessage (message) {
|
||||||
|
try {
|
||||||
|
const messageObject = JSON.parse(message.data);
|
||||||
|
const [slot, data] = messageObject;
|
||||||
|
window.wsCache.publish(slot, data);
|
||||||
|
} catch (e) {
|
||||||
|
window.wsCache.publish('default', message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a recieved websocket message to the appropriate listener(s)
|
||||||
|
*
|
||||||
|
* @param {string} slot
|
||||||
|
* @param {mixed} data
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
publish (slot, data) {
|
||||||
|
if (!this.listeners[slot] || data === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slots[slot].push(data);
|
||||||
|
|
||||||
|
this.listeners[slot].forEach(listener => {
|
||||||
|
listener(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.info(this.slots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to the websocket server
|
||||||
|
*
|
||||||
|
* @param {mixed} message
|
||||||
|
*/
|
||||||
|
send (message) {
|
||||||
|
this.sent['default'].push(message);
|
||||||
|
|
||||||
|
return this.ws.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a JSON-encoded message to the websocket server
|
||||||
|
*
|
||||||
|
* @param {string} slot
|
||||||
|
* @param {mixed} data
|
||||||
|
*/
|
||||||
|
sendJSON (slot, data = {}) {
|
||||||
|
const sentSlots = Object.keys(this.sent);
|
||||||
|
|
||||||
|
if (!sentSlots.includes(slot)) {
|
||||||
|
this.sent[slot] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sent[slot].push(data);
|
||||||
|
|
||||||
|
return this.ws.send(JSONMessage(slot, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to a websocket message type
|
||||||
|
*
|
||||||
|
* Returns an object with a `unsubscribe` method
|
||||||
|
*
|
||||||
|
* @param {string} slot
|
||||||
|
* @param {function} cb
|
||||||
|
*/
|
||||||
|
subscribe (slot, cb) {
|
||||||
|
const slots = Object.keys(this.slots);
|
||||||
|
// Create the slots and listener arrays
|
||||||
|
if (!slots.includes(slot)) {
|
||||||
|
this.slots[slot] = [];
|
||||||
|
this.listeners[slot] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const listenerIndex = this.listeners[slot].push(cb) -1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
remove: () => {
|
||||||
|
delete this.listeners[slot][listenerIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default wsCache;
|
22
yarn.lock
22
yarn.lock
@ -4030,13 +4030,6 @@ inferno-popper@^5.0.0:
|
|||||||
is-equal-shallow "^0.1.3"
|
is-equal-shallow "^0.1.3"
|
||||||
popper.js "^1.10.8"
|
popper.js "^1.10.8"
|
||||||
|
|
||||||
inferno-redux@^5.0.4:
|
|
||||||
version "5.0.5"
|
|
||||||
resolved "https://registry.npmjs.org/inferno-redux/-/inferno-redux-5.0.5.tgz#8d7eca55f324fd7d24c86ac7eaac89d7cd7f54ef"
|
|
||||||
dependencies:
|
|
||||||
hoist-non-inferno-statics "^1.1.3"
|
|
||||||
inferno-shared "5.0.5"
|
|
||||||
|
|
||||||
inferno-router@^5.0.1:
|
inferno-router@^5.0.1:
|
||||||
version "5.0.5"
|
version "5.0.5"
|
||||||
resolved "https://registry.npmjs.org/inferno-router/-/inferno-router-5.0.5.tgz#3fb68a63e6fa4c9b27b7b87861b1c3fca67015b9"
|
resolved "https://registry.npmjs.org/inferno-router/-/inferno-router-5.0.5.tgz#3fb68a63e6fa4c9b27b7b87861b1c3fca67015b9"
|
||||||
@ -5072,7 +5065,7 @@ lodash.uniq@^4.5.0:
|
|||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
|
||||||
lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0:
|
lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0:
|
||||||
version "4.17.10"
|
version "4.17.10"
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||||
|
|
||||||
@ -5080,7 +5073,7 @@ longest@^1.0.1:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
|
loose-envify@^1.0.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -6530,13 +6523,6 @@ reduce-function-call@^1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match "^0.4.2"
|
balanced-match "^0.4.2"
|
||||||
|
|
||||||
redux@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03"
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
symbol-observable "^1.2.0"
|
|
||||||
|
|
||||||
regenerate-unicode-properties@^6.0.0:
|
regenerate-unicode-properties@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-6.0.0.tgz#0fc26f9d5142289df4e177dec58f303d2d097c16"
|
resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-6.0.0.tgz#0fc26f9d5142289df4e177dec58f303d2d097c16"
|
||||||
@ -7370,10 +7356,6 @@ symbol-observable@1.0.1:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
|
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
|
||||||
|
|
||||||
symbol-observable@^1.2.0:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
|
||||||
|
|
||||||
symbol-tree@^3.2.2:
|
symbol-tree@^3.2.2:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
|
resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
|
||||||
|
Loading…
Reference in New Issue
Block a user