git ssb

0+

ev / microbay



forked from 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