git ssb

16+

Dominic / patchbay



Commit 896d9cb98b2e5f5c21d03292502be08b22a16f1a

Merge branch 'master' into hashrouter

Dominic Tarr committed on 7/25/2016, 11:32:13 PM
Parent: 635ea39199988640e918cccefadd0bac2abfdf36
Parent: 2b5c84eb99803edd36a6454033c6011d16b42479

Files changed

.gitignorechanged
README.mdchanged
index.jschanged
modules/about.jschanged
modules/avatar-image.jschanged
modules/avatar-profile.jschanged
modules/avatar.jschanged
modules/follow.jschanged
modules/index.jschanged
modules/invite.jschanged
modules/message-confirm.jschanged
modules/message.jschanged
modules/suggest-mentions.jschanged
modules/audio-mp3.jsadded
modules/avatar-edit.jsadded
modules/git.jsadded
modules/meta-image.jsadded
modules/music-release-cc.jsadded
modules/music-release.jsadded
modules/relationships.jsadded
package.jsonchanged
sbot-api.jschanged
style.csschanged
.gitignoreView
@@ -1,5 +1,4 @@
11 node_modules
22 npm-debug.log
33 .npmignore
44 build
5-
README.mdView
@@ -69,8 +69,9 @@
6969 now clone and run patchboard.
7070 ```
7171 git clone https://github.com/dominictarr/patchbay.git
7272 cd patchbay
73 +npm install
7374 npm install electro electron-prebuilt -g
7475 electro index.js
7576 ```
7677
index.jsView
@@ -1,5 +1,13 @@
11 var h = require('hyperscript')
2 +
3 +window.addEventListener('error', function onError(e) {
4 + document.body.appendChild(h('div.error',
5 + h('h1', e.message),
6 + h('big', h('code', e.filename + ':' + e.lineno)),
7 + h('pre', e.error ? (e.error.stack || e.error.toString()) : e.toString())))
8 +})
9 +
210 var u = require('./util')
311 var pull = require('pull-stream')
412 var combine = require('depject')
513 var fs = require('fs')
modules/about.jsView
@@ -26,9 +26,9 @@
2626 ' as ',
2727 h('a', {href:"#"+about.about},
2828 about.name || null,
2929 about.image
30- ? h('img', {src: blob_url(about.image)})
30 + ? h('img.avatar--fullsize', {src: blob_url(about.image)})
3131 : null
3232 )
3333 )
3434
modules/avatar-image.jsView
@@ -10,10 +10,13 @@
1010 var id = require('../keys').id
1111
1212 var default_avatar = '&qjeAs8+uMXLlyovT4JnEpMwTNDx/QXHfOl2nv2u0VCM=.sha256'
1313
14-exports.avatar_image = function (author) {
15- var img = h('img', {src: blob_url(default_avatar)})
14 +exports.avatar_image = function (author, classes) {
15 + classes = classes || ''
16 + if(classes && 'string' === typeof classes) classes = '.avatar--'+classes
17 +
18 + var img = h('img'+classes, {src: blob_url(default_avatar)})
1619 getAvatar({links: sbot_links}, id, author, function (err, avatar) {
1720 if (err) return console.error(err)
1821 if(ref.isBlob(avatar.image))
1922 img.src = blob_url(avatar.image)
modules/avatar-profile.jsView
@@ -1,13 +1,50 @@
11 var h = require('hyperscript')
22 var plugs = require('../plugs')
3 +var pull = require('pull-stream')
34
45 var avatar_image = plugs.first(exports.avatar_image = [])
6 +var avatar_name = plugs.first(exports.avatar_name = [])
57 var avatar_action = plugs.map(exports.avatar_action = [])
8 +var avatar_edit = plugs.first(exports.avatar_edit = [])
69
10 +var follows = plugs.first(exports.follows = [])
11 +var followers = plugs.first(exports.followers = [])
12 +
13 +function streamToList(stream, el) {
14 + pull(
15 + stream,
16 + pull.drain(function (item) {
17 + if(item) el.appendChild(item)
18 + })
19 + )
20 + return el
21 +}
22 +
23 +function image_link (id) {
24 + return h('a', {href:'#'+id}, avatar_image(id, 'thumbnail'))
25 +}
26 +
727 exports.avatar_profile = function (id) {
28 + return h('div.column.profile',
29 + avatar_edit(id),
30 + avatar_action(id),
831
9- return h('div.row.profile',
10- avatar_image(id),
11- h('div.column', avatar_action(id))
32 + h('div.profile__relationships.column',
33 +
34 + h('strong', 'follows'),
35 + streamToList(pull(
36 + follows(id),
37 + pull.unique(),
38 + pull.map(image_link)
39 + ), h('div.profile__follows.wrap')),
40 +
41 + h('strong', 'followers'),
42 + streamToList(pull(
43 + followers(id),
44 + pull.unique(),
45 + pull.map(image_link)
46 + ), h('div.profile__followers.wrap'))
47 + )
1248 )
1349 }
50 +
modules/avatar.jsView
@@ -5,12 +5,12 @@
55 var plugs = require('../plugs')
66 var avatar_name = plugs.first(exports.avatar_name = [])
77 var avatar_image = plugs.first(exports.avatar_image = [])
88
9-exports.avatar = function (author) {
9 +exports.avatar = function (author, classes) {
1010 return h('a.avatar',
1111 {href:'#'+author},
12- avatar_image(author),
12 + avatar_image(author, classes),
1313 avatar_name(author)
1414 )
1515 }
1616
modules/follow.jsView
@@ -5,15 +5,19 @@
55 var plugs = require('../plugs')
66
77 //render a message when someone follows someone,
88 //so you see new users
9 +function isRelated(value, name) {
10 + return value ? name : value === false ? 'un'+name : ''
11 +}
12 +
913 exports.message_content = function (msg) {
1014
11- if(msg.value.content.type == 'contact' && msg.value.content.contact) {
12- return h('div.contact',
13- 'follows',
14- avatar(msg.value.content.contact)
15- )
15 + var content = msg.value.content
16 + if(content.type == 'contact' && content.contact) {
17 + var relation = isRelated(content.following, 'follows')
18 + if(content.blocking) relation = 'blocks'
19 + return h('div.contact', relation, avatar(msg.value.content.contact, 'thumbnail'))
1620 }
1721 }
1822
1923 var sbot_links2 = plugs.first(exports.sbot_links2 = [])
@@ -81,4 +85,26 @@
8185 }}, h('br'), label)
8286 )
8387 }
8488
89 +
90 +
91 +
92 +
93 +
94 +
95 +
96 +
97 +
98 +
99 +
100 +
101 +
102 +
103 +
104 +
105 +
106 +
107 +
108 +
109 +
110 +
modules/index.jsView
@@ -1,8 +1,10 @@
11 module.exports = {
22 "_screen_view.js": require('./_screen_view.js'),
33 "about.js": require('./about.js'),
44 "app.js": require('./app.js'),
5 + "avatar-edit.js": require('./avatar-edit.js'),
6 + "audio-mp3.js": require('./audio-mp3.js'),
57 "avatar-image.js": require('./avatar-image.js'),
68 "avatar-profile.js": require('./avatar-profile.js'),
79 "avatar.js": require('./avatar.js'),
810 "blob-url.js": require('./blob-url.js'),
@@ -11,15 +13,20 @@
1113 "crypto.js": require('./crypto.js'),
1214 "feed.js": require('./feed.js'),
1315 "file-input.js": require('./file-input.js'),
1416 "follow.js": require('./follow.js'),
17 + "relationships.js": require('./relationships.js'),
18 + "git.js": require('./git.js'),
1519 "invite.js": require('./invite.js'),
1620 "like.js": require('./like.js'),
1721 "markdown.js": require('./markdown.js'),
1822 "message-confirm.js": require('./message-confirm.js'),
1923 "message-link.js": require('./message-link.js'),
2024 "message-name.js": require('./message-name.js'),
2125 "message.js": require('./message.js'),
26 + "meta-image.js": require('./meta-image.js'),
27 + "music-release-cc.js": require('./music-release-cc.js'),
28 + "music-release.js": require('./music-release.js'),
2229 "names.js": require('./names.js'),
2330 "notifications.js": require('./notifications.js'),
2431 "post.js": require('./post.js'),
2532 "private.js": require('./private.js'),
@@ -32,4 +39,13 @@
3239 "tabs.js": require('./tabs.js'),
3340 "thread.js": require('./thread.js'),
3441 "timestamp.js": require('./timestamp.js')
3542 }
43 +
44 +
45 +
46 +
47 +
48 +
49 +
50 +
51 +
modules/invite.jsView
@@ -3,91 +3,83 @@
33 var ssbClient = require('ssb-client')
44 var id = require('../keys').id
55 var h = require('hyperscript')
66
7 +var Progress = require('hyperprogress')
8 +
79 var plugs = require('../plugs')
810 var sbot_publish = plugs.first(exports.sbot_publish = [])
911
1012
11-exports.screen_view = function (invite) {
12-
13- //check that invite is
14- // ws:...~shs:key:seed
15-
13 +//check that invite is
14 +// ws:...~shs:key:seed
15 +function parseMultiServerInvite (invite) {
1616 var parts = invite.split('~')
1717 .map(function (e) { return e.split(':') })
1818
1919 if(parts.length !== 2) return null
2020 if(!/^(net|wss?)$/.test(parts[0][0])) return null
2121 if(parts[1][0] !== 'shs') return null
2222 if(parts[1].length !== 3) return null
23 + var p2 = invite.split(':')
24 + p2.pop()
2325
26 + return {
27 + invite: invite,
28 + remote: p2.join(':'),
29 + }
30 +}
31 +
32 +exports.screen_view = function (invite) {
33 +
34 + var data = parseMultiServerInvite(invite)
35 + if(!data) return
36 +
37 + var progress = Progress(4)
38 +
2439 //connect to server
2540 //request follow
2641 //post pub announce
2742 //post follow pub
28- var progress = h('h1')
29- var status = h('pre')
30- var div = h('div',
31- progress, status,
32- h('a', 'accept', {href: '#', onclick: function (ev) {
33- ev.preventDefault()
34- ev.stopPropagation()
35- attempt()
36- return false
37- }})
43 + var div = h('div.column',
44 + h('div',
45 + "invite to:", h('br'),
46 + h('code', invite),
47 + h('button', 'accept', {onclick: function (ev) {
48 + attempt()
49 + }})
50 + ),
51 + progress
3852 )
3953
4054 function attempt () {
41- progress.textContent = '*'
42- status.textContent = 'connecting...'
55 + progress.reset().next('connecting...')
4356
44- console.log("CONNECT", invite)
4557 ssbClient(null, {
4658 remote: invite,
4759 manifest: { invite: {use: 'async'}, getAddress: 'async' }
4860 }, function (err, sbot) {
49- console.log("ERR?", err, sbot)
50- if(err) {
51- progress.textContent = '*!'
52- status.textContent = err.stack
53- return
54- }
55- progress.textContent = '**'
56- status.textContent = 'requesting follow...' + id
61 + if(err) return progress.fail(err)
62 + progress.next('requesting follow...')
5763
5864 sbot.invite.use({feed: id}, function (err, msg) {
59- if(err) {
60- progress.textContent = '**!'
61- status.textContent = err.stack
62- return
63- }
64- progress.textContent = '***'
65- status.textContent = 'following...'
66-
65 + if(err) return progress.fail(err)
66 + progress.next('following...')
67 +
6768 //remove the seed from the shs address.
6869 //then it's correct address.
6970 //this should make the browser connect to this as remote.
7071 //we don't want to do this if when using this locally, though.
71- if(process.title === 'browser') {
72- var p2 = invite.split(':')
73- p2.pop()
74- localStorage.remote = p2.join(':')
75- }
72 + if(process.title === 'browser')
73 + localStorage.remote = data.remote
7674
7775 sbot_publish({
7876 type: 'contact',
7977 contact: sbot.id,
80- following: true
78 + following: true,
8179 }, function (err) {
82- if(err) {
83- progress.textContent = '***!'
84- status.textContent = err.stack
85- return
86- }
87- progress.textContent = '****'
88- status.textContent = 'READY!'
89-
80 + if(err) return progress.fail(err)
81 + progress.complete()
9082 })
9183
9284 })
9385 })
@@ -95,5 +87,4 @@
9587
9688 return div
9789 }
9890
99-
modules/message-confirm.jsView
@@ -15,9 +15,10 @@
1515 var lb = lightbox()
1616 document.body.appendChild(lb)
1717
1818 var okay = h('button', 'okay', {onclick: function () {
19- publish(content); lb.remove(); cb(null, content)
19 + lb.remove()
20 + publish(content, cb)
2021 }})
2122
2223 var cancel = h('button', 'cancel', {onclick: function () {
2324 lb.remove()
modules/message.jsView
@@ -32,9 +32,9 @@
3232 )
3333
3434 var msg = h('div.message',
3535 h('div.title.row',
36- h('div.avatar', avatar(msg.value.author)),
36 + h('div.avatar', avatar(msg.value.author, 'thumbnail')),
3737 h('div.message_meta.row', message_meta(msg))
3838 ),
3939 h('div.message_content', el),
4040 h('div.message_actions.row',
modules/suggest-mentions.jsView
@@ -4,8 +4,9 @@
44 return /\.(gif|jpg|png|svg)$/i.test(filename)
55 }
66
77 var sbot_links2 = require('../plugs').first(exports.sbot_links2 = [])
8 +var blob_url = require('../plugs').first(exports.blob_url = [])
89
910 exports.suggest = cont.to(function (word, cb) {
1011 if(!/^[@%&!]/.test(word[0])) return cb()
1112 if(word.length < 2) return cb()
@@ -32,12 +33,13 @@
3233 return {
3334 title: e.name + ': ' + e.id.substring(0,10)+' ('+e.rank+')',
3435 value: embed+'['+e.name+']('+e.id+')',
3536 rank: e.rank,
36- image: isImage(e.name) ? 'http://localhost:7777/'+e.id : undefined
37 + image: isImage(e.name) ? blob_url(e.id) : undefined
3738 }
3839 })
3940 cb(null, ary)
4041 })
4142 )
4243 })
4344
45 +
modules/audio-mp3.jsView
@@ -1,0 +1,51 @@
1 +var markdown = require('ssb-markdown');
2 +var h = require('hyperscript');
3 +var u = require('../util');
4 +var ref = require('ssb-ref');
5 +
6 +//render a message
7 +
8 +var plugs = require('../plugs');
9 +var message_link = plugs.first(exports.message_link = []);
10 +var message_confirm = plugs.first(exports.message_confirm = []);
11 +var sbot_links = plugs.first(exports.sbot_links = []);
12 +var blob_url = plugs.first(exports.blob_url = []);
13 +
14 +exports.message_content = function(msg, sbot) {
15 + if (msg.value.content.type !== 'audio-mp3')
16 + return;
17 +
18 + var v = msg.value.content;
19 + return h('div',
20 + h('h2', "(" + v.Track + ") " + v.Title),
21 + // h('img', { "src" : blob_url(v.cover) }),
22 + h('audio', {
23 + "controls" : true,
24 + "src" : blob_url(v.link)
25 + }))
26 + // h('dl',
27 + // Object.keys(v).map(function(k) {
28 + // return [
29 + // h("dt", k),
30 + // h("dd", v[k]),
31 + // ]
32 + // })))
33 +
34 + // "Album": "the fall of",
35 + // "Crc32": "038becab",
36 + // "Creator": "bleupulp",
37 + // "Format": "VBR MP3",
38 + // "Height": "0",
39 + // "Length": "375.23",
40 + // "Md5": "2c517c8e813da5f940c8c7e77d4b7f3f",
41 + // "Mtime": "1399498698",
42 + // "Name": "2_bleupulp_-_clouds.mp3",
43 + // "Sha1": "9f6a96a3d5571ed1ec2a7da38ffebdcd5f181482",
44 + // "Size": "15009000",
45 +
46 + // "Title": "clouds",
47 + // "Track": "2",
48 + // "Width": "0",
49 +
50 +}
51 +
modules/avatar-edit.jsView
@@ -1,0 +1,147 @@
1 +var dataurl = require('dataurl')
2 +var hyperfile = require('hyperfile')
3 +var hypercrop = require('hypercrop')
4 +var hyperlightbox = require('hyperlightbox')
5 +var h = require('hyperscript')
6 +var pull = require('pull-stream')
7 +var getAvatar = require('ssb-avatar')
8 +var plugs = require('../plugs')
9 +var ref = require('ssb-ref')
10 +
11 +var self_id = require('../keys').id
12 +var default_avatar = '&qjeAs8+uMXLlyovT4JnEpMwTNDx/QXHfOl2nv2u0VCM=.sha256'
13 +
14 +var confirm = plugs.first(exports.message_confirm = [])
15 +var sbot_blobs_add = plugs.first(exports.sbot_blobs_add = [])
16 +var blob_url = plugs.first(exports.blob_url = [])
17 +var sbot_links = plugs.first(exports.sbot_links = [])
18 +var avatar_name = plugs.first(exports.avatar_name = [])
19 +
20 +function crop (d, cb) {
21 + var data
22 + var canvas = hypercrop(h('img', {src: d}))
23 +
24 + return h('div.column.avatar_pic',
25 + canvas,
26 + //canvas.selection,
27 + h('div.row.avatar_pic__controls',
28 + h('button', 'okay', {onclick: function () {
29 + cb(null, canvas.selection.toDataURL())
30 + }}),
31 + h('button', 'cancel', {onclick: function () {
32 + cb(new Error('canceled'))
33 + }})
34 + )
35 + )
36 +}
37 +
38 +exports.avatar_edit = function (id) {
39 +
40 + var img = h('img.avatar--large', {src: blob_url(default_avatar)})
41 + var lb = hyperlightbox()
42 + var name_input = h('input', {placeholder: 'rename'})
43 + var name = avatar_name(id)
44 + var selected = null, selected_data = null
45 +
46 + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
47 + if (err) return console.error(err)
48 + //don't show user has already selected an avatar.
49 + if(selected) return
50 + if(ref.isBlob(avatar.image))
51 + img.src = blob_url(avatar.image)
52 + })
53 +
54 + var also_pictured = h('div.profile__alsopicturedas.wrap')
55 +
56 + pull(
57 + sbot_links({dest: id, rel: 'about', values: true}),
58 + pull.map(function (e) {
59 + return e.value.content.image
60 + }),
61 + pull.filter(function (e) {
62 + return e && 'string' == typeof e.link
63 + }),
64 + pull.unique('link'),
65 + pull.drain(function (image) {
66 + also_pictured.appendChild(
67 + h('a', {href:'#', onclick: function (ev) {
68 + ev.stopPropagation()
69 + ev.preventDefault()
70 + selected = image
71 + img.src = blob_url(image.link || image)
72 + }},
73 + h('img.avatar--thumbnail', {src: blob_url(image)})
74 + )
75 + )
76 + })
77 + )
78 +
79 + return h('div.row.profile',
80 + lb,
81 + img,
82 + h('div.column.profile__info',
83 + h('strong', name),
84 + name_input,
85 +
86 + hyperfile.asDataURL(function (data) {
87 + var el = crop(data, function (err, data) {
88 + if(data) {
89 + img.src = data
90 + pull(
91 + pull.once(dataurl.parse(data)),
92 + sbot_blobs_add(function (err, hash) {
93 + //TODO. Alerts are EVIL.
94 + //I use them only in a moment of weakness.
95 +
96 + if(err) return alert(err.stack)
97 + selected = {
98 + link: hash,
99 + size: selected.data.length,
100 + type: selected.mimetype,
101 + width: 512,
102 + height: 512
103 + }
104 +
105 + })
106 + )
107 + }
108 + lb.close()
109 + })
110 + lb.show(el)
111 + }),
112 + h('button', 'update', {onclick: function () {
113 + if(name_input.value)
114 + name.textContent = name_input.value
115 +
116 + if(selected)
117 + confirm({
118 + type: 'about',
119 + about: id,
120 + name: name_input.value || undefined,
121 + image: selected
122 + })
123 + else if(name_input.value) //name only
124 + confirm({
125 + type: 'about',
126 + about: id,
127 + name: name_input.value || undefined,
128 + })
129 + else
130 + //another moment of weakness
131 + alert('must select a name or image')
132 + }}),
133 + also_pictured
134 + )
135 + )
136 +}
137 +
138 +
139 +
140 +
141 +
142 +
143 +
144 +
145 +
146 +
147 +
modules/git.jsView
@@ -1,0 +1,250 @@
1 +var h = require('hyperscript')
2 +var pull = require('pull-stream')
3 +var paramap = require('pull-paramap')
4 +var moment = require('moment')
5 +
6 +var plugs = require('../plugs')
7 +var message_link = plugs.first(exports.message_link = [])
8 +var sbot_links = plugs.first(exports.sbot_links = [])
9 +var sbot_links2 = plugs.first(exports.sbot_links2 = [])
10 +var sbot_get = plugs.first(exports.sbot_get = [])
11 +var getAvatar = require('ssb-avatar')
12 +var avatar_name = plugs.first(exports.avatar_name = [])
13 +var markdown = plugs.first(exports.markdown = [])
14 +
15 +var self_id = require('../keys').id
16 +
17 +function shortRefName(ref) {
18 + return ref.replace(/^refs\/(heads|tags)\//, '')
19 +}
20 +
21 +function repoLink(id) {
22 + var el = h('a', {href: '#'+id}, id.substr(0, 10) + '…')
23 + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
24 + if(err) return console.error(err)
25 + el.textContent = avatar.name
26 + })
27 + return el
28 +}
29 +
30 +function getIssueState(id, cb) {
31 + pull(
32 + sbot_links({dest: id, rel: 'issues', values: true}),
33 + pull.map(function (msg) {
34 + var issues = msg.value.content.issues
35 + if (!Array.isArray(issues)) return
36 + return issues.filter(function (issue) {
37 + return issue.link === id
38 + }).map(function (issue) {
39 + return {
40 + ts: msg.value.timestamp,
41 + open: issue.open,
42 + merged: issue.merged,
43 + }
44 + })
45 + }),
46 + pull.flatten(),
47 + pull.collect(function (err, updates) {
48 + if (err) return cb(err)
49 + var open = true, merged = false
50 + updates.sort(function (a, b) {
51 + return b.ts - a.ts
52 + }).forEach(function (update) {
53 + if (update.open != null)
54 + open = update.open
55 + if (update.merged != null)
56 + merged = update.merged
57 + })
58 + cb(null, open ? 'open' : merged ? 'merged' : 'closed')
59 + })
60 + )
61 +}
62 +
63 +function messageTimestampLink(msg) {
64 + var m = moment(msg.value.timestamp)
65 + return h('a.timestamp', {
66 + timestamp: m,
67 + title: m.format('LLLL'),
68 + href: '#'+msg.key
69 + }, m.fromNow())
70 +}
71 +
72 +function tableRows(headerRow) {
73 + var thead = h('thead'), tbody = h('tbody')
74 + var first = true
75 + var t = [thead, tbody]
76 + t.append = function (row) {
77 + if (first) {
78 + first = false
79 + thead.appendChild(headerRow)
80 + }
81 + tbody.appendChild(row)
82 + }
83 + return t
84 +}
85 +
86 +function repoName(id, link) {
87 + var el = link
88 + ? h('a', {href: '#'+id}, id.substr(0, 8) + '…')
89 + : h('ins', id.substr(0, 8) + '…')
90 + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
91 + if(err) return console.error(err)
92 + el.textContent = avatar.name
93 + })
94 + return el
95 +}
96 +
97 +exports.message_content = function (msg, sbot) {
98 + var c = msg.value.content
99 +
100 + if(c.type === 'git-repo') {
101 + var nameEl
102 + var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT
103 + var div = h('div',
104 + h('p', 'git repo ', repoName(msg.key)),
105 + c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '',
106 + h('p', h('code', 'ssb://' + msg.key)),
107 + h('div.git-table-wrapper', {style: {'max-height': '12em'}},
108 + h('table',
109 + branchesT = tableRows(h('tr',
110 + h('th', 'branch'),
111 + h('th', 'commit'),
112 + h('th', 'last update'))),
113 + tagsT = tableRows(h('tr',
114 + h('th', 'tag'),
115 + h('th', 'commit'),
116 + h('th', 'last update'))))),
117 + h('div.git-table-wrapper', {style: {'max-height': '16em'}},
118 + h('table',
119 + openIssuesT = tableRows(h('tr',
120 + h('th', 'open issues'))),
121 + closedIssuesT = tableRows(h('tr',
122 + h('th', 'closed issues'))))),
123 + h('div.git-table-wrapper', {style: {'max-height': '16em'}},
124 + h('table',
125 + openPRsT = tableRows(h('tr',
126 + h('th', 'open pull requests'))),
127 + closedPRsT = tableRows(h('tr',
128 + h('th', 'closed pull requests'))))))
129 +
130 + // compute refs
131 + var refs = {}
132 + pull(
133 + sbot_links({
134 + reverse: true,
135 + source: msg.value.author,
136 + dest: msg.key,
137 + rel: 'repo',
138 + values: true
139 + }),
140 + pull.drain(function (link) {
141 + var refUpdates = link.value.content.refs
142 + for (var ref in refUpdates) {
143 + if (refs[ref]) continue
144 + refs[ref] = true
145 + var rev = refUpdates[ref]
146 + if (!rev) continue
147 + var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || []
148 + var t
149 + if (parts[1] === 'heads') t = branchesT
150 + else if (parts[1] === 'tags') t = tagsT
151 + if (t) t.append(h('tr',
152 + h('td', parts[2]),
153 + h('td', h('code', rev)),
154 + h('td', messageTimestampLink(link))))
155 + }
156 + }, function (err) {
157 + if (err) console.error(err)
158 + })
159 + )
160 +
161 + // list issues and pull requests
162 + pull(
163 + sbot_links({
164 + reverse: true,
165 + dest: msg.key,
166 + rel: 'project',
167 + values: true
168 + }),
169 + paramap(function (link, cb) {
170 + getIssueState(link.key, function (err, state) {
171 + if(err) return cb(err)
172 + link.state = state
173 + cb(null, link)
174 + })
175 + }),
176 + pull.drain(function (link) {
177 + var c = link.value.content
178 + // TODO: support renamed issues
179 + var title = c.title || (c.text ? c.text.length > 30
180 + ? c.text.substr(0, 30) + '…'
181 + : c.text : link.key)
182 + var author = link.value.author
183 + var t = c.type === 'pull-request'
184 + ? link.state === 'open' ? openPRsT : closedPRsT
185 + : link.state === 'open' ? openIssuesT : closedIssuesT
186 + t.append(h('tr',
187 + h('td',
188 + h('a', {href: '#'+link.key}, title), h('br'),
189 + h('small',
190 + 'opened ', messageTimestampLink(link),
191 + ' by ', h('a', {href: '#'+author}, avatar_name(author))))))
192 + }, function (err) {
193 + if (err) console.error(err)
194 + })
195 + )
196 +
197 + return div
198 + }
199 +
200 + if(c.type === 'git-update') {
201 + return h('p',
202 + 'pushed to ',
203 + repoLink(c.repo),
204 + c.refs ? h('ul', Object.keys(c.refs).map(function (ref) {
205 + var rev = c.refs[ref]
206 + return h('li',
207 + shortRefName(ref) + ': ',
208 + rev ? h('code', rev) : h('em', 'deleted'))
209 + })) : null,
210 + Array.isArray(c.issues) ? c.issues.map(function (issue) {
211 + if (issue.merged === true)
212 + return ['Merged ', message_link(issue.link), ' in ',
213 + h('code', issue.object), ' ', h('q', issue.label)]
214 + if (issue.open === false)
215 + return ['Closed ', message_link(issue.link), ' in ',
216 + h('code', issue.object), ' ', h('q', issue.label)]
217 + }) : null
218 + )
219 + }
220 +
221 + if (c.type === 'issue') {
222 + return h('div',
223 + h('p', 'opened issue on ', repoLink(c.project)),
224 + c.title ? h('h4', c.title) : '',
225 + markdown(c)
226 + )
227 + }
228 +
229 + if (c.type === 'pull-request') {
230 + return h('div',
231 + h('p', 'opened pull-request ',
232 + 'to ', repoLink(c.repo), ':', c.branch, ' ',
233 + 'from ', repoLink(c.head_repo), ':', c.head_branch),
234 + c.title ? h('h4', c.title) : '',
235 + markdown(c)
236 + )
237 + }
238 +}
239 +
240 +exports.message_meta = function (msg, sbot) {
241 + var type = msg.value.content.type
242 + if (type == 'issue' || type == 'pull-request') {
243 + var el = h('em', '...')
244 + getIssueState(msg.key, function (err, state) {
245 + if (err) return console.error(err)
246 + el.textContent = state
247 + })
248 + return el
249 + }
250 +}
modules/meta-image.jsView
@@ -1,0 +1,48 @@
1 +var markdown = require('ssb-markdown');
2 +var h = require('hyperscript');
3 +var u = require('../util');
4 +var ref = require('ssb-ref');
5 +
6 +//render a message
7 +
8 +var plugs = require('../plugs');
9 +var message_link = plugs.first(exports.message_link = []);
10 +var message_confirm = plugs.first(exports.message_confirm = []);
11 +var sbot_links = plugs.first(exports.sbot_links = []);
12 +var blob_url = plugs.first(exports.blob_url = []);
13 +
14 +exports.message_content = function(msg, sbot) {
15 + if (msg.value.content.type !== 'meta-image')
16 + return;
17 +
18 + var v = msg.value.content;
19 + return h('div',
20 + // h('h2', "(" + v.Track + ") " + v.Title),
21 + h('img', { "src" : blob_url(v.link) }))
22 +
23 + // h('dl',
24 + // Object.keys(v).map(function(k) {
25 + // return [
26 + // h("dt", k),
27 + // h("dd", v[k]),
28 + // ]
29 + // })))
30 +
31 + // "Album": "the fall of",
32 + // "Crc32": "038becab",
33 + // "Creator": "bleupulp",
34 + // "Format": "VBR MP3",
35 + // "Height": "0",
36 + // "Length": "375.23",
37 + // "Md5": "2c517c8e813da5f940c8c7e77d4b7f3f",
38 + // "Mtime": "1399498698",
39 + // "Name": "2_bleupulp_-_clouds.mp3",
40 + // "Sha1": "9f6a96a3d5571ed1ec2a7da38ffebdcd5f181482",
41 + // "Size": "15009000",
42 +
43 + // "Title": "clouds",
44 + // "Track": "2",
45 + // "Width": "0",
46 +
47 +}
48 +
modules/music-release-cc.jsView
@@ -1,0 +1,80 @@
1 +var markdown = require('ssb-markdown');
2 +var h = require('hyperscript');
3 +var u = require('../util');
4 +var ref = require('ssb-ref');
5 +
6 +//render a message
7 +
8 +var plugs = require('../plugs');
9 +var message_link = plugs.first(exports.message_link = []);
10 +var message_confirm = plugs.first(exports.message_confirm = []);
11 +var sbot_links = plugs.first(exports.sbot_links = []);
12 +var blob_url = plugs.first(exports.blob_url = []);
13 +
14 +exports.message_content = function(msg, sbot) {
15 + if (msg.value.content.type !== 'music-release-cc')
16 + return;
17 +
18 + var tracks = msg.value.content.tracks;
19 + return h('div',
20 + h('img', { "src" : blob_url(msg.value.content.cover) }),
21 + h('h1', msg.value.content.title),
22 + h('ol',
23 + Object.keys(tracks).map(function(k) {
24 + var t = tracks[k];
25 + return h('li', t.fname,
26 + h("br"),
27 + h('audio', {
28 + "controls" : true,
29 + "src" : blob_url(t.link)
30 + }))
31 + })),
32 + h('p',
33 + "More info:", h('a', { href : msg.value.content.archivedotorg }, "archive.org"),
34 + h("br"),
35 + "License:", h('a', { href : msg.value.content.license }, "Link")))
36 +}
37 +
38 +// copied from like.js
39 +
40 +// inspiration for waveform range selection
41 +
42 +// idea: handout invite codes for upload of tracks to be cached by the pub
43 +
44 +// exports.message_meta = function (msg, sbot) {
45 +
46 +// var yupps = h('a')
47 +
48 +// pull(
49 +// sbot_links({dest: msg.key, rel: 'vote'}),
50 +// pull.collect(function (err, votes) {
51 +// if(votes.length === 1)
52 +// yupps.textContent = ' 1 yup'
53 +// if(votes.length)
54 +// yupps.textContent = ' ' + votes.length + ' yupps'
55 +// })
56 +// )
57 +
58 +// return yupps
59 +// }
60 +
61 +// exports.message_action = function (msg, sbot) {
62 +// if(msg.value.content.type !== 'vote')
63 +// return h('a', {href: '#', onclick: function () {
64 +// var yup = {
65 +// type: 'vote',
66 +// vote: { link: msg.key, value: 1, expression: 'yup' }
67 +// }
68 +// if(msg.value.content.recps) {
69 +// yup.recps = msg.value.content.recps.map(function (e) {
70 +// return e && typeof e !== 'string' ? e.link : e
71 +// })
72 +// yup.private = true
73 +// }
74 +// //TODO: actually publish...
75 +
76 +// message_confirm(yup)
77 +// }}, 'yup')
78 +
79 +// }
80 +
modules/music-release.jsView
@@ -1,0 +1,41 @@
1 +var markdown = require('ssb-markdown');
2 +var h = require('hyperscript');
3 +var u = require('../util');
4 +var ref = require('ssb-ref');
5 +
6 +//render a message
7 +
8 +var plugs = require('../plugs');
9 +var message_link = plugs.first(exports.message_link = []);
10 +var message_confirm = plugs.first(exports.message_confirm = []);
11 +var sbot_links = plugs.first(exports.sbot_links = []);
12 +
13 +exports.message_content = function(msg, sbot) {
14 + if (msg.value.content.type !== 'music-release')
15 + return;
16 +
17 + var v = msg.value.content;
18 + return h('div',
19 + // h('img', { "src" : "http://localhost:7777/" + encodeURIComponent(v.cover) }),
20 + h('h1', v.Title),
21 + h("p", v.Description),
22 + h("dl",
23 +
24 + h("dt", "Creator"),
25 + h("dd", v.Creator),
26 +
27 + h("dt", "Identifier"),
28 + h("dd", v.Identifier),
29 +
30 + h("dt", "Published"),
31 + h("dd", v.Publicdate),
32 +
33 + h("dt", "Runtime"),
34 + h("dd", v.Runtime),
35 +
36 + h("dt", "Source"),
37 + h("dd", v.Source),
38 +
39 + h("dt", "License"),
40 + h("dd", h('a', { href : v.Licenseurl }, "Link"))))
41 +}
modules/relationships.jsView
@@ -1,0 +1,23 @@
1 +
2 +var plugs = require('../plugs')
3 +
4 +var sbot_links2 = plugs.first(exports.sbot_links2 = [])
5 +
6 +//this is a bit crude, and doesn't actually show unfollows yet.
7 +
8 +exports.follows = function (id, cb) {
9 + return sbot_links2({query: [
10 + {"$filter": {"source": id, "rel": ["contact", true, false] }},
11 + {"$map": "dest"}
12 + ]})
13 +}
14 +
15 +exports.followers = function (id) {
16 + return sbot_links2({query: [
17 + {"$filter": {"dest": id, "rel": ["contact", true, false] }},
18 + {"$map": "source"}
19 + ]})
20 +}
21 +
22 +
23 +
package.jsonView
@@ -1,22 +1,27 @@
11 {
22 "name": "patchbay",
33 "description": "a pluggable patchwork",
4- "version": "1.8.4",
4 + "version": "1.13.3",
55 "homepage": "https://github.com/dominictarr/patchbay",
66 "repository": {
77 "type": "git",
88 "url": "git://github.com/dominictarr/patchbay.git"
99 },
1010 "dependencies": {
1111 "cont": "^1.0.3",
12 + "dataurl": "^0.1.0",
1213 "depject": "^1.0.1",
13- "hyperlightbox": "^0.1.0",
14 + "hypercrop": "^1.0.1",
15 + "hyperfile": "^1.1.0",
16 + "hyperlightbox": "^0.1.1",
17 + "hyperprogress": "0.1.0",
1418 "hyperscript": "^1.4.7",
1519 "hypertabs": "^1.2.0",
1620 "mime-types": "^2.1.11",
1721 "moment": "^2.13.0",
1822 "open-external": "^0.1.1",
23 + "peaks.js": "^0.4.7",
1924 "pull-cat": "^1.1.9",
2025 "pull-next": "0.0.0",
2126 "pull-paramap": "^1.1.6",
2227 "pull-reconnect": "^0.0.3",
sbot-api.jsView
@@ -75,9 +75,22 @@
7575 return {
7676 connection_status: connection_status,
7777 sbot_blobs_add: rec.sink(function (cb) {
7878 return pull(
79- Hash(cb),
79 + Hash(function (err, id) {
80 + if(err) return cb(err)
81 + //completely UGLY hack to tell when the blob has been sucessfully written...
82 + var start = Date.now(), n = 5
83 + ;(function next () {
84 + setTimeout(function () {
85 + sbot.blobs.has(id, function (err, has) {
86 + if(has) return cb(null, id)
87 + if(n--) next()
88 + else cb(new Error('write failed'))
89 + })
90 + }, Date.now() - start)
91 + })()
92 + }),
8093 sbot.blobs.add()
8194 )
8295 }),
8396 sbot_links: rec.source(function (query) {
@@ -102,8 +115,16 @@
102115 if(content.recps)
103116 content = ssbKeys.box(content, content.recps.map(function (e) {
104117 return ref.isFeed(e) ? e : e.link
105118 }))
119 + else if(content.mentions)
120 + content.mentions.forEach(function (mention) {
121 + if(ref.isBlob(mention.link)) {
122 + sbot.blobs.push(mention.link, function (err) {
123 + if(err) console.error(err)
124 + })
125 + }
126 + })
106127
107128 feed.add(content, function (err, msg) {
108129 if(err) console.error(err)
109130 else if(!cb) console.log(msg)
style.cssView
@@ -1,8 +1,10 @@
11 body {
22 font-family: "Source Sans Pro", sans-serif;
33 }
44
5 +p { margin-top: .35ex;}
6 +
57 .screen {
68 position: absolute;
79 top: 0px; bottom: 0px;
810 left: 0px; right: 0px;
@@ -20,8 +22,14 @@
2022 display: flex;
2123 flex-direction: row;
2224 }
2325
26 +.wrap {
27 + display: flex;
28 + flex-direction: row;
29 + flex-wrap: wrap;
30 +}
31 +
2432 .stretch {
2533 flex-basis: 0;
2634 }
2735
@@ -44,9 +52,9 @@
4452 overflow-x: hidden;
4553 }
4654
4755
48-img {
56 +.message img {
4957 max-width: 100%;
5058 display: block;
5159 }
5260
@@ -125,9 +133,9 @@
125133 /* messages */
126134
127135 .message {
128136 border: 1px solid #eee;
129- padding: 5px;
137 + padding: 7px;
130138 margin-top: .5em;
131139 background: white;
132140 display: block;
133141 flex-basis: 0;
@@ -203,26 +211,34 @@
203211 .avatar {
204212 display: flex;
205213 flex-direction: row;
206214 }
207-.avatar img {
215 +
216 +.avatar--large {
217 + width: 256px;
218 + height: 256px;
219 + border: 1px solid #eee;
220 +}
221 +
222 +.avatar--thumbnail {
208223 width: 40px;
209224 height: 40px;
210225 margin-right: 3px;
211226 border: 1px solid #ccc;
212227 }
213228
229 +.avatar--fullsize {
230 + width: 100%;
231 +}
232 +
214233 .profile {
215234 background: #fff;
235 + padding: .5em;
216236 border: 1px solid #eee;
217237 }
218238
219-.profile img {
220- width: 150px;
221- height: 150px;
222- margin-right: 1em;
223- margin-bottom: 1em;
224- border: 1px solid #ccc;
239 +.profile__info {
240 + margin-left: .5em;
225241 }
226242
227243 /* lightbox - used in message-confirm */
228244
@@ -278,6 +294,35 @@
278294 .error {
279295 background: red;
280296 }
281297
298 +/* avatar editor */
282299
300 +.hypercrop__canvas {
301 + width: 100%;
302 +}
283303
304 +/* gitssb */
305 +
306 +.git-table-wrapper {
307 + max-height: 12em;
308 + overflow: auto;
309 + word-break: break-all;
310 + margin: 1em 0;
311 +}
312 +
313 +.git-table-wrapper table {
314 + width: 100%;
315 +}
316 +
317 +/* invite codes */
318 +
319 +.hyperprogress__liquid {
320 + height: 1px;
321 + background: blue;
322 +}
323 +
324 +
325 +
326 +
327 +
328 +

Built with git-ssb-web