From c0917f89bfe397b904f48af25393e2454b98a5d8 Mon Sep 17 00:00:00 2001 From: Jo Jordens Date: Tue, 6 Mar 2018 14:41:41 +0100 Subject: [PATCH] support sending message arguments of different types - fixes #25 --- socketTest/index.js | 15 +- src/app/css/components/_column.scss | 7 + .../components/column/messageSender/Editor.js | 96 ++++++++++++ .../column/messageSender/MessageSenderView.js | 148 +++++++++++++----- src/app/js/index.js | 1 - src/app/js/socketManager.js | 21 ++- 6 files changed, 246 insertions(+), 42 deletions(-) create mode 100644 src/app/js/components/column/messageSender/Editor.js diff --git a/socketTest/index.js b/socketTest/index.js index 917daeb..c917f91 100644 --- a/socketTest/index.js +++ b/socketTest/index.js @@ -7,15 +7,19 @@ app.get('/', function(req, res){ res.sendFile(path.join(__dirname, 'index.html')); }); +function getTypeOf (value) { + return Object.prototype.toString.call(value).slice(8, -1) +} + io.on('connection', function(socket){ console.log('a user connected'); // socket.join('testRoom') socket.on('disconnect', function(){ console.log('user disconnected'); }); - socket.on('a', (msg) => { - console.log(msg) - console.log(Object.prototype.toString.apply(msg).slice(8, -1)) + socket.on('a', function () { + console.log('all message arguments:', ...[].slice.call(arguments)) + console.log('types', [].map.call(arguments, arg => getTypeOf(arg))) }) socket.on('chat message', function(){ console.log('message:', ...[].slice.call(arguments)); @@ -44,6 +48,11 @@ function startIntervals () { io.emit('test2', true); }, 2000); + setInterval(function () { + x++; + io.emit('test5', true); + }, 5000); + setInterval(function () { y++; nsp.emit('test', 'nsp - message'+x); diff --git a/src/app/css/components/_column.scss b/src/app/css/components/_column.scss index df16afc..cfad224 100644 --- a/src/app/css/components/_column.scss +++ b/src/app/css/components/_column.scss @@ -139,6 +139,13 @@ .CodeMirror { height: 200px; } + &.red-border { + border-color: red; + } +} + +.column-editor-no-border { + margin-bottom: 8px; } .column-block-group { diff --git a/src/app/js/components/column/messageSender/Editor.js b/src/app/js/components/column/messageSender/Editor.js new file mode 100644 index 0000000..76a3cf4 --- /dev/null +++ b/src/app/js/components/column/messageSender/Editor.js @@ -0,0 +1,96 @@ +import React, { Component } from 'react' +import { Controlled as CodeMirror } from 'react-codemirror2' +import 'codemirror/mode/javascript/javascript' + +const Editor = (props) => { + const type = props.message.type + let editor + switch (type) { + case 'String': + editor = + break + + case 'Number': + editor = + break + + case 'Boolean': + editor = + break + + case 'JSON': + case 'Array': + case 'Object': + editor = + break + + default: + console.warn('unhandled message type', type) + editor =

unhandled message type {type}

