diff options
-rw-r--r-- | app.js | 224 | ||||
-rw-r--r-- | bog.js | 128 | ||||
-rw-r--r-- | css/style.css | 109 | ||||
-rw-r--r-- | index.html | 19 | ||||
-rw-r--r-- | lib.js | 230 | ||||
-rw-r--r-- | package-lock.json | 64 | ||||
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | render.js | 143 | ||||
-rw-r--r-- | server.js | 56 | ||||
-rw-r--r-- | views.js | 59 | ||||
-rw-r--r-- | welcome.js | 67 |
11 files changed, 406 insertions, 697 deletions
@@ -1,176 +1,94 @@ var screen = h('div', {id: 'screen'}) document.body.appendChild(screen) -function keyPage (keys) { - var scroller = document.getElementById('scroller') - - var message = h('div', {classList: 'message'}) - - message.appendChild(h('p', {innerHTML: marked('This is your ed25519 public/private keypair. It was generated using [Tweetnacl.js](https://tweetnacl.js.org/#/). Your public key is your identity when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')})) - - // print stringified keypair - message.appendChild(h('pre', {style: 'width: 80%'}, [h('code', [JSON.stringify(keys)])])) - - // delete key button - message.appendChild(h('button', { - onclick: function () { - localforage.removeItem('id', function () { - location.hash = '' - location.reload() - }) - } - }, ['Delete Key'])) - - var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) - message.appendChild(textarea) - message.appendChild(h('button', { - onclick: function () { - if (textarea.value) { - localforage.setItem('id', JSON.parse(textarea.value)) - location.reload() - } - } - }, ['Import Key'])) - - scroller.appendChild(message) -} - -function profilePage (src, keys) { - var scroller = document.getElementById('scroller') - +function composer (keys, reply) { + var messageDiv = h('div') var message = h('div', {classList: 'message'}) - var identify = h('input', {placeholder: 'Identify ' + src.substring(0, 10) + '...'}) + var textarea = h('textarea', {placeholder: 'Write a new bog post...'}) - message.appendChild(h('div', [ - identify, - h('button', {onclick: function () { - if (identify.value) { - var content = { - author: keys.publicKey, - type: 'name', - naming: src, - name: identify.value, - timestamp: Date.now() - } - - identify.value = '' - publish(content, keys) - localforage.setItem('id', keys, function (err, published) { - if (published) { - location.reload() + var publisher = h('div', [ + textarea, + h('button', { + onclick: function () { + if (textarea.value) { + var content = { + type: 'post', + text: textarea.value, + timestamp: Date.now() + } + if (reply) { + content.reply = reply.key } - }) + publish(content, keys).then(post => { + open(post).then(msg => { + textarea.value = '' + if (reply) { + messageDiv.removeChild(messageDiv.firstChild) + if (messageDiv.firstChild) { + messageDiv.insertBefore(h('div', {classList: 'submessage'}, [render(msg, keys)]), messageDiv.childNodes[1]) + } else { + messageDiv.appendChild(h('div', {classList: 'submessage'}, [render(msg, keys)])) + } + } else { + if (messageDiv.firstChild) { + messageDiv.insertBefore(render(msg, keys), messageDiv.childNodes[1]) + } else { + messageDiv.appendChild(render(msg, keys)) + } + } + }) + }) + } } - }}, ['Identify']) - ])) - - scroller.appendChild(message) + }, ['Publish']) + ]) - requestFeed(src, 'ws://localhost:8080/', keys.publicKey) - - localforage.getItem(src, function (err, log) { - if (log) { - for (var i=0; i < log.length; i++) { - var post = log[i] - scroller.appendChild(renderMessage(post)) - } - } - }) + message.appendChild(publisher) + messageDiv.appendChild(message) + return messageDiv } -function threadPage (src, keys) { - var scroller = document.getElementById('scroller') - - localforage.getItem('log', function (err, log) { - for (var i = log.length - 1; i >= 0; --i) { - if (log[i].key === src) { - var post = log[i] - scroller.appendChild(renderMessage(post)) - } - } - }) -} - -function publicPage (keys) { - compose(keys) - - localforage.getItem('log', function (err, log) { - if (log) { - for (var i=0; i < log.length; i++) { - var post = log[i] - scroller.appendChild(renderMessage(post)) - } - var newLog = log.sort(function (a, b) { - return b.content.timestamp - a.content.timestamp - }) - if (newLog) { - localforage.setItem('log', log) - } - } - }) -} - -function route () { - localforage.getItem('id', function (err, keys) { +function route (keys) { + src = window.location.hash.substring(1) - src = window.location.hash.substring(1) - var scroller = h('div', {id: 'scroller'}) - var screen = document.getElementById('screen') - screen.appendChild(scroller) + var scroller = h('div', {id: 'scroller'}) + var screen = document.getElementById('screen') + screen.appendChild(scroller) - if (src === 'key') { - keyPage(keys) - } else if (src[0] === '@') { - profilePage(src, keys) - } else if (src[0] === '%') { - threadPage(src, keys) - } else { - publicPage(keys) - } - }) + if (src === 'key') { + keyPage(keys) + } else if (src[0] === '@') { + profilePage(src, keys) + } else if (src[0] === '%') { + threadPage(src, keys) + } else { + publicPage(keys) + } } -localforage.getItem('id', function (err, keys) { - if (keys) { +keys().then(key => { - var navbar = h('div', {classList: 'navbar'}, [ - h('div', {classList: 'internal'}, [ - h('li', [h('a', {href: '/#'}, ['Home'])]), - h('li', [h('a', {href: '#' + keys.publicKey}, [getName(keys.publicKey)])]), - h('li', [h('a', {href: '/#key'}, ['Key'])]) - ]) + var navbar = h('div', {classList: 'navbar'}, [ + h('div', {classList: 'internal'}, [ + h('li', [h('a', {href: '#'}, ['Home'])]), + h('li', [h('a', {href: '#' + key.publicKey}, [getName(key.publicKey)])]), + h('li', [h('a', {href: '#key'}, ['Key'])]) ]) + ]) + document.body.appendChild(navbar) - document.body.appendChild(navbar) - - route() - } else { - var genkey = nacl.sign.keyPair() - if (genkey) { - var keys = { - publicKey: '@' + nacl.util.encodeBase64(genkey.publicKey), - privateKey: nacl.util.encodeBase64(genkey.secretKey) - } - - if (keys.publicKey.includes('/')) { - console.log('TRYING AGAIN') - setTimeout(function () { - location.reload() - }, 10) - } else { - welcomeScreen(keys) - } - } - } + route(key) }) - - window.onhashchange = function () { - var oldscreen = document.getElementById('screen') - var newscreen = h('div', {id: 'screen'}) - oldscreen.parentNode.replaceChild(newscreen, oldscreen) - route() + keys().then(key => { + var oldscreen = document.getElementById('screen') + var newscreen = h('div', {id: 'screen'}) + oldscreen.parentNode.replaceChild(newscreen, oldscreen) + + route(key) + }) } + @@ -0,0 +1,128 @@ +// bog.open -- opens a signature and returns content if you pass a signature and a public key +// EX: open(msg).then(content => { console.log(content) }) + +async function open (msg) { + + var pubkey = nacl.util.decodeBase64(msg.author.substring(1)) + var sig = nacl.util.decodeBase64(msg.signature) + var opened = await JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) + + opened.key = msg.key + + return opened +} + +// bog.keys -- gets your public/private keypair, generates one if there is none +// EX: keys().then(key => { console.log(key)}) + +async function keys (key) { + var keypair = await localforage.getItem('id') + if (keypair != null) { + return keypair + } else { + var genkey = nacl.sign.keyPair() + var keypair = { + publicKey: '@' + nacl.util.encodeBase64(genkey.publicKey), + privateKey: nacl.util.encodeBase64(genkey.secretKey) + } + localforage.setItem('id', keypair) + return keypair + } +} + +// bog.get -- iterates over log and returns a post. +// EX: get('%x5T7KZ5haR2F59ynUuCggwEdFXlLHEtFoBQIyKYppZYerq9oMoIqH76YzXQpw2DnYiM0ugEjePXv61g3E4l/Gw==').then(msg => { console.log(msg)}) + +async function get (key) { + var log = await localforage.getItem('log') + if (log != null) { + for (var i = log.length - 1; i >= 0; --i) { + if (log[i].key === key) { + return log[i] + } + } + } +} + +// bog.getName -- iterates over a feed and returns a person's name + +function getName (id) { + var name = h('span') + + name.textContent = id.substring(0, 10) + '...' + + return name +} + +// bog.log (feed) -- returns a specific feed if a parameter is passed, if not returns the entire log +// EX: bog().then(log => { console.log(log)}) +// EX: bog('@ExE3QXmBhYQlGVA3WM2BD851turNzwhruWbIpMd7rbQ=').then(log => { console.log(log)}) + + +async function bog (feed) { + if (feed) { + var log = await localforage.getItem(feed) + return log + } else { + var log = await localforage.getItem('log') + return log + } +} + +// bog.publish -- publishes a new bog post and updates the feeds +// EX: publish({type: 'post', timestamp: Date.now(), text: 'Hello World'}).then(msg => { console.log(msg)}) + +async function publish (post, keys) { + post.author = keys.publicKey + + var message = { author: keys.publicKey } + + var feed = await localforage.getItem(keys.publicKey) + if (feed) { + var firstMsg = await open(feed[0]) + + post.seq = ++firstMsg.seq + + message.key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(post)))), + message.signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(post)), nacl.util.decodeBase64(keys.privateKey))) + + localforage.getItem('log').then(log => { + if (log) { + log.unshift(message) + localforage.setItem('log', log) + } else { + var feed = [message] + localforage.setItem('log', feed) + } + }) + + feed.unshift(message) + localforage.setItem(keys.publicKey, feed) + + return message + + } else { + + post.seq = 0 + + message.key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(post)))), + message.signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(post)), nacl.util.decodeBase64(keys.privateKey))) + + localforage.getItem('log').then(log => { + if (log) { + log.unshift(message) + localforage.setItem('log', log) + } else { + var feed = [message] + localforage.setItem('log', feed) + } + }) + + var feed = [message] + localforage.setItem(keys.publicKey, feed) + + return message + } +} + + diff --git a/css/style.css b/css/style.css index a59af50..7a517d1 100644 --- a/css/style.css +++ b/css/style.css @@ -1,7 +1,7 @@ body { font-family: 'Source Sans Pro'; - background: #222; - color: #f5f5f5; + background: #f5f5f5; + color: #5a5a5a; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; @@ -49,12 +49,23 @@ hr { .right { float: right;} .message { - background: #333; + border: 1px solid #ddd; + background: white; margin-top: .5em; padding: .3em .5em; border-radius: 5px; } +.message, .message > *, .navbar, .navbar > * { + animation: fadein .5s; +} + +@keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} + + .submessage { margin-left: 2em; } @@ -73,7 +84,7 @@ img.small { } a { - color: cyan; + color: #0088cc; text-decoration: none; } @@ -82,7 +93,7 @@ a:hover { } pre { - color: violet; + color: #dd1144; width: 100%; display: block; } @@ -105,13 +116,17 @@ code, pre { textarea, input { font-family: 'Source Sans Pro'; font-size: 1em; - background: #222; + background: #f5f5f5; padding: .5em; - color: #f5f5f5; + color: #5a5a5a; border: none; border-radius: 5px; } +textarea:active, textarea:focus { + background: white; +} + textarea { width: 100%; } @@ -170,28 +185,82 @@ textarea { button { display: inline-block; - padding: .25em .5em; - margin: .15em; - font-size: 1em; - line-height: 1.2em; - color: #d5d5d5; + *display: inline; + padding: 4px 12px; + margin-top: .5em; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + color: #333333; text-align: center; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; cursor: pointer; - background-color: #222; - border: 1px solid #222; - border-radius: 4px; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #cccccc; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } button:hover, button:focus, +button:active, +button[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + button:active { - color: white; - background-color: black; + background-color: #cccccc \9; +} + +button:first-child { + *margin-left: 0; } -button.active { - background-color: #111; +button:hover, +button:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; } +button:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +button:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + + @@ -2,18 +2,19 @@ <head> <title>Bogbook</title> <meta name='viewport' content='width=device-width initial-scale=1' /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel='stylesheet' href='./css/source-sans-pro.min.css' /> <link rel='stylesheet' href='./css/style.css' /> + <script src="./lib/nacl.min.js"></script> + <script src="./lib/nacl-util.min.js"></script> + <script src="./lib/localforage.min.js"></script> + <script src="./lib/marked.min.js"></script> + <script src="./lib/misc.js"></script> + <script async src="bog.js"></script> + <script async src="render.js"></script> + <script async src="views.js"></script> + <script async src="app.js"></script> </head> <body> - <script src="./lib/nacl.min.js"></script> - <script src="./lib/nacl-util.min.js"></script> - <script src="./lib/localforage.min.js"></script> - <script src="./lib/marked.min.js"></script> - <script src="./lib/misc.js"></script> - <script src="lib.js"></script> - <script src="welcome.js"></script> - <script src="render.js"></script> - <script src="app.js"></script> </body> </html> @@ -1,230 +0,0 @@ -/*function requestFeed (src, server, requester) { - - console.log(src) - console.log(server) - - var ws = new WebSocket(server + src) - - localforage.getItem(src, function (err, log) { - if (log) { - console.log(log) - - var post = log[0] - - var pubkey = nacl.util.decodeBase64(src.substring(1)) - var sig = nacl.util.decodeBase64(post.signature) - post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - - var seq = post.content.sequence - - ws.onopen = function () { - var req = { - feed: src, - seq, - requester - } - - console.log(req) - - ws.send(JSON.stringify(req)) - - } - ws.onmessage = function (message) { - var res = JSON.parse(message.data) - if (res.seq == null) { - console.log('SENDING ENTIRE LOG') - var send = { - feed: src, - log, - requester - } - ws.send(JSON.stringify(send)) - } - if (seq > res.req) { - console.log('SENDING') - console.log(log) - } - } - } else { - ws.onopen = function () { - var seq = null - - } - ws.onmessage = function (message) { - console.log(message.data) - } - } - }) -}*/ - -// publish new messages to your log -function publish (toPublish, keys) { - - localforage.getItem(keys.publicKey, function (err, log) { - if (log) { - var lastPost = log[0] - - var pubkey = nacl.util.decodeBase64(keys.publicKey.substring(1)) - var sig = nacl.util.decodeBase64(lastPost.signature) - - var opened = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - - var seq = opened.sequence - - toPublish.sequence = ++seq - toPublish.previous = nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(log[0])))) - - var author = keys.publicKey - var key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(toPublish)))) - var signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(toPublish)), nacl.util.decodeBase64(keys.privateKey))) - - var toPost = { - author, - key, - signature - } - - console.log(toPost) - - // update the log - updateLog(keys.publicKey, toPost) - - /*var scroller = document.getElementById('scroller') - if (scroller.firstChild) { - scroller.insertBefore(renderMessage(toPost), scroller.childNodes[1]) - } else { - scroller.appendChild(renderMessage(toPost)) - }*/ - - } else { - toPublish.sequence = 0 - - var toPost = { - author: keys.publicKey, - key: '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(toPublish)))), - signature: nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(toPublish)), nacl.util.decodeBase64(keys.privateKey))) - } - - updateLog(keys.publicKey, toPost) - - /*var scroller = document.getElementById('scroller') - if (scroller.firstChild) { - scroller.insertBefore(renderMessage(toPost), scroller.childNodes[1]) - } else { - scroller.appendChild(renderMessage(toPost)) - }*/ - } - }) -} - -function compose (keys) { - var message = h('div', {classList: 'message'}) - - var scroller = document.getElementById('scroller') - - scroller.insertBefore(message, scroller.firstChild) - - var textarea = h('textarea', {placeholder: 'Write a new bog post'}) - - message.appendChild(textarea) - - var composer = h('div', [ - h('button', { - onclick: function () { - if (textarea.value) { - var toPublish = { - author: keys.publicKey, - type: 'post', - text: textarea.value, - timestamp: Date.now() - } - textarea.value = '' - publish(toPublish, keys) - } - } - }, ['Publish']) - ]) - message.appendChild(composer) -} - - -// update your log in the browser - -function updateLog (feed, post) { - localforage.getItem(feed, function (err, log) { - if (log) { - log.unshift(post) - localforage.setItem(feed, log, function () { - console.log('FEED UPDATED') - }) - } else { - log = [] - log.unshift(post) - localforage.setItem(feed, log, function () { - console.log('FEED UPDATED') - }) - } - }) - - localforage.getItem('log', function (err, log) { - if (log) { - log.unshift(post) - localforage.setItem('log', log, function () { - console.log('LOG UPDATED') - location.reload() - }) - } else { - log = [] - log.unshift(post) - localforage.setItem('log', log, function () { - console.log('LOG UPDATED') - location.reload() - }) - } - }) -} - -function getName (id) { - var name = h('span') - name.textContent = id.substring(0, 10) + '...' - - localforage.getItem(id, function (err, log) { - if (log) { - for (var i=0; i < log.length; i++) { - var post = log[i] - - var pubkey = nacl.util.decodeBase64(post.author.substring(1)) - var sig = nacl.util.decodeBase64(post.signature) - post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - - //if (post.content) { - if (post.content.type == 'name') { - name.textContent = '@' + post.content.name - } - //} - } - } - }) - return name -} - -function getHeader (post, mini) { - var inner - if (mini) { - var inner = mini - } - - var head = h('span', [ - h('a', {href: '#' + post.key}, [ - h('p', {classList: 'right'}, [human(new Date(post.content.timestamp))]), - ]), - h('p', [ - h('a', {href: '#' + post.content.author}, [ - getName(post.content.author) - ]), - inner - ]) - ]) - return head -} - diff --git a/package-lock.json b/package-lock.json index 05b71a4..89c4e57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,75 +1,21 @@ { "name": "bogbook", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "ecstatic": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.1.tgz", - "integrity": "sha512-/rrctvxZ78HMI/tPIsqdvFKHHscxR3IJuKrZI2ZoUgkt2SiufyLFBmcco+aqQBIu6P1qBsUNG3drAAGLx80vTQ==", - "requires": { - "he": "^1.1.1", - "mime": "^1.6.0", - "minimist": "^1.1.0", - "url-join": "^2.0.5" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "opn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", - "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", + "open": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.2.0.tgz", + "integrity": "sha512-Vxf6HJkwrqmvh9UAID3MnMYXntbTxKLOSfOnO7LJdzPf3NE3KQYFNV0/Lcz2VAndbRFil58XVCyh8tiX11fiYw==", "requires": { "is-wsl": "^1.1.0" } - }, - "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" - }, - "tweetnacl-util": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz", - "integrity": "sha1-RXbBzuXi1j0gf+5S8boCgZSAvHU=" - }, - "url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } } } } diff --git a/package.json b/package.json index f73137a..cca809b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bogbook", - "version": "1.2.0", + "version": "1.3.0", "description": "secure blockchain logging (blogging, without the l) -- bogging", "main": "server.js", "scripts": { @@ -11,7 +11,7 @@ "license": "MIT", "dependencies": { "ecstatic": "^3.3.1", - "opn": "^6.0.0", + "open": "^6.2.0", "tweetnacl": "^1.0.1", "tweetnacl-util": "^0.15.0", "ws": "^6.2.1" @@ -1,118 +1,53 @@ -function renderMessage (post) { - var messageDiv = h('messageDiv', {id: post.key}) - var message = h('div', {classList: 'message'}) - - var pubkey = nacl.util.decodeBase64(post.author.substring(1)) - var sig = nacl.util.decodeBase64(post.signature) - post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - - - if (post.content.type == 'name') { - var mini = h('span', [ - ' identified as ', - post.content.name +function getHeader (post, mini) { + var head = h('span', [ + h('a', {href: '#' + post.key}, [ + h('p', {classList: 'right'}, [human(new Date(post.timestamp))]), + ]), + h('p', [ + h('a', {href: '#' + post.author}, [ + getName(post.author) + ]), + mini ]) + ]) + return head +} - message.appendChild(getHeader(post, mini)) - - messageDiv.appendChild(message) - } - - - if (post.content.type == 'post') { +function render (msg, keys) { + var messageDiv = h('messageDiv', {id: msg.key}) + var message = h('div', {classList: 'message'}) - /* - NEED TO UNBOX THREADS - localforage.getItem('log', function (err, log) { - if (log) { - for (var i = log.length - 1; i >= 0; --i) { - - if (log[i].content.reply == post.key) { - var nextPost = log[i] - var messageExists = (document.getElementById(nextPost.key) !== null); - if (!messageExists) { - messageDiv.appendChild(h('div', {classList: 'submessage'}, [ - renderMessage(nextPost) - ])) - } + bog().then(log => { + log.forEach(function (nextPost) { + open(nextPost).then(nextMessage => { + var messageExists = (document.getElementById(nextMessage.key) !== null); + if (nextMessage.reply == msg.key) { + if (!messageExists) { + messageDiv.appendChild(h('div', {classList: 'submessage'}, [render(nextMessage, keys)])) } } - } - })*/ - - var renderer = new marked.Renderer(); - renderer.link = function(href, title, text) { - if ((href[0] == '@') || (href[0] == '%')) { - href = '#' + href - } - var link = marked.Renderer.prototype.link.call(this, href, title, text); - return link - } - - marked.setOptions({ - renderer: renderer - }); + }) + }) + }) + + if (msg.type == 'post') { + message.appendChild(getHeader(msg)) - message.appendChild(getHeader(post)) - - if (post.content.reply) { + if (msg.reply) { message.appendChild(h('span', [ 're: ', - h('a', {href: '#' + post.content.reply}, [post.content.reply.substring(0, 10) + '...']) + h('a', {href: '#' + msg.reply}, [msg.reply.substring(0, 10) + '...']) ])) } - message.appendChild(h('div', {innerHTML: marked(post.content.text)})) - - message.appendChild(h('span', {id: post.key + 'src', classList: 'right'}, [ - h('a', { - onclick: function () { - message.appendChild(h('pre', [JSON.stringify(post)])) - var span = document.getElementById(post.key + 'src') - span.parentNode.removeChild(span) - } - }, ['[src]']) - ])) - - var gotName = getName(post.content.author) - - localforage.getItem('id', function (err, keys) { - - var publishButton = h('button', { - onclick: function () { - if (textarea.value) { - var content = { - author: keys.publicKey, - type: 'post', - text: textarea.value, - reply: post.key, - timestamp: Date.now() - } - publish(content, keys) - message.removeChild(textarea) - message.removeChild(publishButton) - } - } - }, ['Publish']) - - var textarea = h('textarea', {placeholder: 'Reply to this bog post'}, ['['+ gotName.textContent + '](' + post.content.author + ')']) - - var replyButton = h('button', { - classList: 'replyButton:' + post.key, - onclick: function () { - message.removeChild(replyButton) - message.appendChild(textarea) - message.appendChild(publishButton) - - } - }, ['Reply']) - - message.appendChild(replyButton) - }) - - messageDiv.appendChild(message) - } + message.appendChild(h('div', [msg.text])) + message.appendChild(h('button', { + onclick: function () { + messageDiv.appendChild(composer(keys, msg)) + } + }, ['Reply'])) + } + messageDiv.appendChild(message) return messageDiv } - @@ -2,62 +2,12 @@ var http = require('http') var serve = require('ecstatic') -var opn = require('opn') +var open = require('open') http.createServer( serve({ root: __dirname}) ).listen(8089) -opn('http://localhost:8089') - -// websocket server (8080) - -var WebSocket = require('ws') -var fs = require('fs') -var nacl = require('tweetnacl') - nacl.util = require('tweetnacl-util') - -var wserver = new WebSocket.Server({ port: 8080 }) - -wserver.on('connection', function (ws) { - ws.on('message', function (message) { - var data = JSON.parse(message) - console.log(data) - - // initial req/res contains a sequence number - if (data.seq) { - if (fs.existsSync(__dirname + '/bogs/' + data.feed)) { - fs.readFile(__dirname + '/bogs/' + data.feed, 'UTF-8', function (err, data) { - if (data) { - var log = JSON.parse(data) - console.log(log) - } - }) - } else { - var res = { - feed: data.feed, - seq: null - } - console.log(res) - ws.send(JSON.stringify(res)) - } - - } - - // if the client has a longer log, it'll send one for the server to save - if (data.log) { - console.log(data) - if (fs.existsSync(__dirname + '/bogs/' + data.feed)) { - var log = JSON.parse(fs.readFileSync(__dirname + '/bogs/' + data.feed)) - console.log(log) - - } else { - fs.writeFile(__dirname + '/bogs/' + data.feed, JSON.stringify(data.log), function (err, success) { - console.log('saved ' + data.feed + ' sent by ' + data.requester) - }) - } - } - - }) -}) +open('http://localhost:8089') +// ws server (8080) diff --git a/views.js b/views.js new file mode 100644 index 0000000..94fcb5d --- /dev/null +++ b/views.js @@ -0,0 +1,59 @@ +function threadPage (src, keys) { + get(src).then(msg => { + open(msg).then(post => { + scroller.appendChild(render(post, keys)) + }) + }) +} + +function profilePage (src, keys) { + bog(src).then(log => { + log.forEach(function (msg) { + open(msg).then(post => { + scroller.appendChild(render(post, keys)) + }) + }) + }) +} + +function publicPage (keys) { + scroller.appendChild(composer(keys)) + bog().then(log => { + log.forEach(function (msg) { + open(msg).then(post => { + scroller.appendChild(render(post, keys)) + }) + }) + }) +} + +function keyPage (keys) { + var message = h('div', {classList: 'message'}) + + message.appendChild(h('p', {innerHTML: marked('This is your ed25519 public/private keypair. It was generated using [TweetNaCl.js](https://tweetnacl.js.org/#/). Your public key is your identity when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')})) + + message.appendChild(h('pre', {style: 'width: 80%'}, [h('code', [JSON.stringify(keys)])])) + + message.appendChild(h('button', { + onclick: function () { + localforage.removeItem('id', function () { + location.hash = '' + location.reload() + }) + } + }, ['Delete Key'])) + + var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) + message.appendChild(textarea) + message.appendChild(h('button', { + onclick: function () { + if (textarea.value) { + localforage.setItem('id', JSON.parse(textarea.value)) + location.reload() + } + } + }, ['Import Key'])) + + scroller.appendChild(message) +} + diff --git a/welcome.js b/welcome.js deleted file mode 100644 index 342756a..0000000 --- a/welcome.js +++ /dev/null @@ -1,67 +0,0 @@ -function welcomeScreen (keys) { - var screen = document.getElementById('screen') - - var scroller = h('div', {id: 'scroller'}) - screen.appendChild(scroller) - - var message = h('div', {classList: 'message'}) - - scroller.appendChild(message) - - message.appendChild(h('h1', ['Welcome to Bogbook'])) - message.appendChild(h('p', ['Bogbook is a distributed blogging network of signed append-only feeds. We call them "bogs".'])) - message.appendChild(h('p', ['Please note: Bogbook is experimental software, not for use in producton environments. Expect bugs and breaking changes. Pull-requests are needed.'])) - message.appendChild(h('p', {innerHTML: marked('View the code: [http://github.com/bogbook/bog](http://github.com/bogbook/bog). Questions? [ev@evbogue.com](mailto:ev@evbogue.com).')})) - message.appendChild(h('hr')) - message.appendChild(h('h3', ['Get started'])) - message.appendChild(h('p', {innerHTML: marked('This is an ed25519 public/private signing keypair. It was generated using [TweetNaCl.js](https://tweetnacl.js.org/#/)')})) - message.appendChild(h('pre', [JSON.stringify(keys)])) - message.appendChild(h('p', ['Right now, this keypair exists only in memory. When you leave this page, the keypair will vanish forever. If you refresh this page you\'ll receive a new keypair.'])) - message.appendChild(h('p', {innerHTML: marked('To save this keypair, identify with a handle below. Once you identify, your public/private keypair will be stored in your browser using [localForage.js](https://localforage.github.io/localForage). Save your keypair somewhere safe to preserve your identity.')})) - message.appendChild(h('hr')) - message.appendChild(h('h3', ['Identify'])) - - var identify = h('input', {placeholder: 'Your Name'}) - - message.appendChild(h('div', [ - identify, - h('button', {onclick: function () { - if (identify.value) { - var toPublish = { - author: keys.publicKey, - type: 'name', - naming: keys.publicKey, - name: identify.value, - timestamp: Date.now() - } - - identify.value = '' - publish(toPublish, keys) - localforage.setItem('id', keys, function (err, published) { - if (published) { - location.hash = '' - location.reload() - } - }) - } - }}, ['Identify']) - ])) - message.appendChild(h('p', ['When you click [Identify], you will post your first message to your append-only bog, your ed25519 keypair will be saved in your browser, and the page will reload. Don\'t forget to back up your key! and happy bogging.'])) - message.appendChild(h('hr')) - message.appendChild(h('h3', ['Already have a key?'])) - message.appendChild(h('p', ['Import it here. Make sure to sync your existing feed from a Bogbook \'pub\' before posting a message.'])) - - var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) - message.appendChild(textarea) - message.appendChild(h('button', { - onclick: function () { - if (textarea.value) { - localforage.setItem('id', JSON.parse(textarea.value)) - location.hash = '' - location.reload() - } - } - }, ['Import Key'])) - - -} |