Skip to content

Instantly share code, notes, and snippets.

@ishiduca
Last active December 20, 2017 06:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ishiduca/88f6421cafd6dabf9ba14385d6a7d5a8 to your computer and use it in GitHub Desktop.
Save ishiduca/88f6421cafd6dabf9ba14385d6a7d5a8 to your computer and use it in GitHub Desktop.
example yo-yo-with-websocket
'use strict'
const path = require('path')
const http = require('http')
const xtend = require('xtend')
const valid = require('is-my-json-valid')
const validate = valid(require('./schema'))
const ecstatic = require('ecstatic')(path.join(__dirname, 'static'))
const websocket = require('websocket-stream')
const router = require('router-on-websocket-stream')
const r = router()
const app = module.exports = http.createServer(ecstatic)
r.add('echo', p => {
return validate(p, {verbose: true})
? Promise.resolve(xtend(p, {time: Date.now()}))
: Promise.reject(validate.errors)
})
websocket.createServer({server: app}, s => s.pipe(r.route()).pipe(s))
const port = process.env.PORT
app.listen(port, () => console.log(`server start to listen on port "${port}"`))
{
"dependencies": {
"bulma": "^0.6.1",
"ecstatic": "^3.1.1",
"is-my-json-valid": "^2.17.1",
"reconnect-core": "^1.3.0",
"router-on-websocket-stream": "git+https://github.com/ishiduca/router-on-websocket-stream.git",
"sheetify": "^6.2.0",
"timeago.js": "^3.0.2",
"websocket-stream": "^5.1.1",
"xtend": "^4.0.1",
"yo-yo": "^1.4.1"
},
"devDependencies": {
"yo-yoify": "^4.2.0"
},
"scripts": {
"build": "browserify ./yo-yo-app/main.js -o ./static/js/bundle.js"
},
"browserify": {
"transform": [
"yo-yoify",
"sheetify"
]
}
{
"type": "object",
"required": true,
"additionalProperties": false,
"properties": {
"faceName": {
"title": "face name",
"description": "your \"face name\"",
"type": "string",
"required": true,
"pattern": "^.{2,20}$"
},
"message": {
"title": "message",
"description": "message",
"type": "string",
"required": true,
"minLength": 1,
"maxLength": 140
}
}
}
const yo = require('yo-yo')
const css = require('sheetify')
const ago = require('timeago.js')
const xtend = require('xtend')
const inject = require('reconnect-core')
const websocket = require('websocket-stream')
const router = require('router-on-websocket-stream')
const schema = require('../schema')
const validate = require('is-my-json-valid')(schema)
const loc = window.location
const uri = [ loc.protocol.replace('http', 'ws'), '//', loc.host ].join('')
const POST = 'post'
const BROADCAST = 'broad cast'
const data = new Proxy({
isWsConnected: false,
faceName: '',
message: '',
doBroadcast: false,
messages: []
}, {
get (target, prop) {
if (prop === 'payload') {
return {
faceName: target.faceName,
message: target.message
}
}
if (prop === 'messages') {
return target.messages.map(m => xtend(m, {time: ago().format(m.time)}))
}
if (prop === 'submitButtonText') {
return target.doBroadcast ? BROADCAST : POST
}
return target[prop]
},
set (obj, prop, value) {
if (prop === 'faceName' && typeof value !== 'string') {
throw new TypeError('"faceName" must be "string"')
}
if (prop === 'message' && typeof value !== 'string') {
throw new TypeError('"message" must be "string"')
}
if (prop === 'lastMessage') {
obj.messages = [value].concat(obj.messages)
} else {
obj[prop] = value
}
update()
}
})
const r = router()
r.on('error', err => console.error(err))
r.on('end', () => console.log('router ended'))
r.on('finish', () => console.log('router finished'))
const reconnect = inject(uri => websocket(uri))
const re = reconnect({}, ws => {
ws.on('error', err => console.error(err))
ws.once('close', () => console.log('ws closed'))
ws.once('end', () => {
ws.unpipe(r)
r.unpipe(ws)
})
r.pipe(ws).pipe(r, {end: false})
})
re.on('error', err => {
console.error(err)
data.isWsConnected = false
})
re.on('connect', () => {
console.log(`connected - "${uri}"`)
data.isWsConnected = true
})
re.on('reconnect', (n, delay) => {
console.log(`reconnect - "${n}" times, "${delay}"`)
})
const echo = r.method('echo')
echo.on('error', err => console.error(err))
echo.on('data', result => (data.lastMessage = result))
const el = render()
css('../static/css/bulma-0.6.1/bulma.css')
document.body.appendChild(el)
re.connect(uri)
function render () {
if (!data.isWsConnected) return yo`<div class="container"><p>wait ws proxy connect...</p></div>`
return yo`
<div class="container">
<section class="hero">
<div class="hero-body">
<h1 class="title">example yo-yo app with weboscket</h1>
</div>
</section>
<section>
<form onsubmit=${onsubmit}>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">${schema.properties.faceName.title}</label>
</div>
<div class="field-body">
<div class="field">
<p class="control is-expanded">
<input
type="text"
class="input faceName"
required
placeholder=${schema.properties.faceName.description}
pattern=${schema.properties.faceName.pattern}
value=${data.faceName}
oninput=${onFaceNameInput}
autofocus
/>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">${schema.properties.message.title}</label>
</div>
<div class="field-body">
<div class="field">
<p class="control is-expanded">
<input
type="text"
class="input message"
required
placeholder=${schema.properties.message.description}
maxlength=${schema.properties.message.maxLength}
minlength=${schema.properties.message.minLength}
value=${data.message}
oninput=${onMessageInput}
/>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">do broadcast ?</label>
</div>
<div class="field-body">
<div class="field">
<p class="control is-expanded">
<input
type="checkbox"
checked=${data.doBroadcast ? 'checked' : ''}
onclick=${onDoBroadcastClick}
/>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal"></div>
<div class="field-body">
<div class="field">
<div class="control is-expanded">
<button
type="submit"
class="button is-primary"
>
${data.submitButtonText}
</button>
</div>
</div>
</div>
</div>
</form>
</section>
<section>
<h3 class="subtitle">result</h3>
${data.messages.map(m => yo`
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<p>
<b>${m.faceName}</b>
<span class="is-size-7"> (${m.time})</span>
</p>
<p>${m.message}</p>
</div>
</div>
</article>
</div>
`)}
</section>
</div>
`
}
function update () {
console.log('update')
console.log(data)
yo.update(el, render())
return el
}
function onsubmit (e) {
e.preventDefault()
if (!validate(data.payload, {verbose: true})) {
return validate.errors.forEach(err => console.error(err))
}
data.doBroadcast
? echo.broadcast(data.payload)
: echo.write(data.payload)
data.message = ''
}
function onFaceNameInput (e) {
e.stopPropagation()
data.faceName = e.target.value
}
function onMessageInput (e) {
e.stopPropagation()
data.message = e.target.value
}
function onDoBroadcastClick (e) {
e.stopPropagation()
data.doBroadcast = !data.doBroadcast
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment