sbot/ui/lib/com/webcam-giffer-form.js

265 lines
7.2 KiB
JavaScript

'use strict'
var h = require('hyperscript')
var o = require('observable')
var schemas = require('ssb-msg-schemas')
var mlib = require('ssb-msgs')
var pull = require('pull-stream')
var pushable = require('pull-pushable')
var createHash = require('multiblob/util').createHash
var suggestBox = require('suggest-box')
var toBuffer = require('blob-to-buffer')
var app = require('../app')
var ui = require('../ui')
var modals = require('../ui/modals')
var com = require('./index')
var mentionslib = require('../mentions')
var videoOpts = {
optional: [
{ minHeight: 150 },
{ maxHeight: 150 },
{ minWidth: 300 },
{ maxWidth: 300 }
]
}
module.exports = function (rootMsg, branchMsg, opts) {
opts = opts || {}
var blob
var recordInterval
var encoder = new Whammy.Video(10)
var countdown = o(0)
// markup
var canvas = h('canvas')
var context = canvas.getContext('2d')
var invideo = h('video')
var outvideo = h('video.hide', { autoplay: true, loop: true })
var textarea = h('textarea.form-control', {
name: 'text',
placeholder: 'Add a message (optional)',
rows: 6
})
var publishBtn = h('button.btn.btn-primary.pull-right.hidden', { onclick: onpublish }, 'Publish')
var form = h('form.webcam-giffer-form',
h('.webcam-giffer-form-videos', { onmousedown: onmousedown },
o.transform(countdown, function (c) {
if (!c)
return ''
return h('.countdown', c)
}),
invideo,
outvideo,
h('br'),
h('a.btn.btn-3d', { onclick: onrecord(1) }, com.icon('record'), ' Record 1s'), ' ',
h('a.btn.btn-3d', { onclick: onrecord(2) }, '2s'), ' ',
h('a.btn.btn-3d', { onclick: onrecord(3), style: 'margin-right: 10px' }, '3s'),
h('a.text-muted', { href: '#', onclick: onreset }, com.icon('repeat'), ' Reset')
),
h('.webcam-giffer-form-ctrls', textarea, publishBtn)
)
suggestBox(textarea, app.suggestOptions)
function disable () {
publishBtn.classList.add('hidden')
}
function enable () {
publishBtn.classList.remove('hidden')
}
// handlers
function onmousedown (e) {
if (e.target.tagName == 'VIDEO') {
e.preventDefault()
startRecording()
document.addEventListener('mouseup', onmouseup)
}
}
function onmouseup (e) {
e.preventDefault()
stopRecording()
document.removeEventListener('mouseup', onmouseup)
}
function onrecord (seconds) {
return function (e) {
e.preventDefault()
startRecordingAfter(2, seconds)
}
}
function onreset (e) {
e && e.preventDefault()
encoder.frames = []
invideo.classList.remove('hide')
outvideo.classList.add('hide')
disable()
}
function onpublish (e) {
e.preventDefault()
var text = textarea.value || ''
if (!blob)
return
disable()
ui.pleaseWait(true)
// abort if the rootMsg wasnt decryptable
if (rootMsg && typeof rootMsg.value.content == 'string') {
ui.pleaseWait(false)
ui.notice('danger', 'Unable to decrypt rootMsg message')
enable()
return
}
function onerr (err) {
ui.setStatus(null)
ui.pleaseWait(false)
enable()
modals.error('Error While Publishing', err, 'This error occurred while trying to upload a webcam video to the blobstore.')
}
// upload blob to sbot
var hasher = createHash('sha256')
var ps = pushable()
pull(
ps,
hasher,
app.ssb.blobs.add(function (err) {
if (err) return onerr(err)
afterUpload()
})
)
toBuffer(blob, function (err, buffer) {
if (err) return onerr(err)
ps.push(buffer)
ps.end()
})
function afterUpload () {
// prepend the image-embed ot the text
text = '![webcam.webm](&'+hasher.digest+')\n\n' + text
console.log('posting', text)
// prep text
mentionslib.extract(text, function (err, mentions) {
if (err) {
ui.setStatus(null)
ui.pleaseWait(false)
enable()
if (err.conflict)
modals.error('Error While Publishing', 'You follow multiple people with the name "'+err.name+'." Go to the homepage to resolve this before publishing.')
else
modals.error('Error While Publishing', err, 'This error occurred while trying to extract the mentions from the text of a webcam post.')
return
}
// get encryption recipients from rootMsg
var recps
try {
if (Array.isArray(rootMsg.value.content.recps)) {
recps = mlib.links(rootMsg.value.content.recps)
.map(function (recp) { return recp.link })
.filter(Boolean)
}
} catch (e) {}
// post
var post = schemas.post(text, rootMsg && rootMsg.key, branchMsg && branchMsg.key, mentions, recps)
if (recps)
app.ssb.private.publish(post, recps, published)
else
app.ssb.publish(post, published)
function published (err, msg) {
ui.setStatus(null)
enable()
ui.pleaseWait(false)
if (err) modals.error('Error While Publishing', err, 'This error occurred while trying to post a webcam video.')
else {
textarea.value = ''
app.ssb.patchwork.subscribe(msg.key)
app.ssb.patchwork.markRead(msg.key)
opts && opts.onpost && opts.onpost(msg)
onreset()
}
}
})
}
}
// init webcam
navigator.webkitGetUserMedia({ video: videoOpts, audio: false }, function (stream) {
invideo.src = window.URL.createObjectURL(stream)
invideo.onloadedmetadata = function () { invideo.play() }
ui.onTeardown(function () {
stream.stop()
})
}, function (err) {
modals.error('Failed to Access Webcam', err)
})
// recording functions
function startRecordingAfter(c, seconds) {
// show input stream
invideo.classList.remove('hide')
outvideo.classList.add('hide')
// run countdown
countdown(c)
var i = setInterval(function () {
countdown(countdown() - 1)
if (countdown() === 0) {
clearInterval(i)
startRecording(seconds)
}
}, 1000)
}
function startRecording (seconds) {
// show input stream
invideo.classList.remove('hide')
outvideo.classList.add('hide')
// add 'recording' border
invideo.classList.add('recording')
// start capture
recordInterval = setInterval(captureFrame, 1000/10)
// captureFrame()
if (seconds)
setTimeout(stopRecording, seconds*1000)
}
function captureFrame () {
context.drawImage(invideo, 0, 0, 300, 150)
encoder.add(canvas)
}
function stopRecording () {
// stop capture
clearInterval(recordInterval)
// remove 'recording' border
invideo.classList.remove('recording')
// produce output
if (encoder.frames && encoder.frames.length) {
blob = encoder.compile()
console.log('Webm video encoded:', blob.size, 'bytes')
outvideo.src = URL.createObjectURL(blob, 'video/webm')
// show output stream
invideo.classList.add('hide')
outvideo.classList.remove('hide')
} else {
// show input stream
invideo.classList.remove('hide')
outvideo.classList.add('hide')
}
enable()
}
return form
}