Do you wish to register an account?
Browse Source

Use a custom pub-sub system to handle receiving web socket responses

master
Timothy Warren 1 year ago
parent
commit
25230a35f7
11 changed files with 183 additions and 137 deletions
  1. +8
    -5
      package.json
  2. +1
    -1
      scripts/test.js
  3. +23
    -70
      src/App.js
  4. +1
    -1
      src/Routes.js
  5. +5
    -6
      src/index.js
  6. +0
    -2
      src/reducers/index.js
  7. +0
    -13
      src/reducers/receiveReducer.js
  8. +0
    -13
      src/reducers/sendReducer.js
  9. +5
    -6
      src/views/HomeView.js
  10. +138
    -0
      src/wsCache.js
  11. +2
    -20
      yarn.lock

+ 8
- 5
package.json View File

@@ -7,8 +7,9 @@
"build": {
"appId": "net.timshomepage.film-exif",
"files": [
"build/*",
"node_modules/**/*"
"build/**/*",
"node_modules/**/*",
"src/**/*"
]
},
"dependencies": {
@@ -18,9 +19,8 @@
"inferno": "^5.0.1",
"inferno-bootstrap": "^5.0.0",
"inferno-dev-utils": "^5.3.0",
"inferno-redux": "^5.0.4",
"inferno-router": "^5.0.1",
"redux": "^4.0.0",
"lodash": "^4.17.10",
"sqlite3": "^4.0.0",
"ws": "^5.1.1"
},
@@ -54,7 +54,7 @@
"raf": "^3.4.0"
},
"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",
"electron-start": "node src/electron/wait-inferno",
"electron": "electron .",
@@ -69,6 +69,9 @@
},
"homepage": "./",
"main": "src/electron/index.js",
"mac": {
"category": "public.app-category.photography"
},
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx,mjs}"

+ 1
- 1
scripts/test.js View File

@@ -11,7 +11,7 @@ require('raf').polyfill(global);
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
throw err;
});

const jest = require('jest');

+ 23
- 70
src/App.js View File

@@ -1,7 +1,6 @@
import * as _ from 'lodash';
import { Component } from 'inferno';
import { BrowserRouter, Link, NavLink } from 'inferno-router';
import { Loader } from '//components';
import {
Container,
Nav,
@@ -11,72 +10,26 @@ import {
} from '//components/Bootstrap';
import { Routes } from '//Routes';

export class App extends Component {
constructor (props) {
super(props);

this.state = {
webSocketLoaded: false,
};

_.bindAll(this, [
'onWebSocketOpen',
'onWebSocketClose',
]);
}

componentDidMount () {
window.clientWS = new WebSocket('ws://localhost:65432/');

window.clientWS.addEventListener('open', this.onWebSocketOpen);
window.clientWS.addEventListener('message', console);
window.clientWS.addEventListener('close', this.onWebSocketClose);

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>
);
}
}
export const App = () => {
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 />
</Container>
</BrowserRouter>
);
};

+ 1
- 1
src/Routes.js View File

@@ -7,7 +7,7 @@ import {
OopsView,
} from '//views';

export const Routes = (props) => (
export const Routes = () => (
<Switch>
<Route exact path="/" component={HomeView} />
<Route path="/camera" component={CameraAddView} />

+ 5
- 6
src/index.js View File

@@ -3,11 +3,10 @@ import { Provider } from 'inferno-redux';

import configureStore from './configureStore';
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((
<Provider store={store}>
<App />
</Provider>
), document.getElementById('app'));
render(<App />, document.getElementById('app'));

+ 0
- 2
src/reducers/index.js View File

@@ -1,2 +0,0 @@
export * from '//reducers/receiveReducer';
export * from '//reducers/sendReducer';

+ 0
- 13
src/reducers/receiveReducer.js View File

@@ -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;
}
};

+ 0
- 13
src/reducers/sendReducer.js View File

@@ -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;
}
};

+ 5
- 6
src/views/HomeView.js View File

@@ -10,7 +10,6 @@ import {
ModalHeader,
Row,
} from '//components/Bootstrap';
import { JSONMessage } from '//helpers/web-socket';

export class HomeView extends Component {
constructor (props) {
@@ -40,22 +39,22 @@ export class HomeView extends Component {
const newTransfer = { ...e.dataTransfer };
console.info(newTransfer);

window.clientWS.send(JSONMessage('dropped-files', draggedFiles));
window.wsCache.sendJSON('dropped-files', draggedFiles);
}

showErrorDialog () {
window.clientWS.send(JSONMessage(
window.wsCache.sendJSON(
'show-error-box',
'Looks like there was a problem. (╥﹏╥) \n (╯°□°)╯︵ ┻━┻'
));
);
}

showOpenDialog () {
window.clientWS.send(JSONMessage('show-open-dialog', {}));
window.wsCache.sendJSON('show-open-dialog');
}

showSaveDialog () {
window.clientWS.send(JSONMessage('show-save-dialog', {}));
window.wsCache.sendJSON('show-save-dialog');
}

toggleErrorModal () {

+ 138
- 0
src/wsCache.js View 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;

+ 2
- 20
yarn.lock View File

@@ -4030,13 +4030,6 @@ inferno-popper@^5.0.0:
is-equal-shallow "^0.1.3"
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:
version "5.0.5"
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"
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"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"

@@ -5080,7 +5073,7 @@ longest@^1.0.1:
version "1.0.1"
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"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -6530,13 +6523,6 @@ reduce-function-call@^1.0.1:
dependencies:
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:
version "6.0.0"
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"
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:
version "3.2.2"
resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"

Loading…
Cancel
Save