+ } + return editor +} + +class TextInput extends Component { + render () { + const { message, handleMessageChange } = this.props + return ( + handleMessageChange(value) } + /> + ) + } +} + +class NumberInput extends Component { + render () { + const { message, handleMessageChange } = this.props + return ( +
+ + handleMessageChange(e.target.value) } + /> +
+ ) + } +} + +class CodeMirrorInput extends Component { + render () { + const { message, handleMessageChange } = this.props + const isValid = message.isValid + return ( +
+ handleMessageChange(value) } + options={{mode: {name: 'javascript', json: true}}} + /> +
+ ) + } +} + +class BoolInput extends Component { + render () { + const { message, handleMessageChange } = this.props + return ( +
+ Boolean: + handleMessageChange('true') } checked={message.value === 'true'} id="boolTrue" type="radio" name="boolean" /> + + handleMessageChange('false') } checked={message.value === 'false'} id="boolFalse" type="radio" name="boolean" /> + +
+ ) + } +} + +export default Editor diff --git a/src/app/js/components/column/messageSender/MessageSenderView.js b/src/app/js/components/column/messageSender/MessageSenderView.js index c512b87..a695fdc 100644 --- a/src/app/js/components/column/messageSender/MessageSenderView.js +++ b/src/app/js/components/column/messageSender/MessageSenderView.js @@ -5,9 +5,8 @@ */ import React, { Component } from 'react' -import { Controlled as CodeMirror } from 'react-codemirror2' -import 'codemirror/mode/javascript/javascript' import Autosuggest from 'react-autosuggest' +import Editor from './Editor' // import TriangleBottomIcon from '../../../icons/TriangleBottom' @@ -18,16 +17,14 @@ class MessageSender extends Component { this.state = { tab: this.getThisTab(props), eventName: '', - messageCollection: [''], + messageCollection: [{type: 'String', value: '', isValid: true}], messageInEditor: 0, - // message: '', - messageIsJson: false, autosuggestResults: [] } this.handleMessageSend = this.handleMessageSend.bind(this) this.handleEventNameChange = this.handleEventNameChange.bind(this) - this.handleMessageBeforeChange = this.handleMessageBeforeChange.bind(this) + this.handleMessageChange = this.handleMessageChange.bind(this) this.handleClearClick = this.handleClearClick.bind(this) this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this) this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this) @@ -35,6 +32,7 @@ class MessageSender extends Component { this.addMessageArgument = this.addMessageArgument.bind(this) this.removeMessageArgument = this.removeMessageArgument.bind(this) this.noMessageArgument = this.noMessageArgument.bind(this) + this.changeType = this.changeType.bind(this) } componentWillReceiveProps(nextProps) { @@ -64,33 +62,107 @@ class MessageSender extends Component { }) } - handleMessageBeforeChange (editor, data, value) { + /** + * Change message type and value if required + * @param {event} e - input change event + */ + changeType (e) { const state = this.state const messageCollection = state.messageCollection.slice() - messageCollection[state.messageInEditor] = value + const type = e.target.value + let isValid = true + let value = messageCollection[state.messageInEditor].value + switch ( type ) { + case 'Boolean': + value = 'true' + break + + case 'Number': + value = ~~value + break + + case 'Object': + case 'Array': + const parsedValue = this.testObjValid(value) + if ( parsedValue && Object.prototype.toString.apply(parsedValue).slice(8, -1) === type ) + isValid = true + else + isValid = false + break + + case 'String': + try { // stringify current value in case it's an object or it'll look like "[object Object]", if JSON.parse works it's already a string, so we don't need to change it + JSON.parse(value) + value = value + '' // convert boolean to string or codemirror throws an error + } catch ( e ) { // if it throws we know it's not a JSON string already and we have to stringify it + value = JSON.stringify(value) + } + break + + case 'JSON': + try { // if JSON.parse works it's valid JSON + JSON.parse(value) + } catch ( e ) { + isValid = false + } + break + } + messageCollection[state.messageInEditor] = { + value, + type, + isValid + } this.setState({ - messageCollection, - messageIsJson: this.jsonOrText(value) + messageCollection }) } /** - * Returns true if string is valid JSON - * - * @param {String} string - * - * @return {Boolean} + * Update message and validation */ - jsonOrText (string) { - let isJson - try { - JSON.parse(string) - isJson = true + handleMessageChange (value) { + const state = this.state + const messageCollection = state.messageCollection.slice() + const messageInEditor = state.messageInEditor + const message = messageCollection[messageInEditor] + + const type = message.type + let isValid = message.isValid + + switch ( type ) { + case 'Object': + case 'Array': + isValid = !!this.testObjValid(value) + break + + case 'JSON': + try { + JSON.parse(value) // if it doesn't throw it's a valid JSON string + isValid = true + } catch ( e ) { + isValid = false + } } - catch (error) { - isJson = false + + messageCollection[messageInEditor] = { + value, + isValid, + type } - return isJson + this.setState({ + messageCollection + }) + } + + testObjValid (value) { + let evalResult, JSONResult + try { + eval(`evalResult = ${value}`) // if it doesn't throw it's a valid array or object + } catch ( e ) {} + try { + JSONResult = JSON.parse(value) // if it doesn't throw it's a valid array or object + } catch ( e ) {} + return evalResult || JSONResult } /** @@ -164,7 +236,7 @@ class MessageSender extends Component { addMessageArgument () { const messageCollection = this.state.messageCollection this.setState({ - messageCollection: messageCollection.concat(''), + messageCollection: messageCollection.concat({type: 'String', value: '', isValid: true}), messageInEditor: messageCollection.length }) } @@ -200,6 +272,7 @@ class MessageSender extends Component { const state = this.state const connected = state.tab.connected const messageInEditor = state.messageInEditor + const messageInEditorObject = state.messageCollection[state.messageInEditor] const autosuggestInputProps = { placeholder: 'Event name', @@ -207,6 +280,10 @@ class MessageSender extends Component { onChange: this.handleEventNameChange }; + let sendIsEnabled = true + if ( !connected || !state.eventName || !state.messageCollection.map( m => m.isValid ).reduce( (a, b) => a && b) ) + sendIsEnabled = false + return (

Send Message

@@ -274,12 +351,14 @@ class MessageSender extends Component {
- { - state.messageIsJson ? - "JSON" - : - "String" - } +
@@ -290,16 +369,11 @@ class MessageSender extends Component { Clear - +