Commit 449d051990b8d8149dd2a3bb9078e269533e324e
Merge branch 'master' into blob
mix irving committed on 1/13/2017, 10:27:18 PMParent: e33a98abcc85d3a3b35c0b2db5b98200b0614954
Parent: 2ee7ff4448c085c5e1659d6a5833f545fe3c6e39
Files changed
.gitignore | ||
---|---|---|
@@ -2,5 +2,4 @@ | ||
2 | 2 … | npm-debug.log |
3 | 3 … | .npmignore |
4 | 4 … | build |
5 | 5 … | modules/_index.js |
6 | -style.css.json |
README.md | ||
---|---|---|
@@ -38,8 +38,27 @@ | ||
38 | 38 … | npm run bundle |
39 | 39 … | npm start |
40 | 40 … | ``` |
41 | 41 … | |
42 … | +## Lite | |
43 … | + | |
44 … | +To run a lite client in the browser instead of using electron, use npm | |
45 … | +run lite from the prompt instead of run bundle. After that you need to | |
46 … | +generate a modern invite: | |
47 … | + | |
48 … | +``` | |
49 … | +sbot invite.create --modern | |
50 … | +``` | |
51 … | + | |
52 … | +Also set up sbot to allow these connections with: | |
53 … | + | |
54 … | +``` | |
55 … | +sbot server --allowPrivate | |
56 … | +``` | |
57 … | + | |
58 … | +Lastly open build/index.html in a browser and append the invite | |
59 … | +created above using: index.html#ws://localhost:8989.... | |
60 … | + | |
42 | 61 … | ## how to add a feature |
43 | 62 … | |
44 | 63 … | To add a new message type, add add a js to `./modules/` that |
45 | 64 … | exports a function named `message_content` (it should return an html element) |
index.js | ||
---|---|---|
@@ -1,4 +1,7 @@ | ||
1 … | +// polyfills | |
2 … | +require('setimmediate') | |
3 … | + | |
1 | 4 … | require('depject')( |
2 | 5 … | // from more specialized to more general |
3 | 6 … | require('./modules_extra'), |
4 | 7 … | require('./modules_basic'), |
modules_basic/about.js | ||
---|---|---|
@@ -1,48 +1,100 @@ | ||
1 … | +const fs = require('fs') | |
2 … | +const h = require('../h') | |
3 … | +const { when } = require('@mmckegg/mutant') | |
1 | 4 … | |
2 | -var h = require('hyperscript') | |
3 | - | |
4 | -function idLink (id) { | |
5 | - return h('a', {href:'#'+id}, id) | |
5 … | +exports.needs = { | |
6 … | + blob_url: 'first', | |
7 … | + markdown: 'first' | |
6 | 8 … | } |
7 | 9 … | |
8 | -function asLink (ln) { | |
9 | - return 'string' === typeof ln ? ln : ln.link | |
10 … | +exports.gives = { | |
11 … | + mcss: true, | |
12 … | + message_content: true, | |
13 … | + message_content_mini: true | |
10 | 14 … | } |
11 | 15 … | |
12 | -//var blob_url = require('../plugs').first(exports.blob_url = []) | |
16 … | +exports.create = function (api) { | |
17 … | + return { | |
18 … | + message_content, | |
19 … | + message_content_mini, | |
20 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
21 … | + } | |
13 | 22 … | |
14 | -exports.needs = { | |
15 | - blob_url: 'first' | |
16 | -} | |
23 … | + function message_content (msg) { | |
24 … | + if (msg.value.content.type !== 'about') return | |
17 | 25 … | |
18 | -exports.gives = 'message_content' | |
26 … | + var { content: about, author: authorId } = msg.value | |
27 … | + var { about: aboutId, name, image, description } = about | |
19 | 28 … | |
20 | -exports.create = function (api) { | |
29 … | + if (!aboutId) return null | |
21 | 30 … | |
22 | - return function (msg) { | |
23 | - if(msg.value.content.type !== 'about') return | |
31 … | + return h('About', [ | |
32 … | + Name({ aboutId, authorId, name }), | |
33 … | + Image({ aboutId, authorId, image }), | |
34 … | + Description({ aboutId, authorId, description }) | |
35 … | + ]) | |
36 … | + } | |
24 | 37 … | |
25 | - if(!msg.value.content.image && !msg.value.content.name) | |
26 | - return | |
38 … | + function message_content_mini (msg) { | |
39 … | + if (msg.value.content.type !== 'about') return | |
27 | 40 … | |
28 | - var about = msg.value.content | |
29 | - var id = msg.value.content.about | |
30 | - return h('p', | |
31 | - about.about === msg.value.author | |
32 | - ? h('span', 'self-identifies ') | |
33 | - : h('span', 'identifies ', idLink(id)), | |
34 | - ' as ', | |
35 | - h('a', {href:"#"+about.about}, | |
36 | - about.name || null, | |
37 | - about.image | |
38 | - ? h('img.avatar--fullsize', {src: api.blob_url(about.image)}) | |
39 | - : null | |
40 | - ) | |
41 | - ) | |
41 … | + var { content: about, author: authorId } = msg.value | |
42 … | + var { about: aboutId, name, image, description } = about | |
42 | 43 … | |
44 … | + if (!aboutId) return null | |
45 … | + if (image || description) return null | |
46 … | + | |
47 … | + return h('About', Name({ aboutId, authorId, name })) | |
43 | 48 … | } |
44 | 49 … | |
45 | -} | |
46 | 50 … | |
51 … | + function Name ({ aboutId, authorId, name }) { | |
52 … | + if (!name) return null | |
53 … | + return h('section -name', [ | |
54 … | + h('header', when(authorId === aboutId, | |
55 … | + 'self-identifies as', | |
56 … | + ['identifies ', targetLink(aboutId), ' as'] | |
57 … | + )), | |
58 … | + h('section', h( | |
59 … | + 'a -name', | |
60 … | + { href: `#${aboutId}` }, | |
61 … | + name | |
62 … | + )) | |
63 … | + ]) | |
64 … | + } | |
47 | 65 … | |
66 … | + function Image ({ aboutId, authorId, image }) { | |
67 … | + if (!image) return null | |
68 … | + return h('section -image', [ | |
69 … | + h('header', when(authorId === aboutId, | |
70 … | + 'self-portrays as', | |
71 … | + ['portrays ', targetLink(aboutId), ' as'] | |
72 … | + )), | |
73 … | + h('section', h( | |
74 … | + 'a -image', | |
75 … | + { href: `#${aboutId}` }, | |
76 … | + h('img', { src: api.blob_url(image) }) | |
77 … | + )) | |
78 … | + ]) | |
79 … | + } | |
48 | 80 … | |
81 … | + function Description ({ aboutId, authorId, description }) { | |
82 … | + if (!description) return null | |
83 … | + return h('section -description', [ | |
84 … | + h('header', when(authorId === aboutId, | |
85 … | + 'self-describes as', | |
86 … | + ['describes ', targetLink(aboutId), ' as'] | |
87 … | + )), | |
88 … | + h('section', api.markdown(description)) | |
89 … | + ]) | |
90 … | + } | |
91 … | +} | |
92 … | + | |
93 … | +function targetLink (aboutId) { | |
94 … | + const content = aboutId.slice(0, 9) + '...' | |
95 … | + return h( | |
96 … | + 'a -target', | |
97 … | + { href: `#${aboutId}` }, | |
98 … | + content | |
99 … | + ) | |
100 … | +} |
modules_basic/avatar-edit.js | ||
---|---|---|
@@ -135,9 +135,9 @@ | ||
135 | 135 … | name: name_input.value || undefined, |
136 | 136 … | image: selected |
137 | 137 … | }) |
138 | 138 … | else if(name_input.value) //name only |
139 | - confirm({ | |
139 … | + api.message_confirm({ | |
140 | 140 … | type: 'about', |
141 | 141 … | about: id, |
142 | 142 … | name: name_input.value || undefined, |
143 | 143 … | }) |
modules_basic/avatar-image.js | ||
---|---|---|
@@ -33,8 +33,10 @@ | ||
33 | 33 … | var waiting = [] |
34 | 34 … | |
35 | 35 … | var last = 0 |
36 | 36 … | |
37 … | +var cache = {} | |
38 … | + | |
37 | 39 … | exports.create = function (api) { |
38 | 40 … | var avatars = {} |
39 | 41 … | |
40 | 42 … | //blah blah |
@@ -89,20 +91,25 @@ | ||
89 | 91 … | avatar_image: function (author, classes) { |
90 | 92 … | classes = classes || '' |
91 | 93 … | if(classes && 'string' === typeof classes) classes = '.avatar--'+classes |
92 | 94 … | |
93 | - var img = visualize(new Buffer(author.substring(1), 'base64'), 256) | |
95 … | + function gen (id) { | |
96 … | + if(cache[id]) return h('img', {src: cache[id]}) | |
97 … | + var img = visualize(new Buffer(author.substring(1), 'base64'), 256) | |
98 … | + cache[id] = img.src | |
99 … | + return img | |
100 … | + } | |
101 … | + | |
102 … | + var img = ready && avatars[author] ? h('img', {src: api.blob_url(avatars[author].image)}) : gen(author) | |
103 … | + | |
94 | 104 … | ;(classes || '').split('.').filter(Boolean).forEach(function (c) { |
95 | 105 … | img.classList.add(c) |
96 | 106 … | }) |
97 | 107 … | |
98 | - function go () { | |
99 | - if(avatars[author]) img.src = api.blob_url(avatars[author].image) | |
100 | - } | |
101 | - | |
102 | 108 … | if(!ready) |
103 | - waiting.push(go) | |
104 | - else go() | |
109 … | + waiting.push(function () { | |
110 … | + if(avatars[author]) img.src = api.blob_url(avatars[author].image) | |
111 … | + }) | |
105 | 112 … | |
106 | 113 … | return img |
107 | 114 … | } |
108 | 115 … | } |
modules_basic/avatar-name.js | ||
---|---|---|
@@ -8,9 +8,9 @@ | ||
8 | 8 … | |
9 | 9 … | exports.create = function (api) { |
10 | 10 … | |
11 | 11 … | return function name (id) { |
12 | - var n = h('span', id.substring(0, 10)) | |
12 … | + var n = h('span', id ? id.substring(0, 10) : "") | |
13 | 13 … | |
14 | 14 … | //choose the most popular name for this person. |
15 | 15 … | //for anything like this you'll see I have used sbot.links2 |
16 | 16 … | //which is the ssb-links plugin. as you'll see the query interface |
modules_basic/avatar.js | ||
---|---|---|
@@ -9,29 +9,37 @@ | ||
9 | 9 … | |
10 | 10 … | exports.gives = { |
11 | 11 … | avatar: true, |
12 | 12 … | avatar_image_name_link: true, |
13 | - avatar_image_link: true | |
13 … | + avatar_image_link: true, | |
14 … | + avatar_name_link: true | |
14 | 15 … | } |
15 | 16 … | |
16 | 17 … | exports.create = function (api) { |
18 … | + return { | |
19 … | + avatar, | |
20 … | + avatar_image_name_link, | |
21 … | + avatar_image_link, | |
22 … | + avatar_name_link | |
23 … | + } | |
17 | 24 … | |
18 | - var exports = {} | |
19 | - exports.avatar = function (author, classes) { | |
25 … | + function avatar (author, classes) { | |
20 | 26 … | return exports.avatar_image_name_link(author, classes) |
21 | 27 … | } |
22 | 28 … | |
23 | - exports.avatar_image_name_link = function (author, classes) { | |
29 … | + function avatar_image_name_link (author, classes) { | |
24 | 30 … | return api.avatar_link(author, [ |
25 | 31 … | api.avatar_image(author, classes), |
26 | 32 … | api.avatar_name(author) |
27 | 33 … | ]) |
28 | 34 … | } |
29 | 35 … | |
30 | - exports.avatar_image_link = function (author, classes) { | |
36 … | + function avatar_image_link (author, classes) { | |
31 | 37 … | return api.avatar_link(author, api.avatar_image(author, classes)) |
32 | 38 … | } |
33 | 39 … | |
34 | - return exports | |
40 … | + function avatar_name_link (author, classes) { | |
41 … | + return api.avatar_link(author, api.avatar_name(author)) | |
42 … | + } | |
35 | 43 … | } |
36 | 44 … | |
37 | 45 … |
modules_basic/compose.js | ||
---|---|---|
@@ -1,11 +1,11 @@ | ||
1 | 1 … | |
2 | -var h = require('hyperscript') | |
3 | -var u = require('../util') | |
4 | -var suggest = require('suggest-box') | |
5 | -var mentions = require('ssb-mentions') | |
6 | -var lightbox = require('hyperlightbox') | |
7 | -var cont = require('cont') | |
2 … | +const fs = require('fs') | |
3 … | +const h = require('../h') | |
4 … | +const u = require('../util') | |
5 … | +const suggest = require('suggest-box') | |
6 … | +const mentions = require('ssb-mentions') | |
7 … | +const cont = require('cont') | |
8 | 8 … | |
9 | 9 … | //var plugs = require('../plugs') |
10 | 10 … | //var suggest_mentions= plugs.asyncConcat(exports.suggest_mentions = []) |
11 | 11 … | //var publish = plugs.first(exports.sbot_publish = []) |
@@ -20,61 +20,63 @@ | ||
20 | 20 … | message_confirm: 'first', |
21 | 21 … | file_input: 'first' |
22 | 22 … | } |
23 | 23 … | |
24 | -exports.gives = 'message_compose' | |
24 … | +exports.gives = { | |
25 … | + 'message_compose': true, | |
26 … | + 'mcss': true | |
27 … | +} | |
25 | 28 … | |
26 | -function id (e) { return e } | |
29 … | +exports.create = function (api) { | |
30 … | + return { | |
31 … | + message_compose, | |
32 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
33 … | + } | |
27 | 34 … | |
28 | -/* | |
29 | - opts can take | |
35 … | + /* | |
36 … | + opts can take | |
30 | 37 … | |
31 | - placeholder: string. placeholder text, defaults to "Write a message" | |
32 | - prepublish: function. called before publishing a message. | |
33 | - shrink: boolean. set to false, to make composer not shrink (or hide controls) when unfocused. | |
34 | -*/ | |
38 … | + placeholder: string. placeholder text, defaults to "Write a message" | |
39 … | + prepublish: function. called before publishing a message. | |
40 … | + shrink: boolean. set to false, to make composer not shrink (or hide controls) when unfocused. | |
41 … | + */ | |
35 | 42 … | |
36 | -exports.create = function (api) { | |
43 … | + function message_compose (meta = {}, opts = {}, cb) { | |
44 … | + if(!meta.type) throw new Error('message must have type') | |
37 | 45 … | |
38 | - return function (meta, opts, cb) { | |
39 | 46 … | if('function' === typeof cb) { |
40 | - if('function' === typeof opts) | |
47 … | + if('function' === typeof opts) { | |
41 | 48 … | opts = {prepublish: opts} |
42 | 49 … | } |
43 | - | |
44 | - if(!opts) opts = {} | |
50 … | + } | |
45 | 51 … | opts.prepublish = opts.prepublish || id |
46 | 52 … | |
47 | - var accessories | |
48 | - meta = meta || {} | |
49 | - if(!meta.type) throw new Error('message must have type') | |
50 | - var ta = h('textarea', { | |
51 | - placeholder: opts.placeholder || 'Write a message', | |
52 | - style: {height: opts.shrink === false ? '200px' : ''} | |
53 … | + var actions | |
54 … | + | |
55 … | + var textArea = h('textarea', { | |
56 … | + placeholder: opts.placeholder || 'Write a message' | |
53 | 57 … | }) |
54 | 58 … | |
55 | 59 … | if(opts.shrink !== false) { |
56 | 60 … | var blur |
57 | - ta.addEventListener('focus', function () { | |
61 … | + textArea.addEventListener('focus', () => { | |
58 | 62 … | clearTimeout(blur) |
59 | - if(!ta.value) { | |
60 | - ta.style.height = '200px' | |
63 … | + if(!textArea.value) { | |
64 … | + composer.className = 'Compose -expanded' | |
61 | 65 … | } |
62 | - accessories.style.display = 'block' | |
63 | 66 … | }) |
64 | - ta.addEventListener('blur', function () { | |
67 … | + textArea.addEventListener('blur', () => { | |
65 | 68 … | //don't shrink right away, so there is time |
66 | 69 … | //to click the publish button. |
67 | 70 … | clearTimeout(blur) |
68 | - blur = setTimeout(function () { | |
69 | - if(ta.value) return | |
70 | - ta.style.height = '50px' | |
71 | - accessories.style.display = 'none' | |
72 | - }, 200) | |
71 … | + blur = setTimeout(() => { | |
72 … | + if(textArea.value) return | |
73 … | + composer.className = 'Compose -contracted' | |
74 … | + }, 300) | |
73 | 75 … | }) |
74 | 76 … | } |
75 | 77 … | |
76 | - ta.addEventListener('keydown', function (ev) { | |
78 … | + textArea.addEventListener('keydown', ev => { | |
77 | 79 … | if(ev.keyCode === 13 && ev.ctrlKey) publish() |
78 | 80 … | }) |
79 | 81 … | |
80 | 82 … | var files = [] |
@@ -83,12 +85,12 @@ | ||
83 | 85 … | function publish() { |
84 | 86 … | publishBtn.disabled = true |
85 | 87 … | var content |
86 | 88 … | try { |
87 | - content = JSON.parse(ta.value) | |
89 … | + content = JSON.parse(textArea.value) | |
88 | 90 … | } catch (err) { |
89 | - meta.text = ta.value | |
90 | - meta.mentions = mentions(ta.value).map(function (mention) { | |
91 … | + meta.text = textArea.value | |
92 … | + meta.mentions = mentions(textArea.value).map(mention => { | |
91 | 93 … | // merge markdown-detected mention with file info |
92 | 94 … | var file = filesById[mention.link] |
93 | 95 … | if (file) { |
94 | 96 … | if (file.type) mention.type = file.type |
@@ -110,46 +112,49 @@ | ||
110 | 112 … | |
111 | 113 … | function done (err, msg) { |
112 | 114 … | publishBtn.disabled = false |
113 | 115 … | if(err) return alert(err.stack) |
114 | - else if (msg) ta.value = '' | |
116 … | + else if (msg) textArea.value = '' | |
115 | 117 … | |
116 | 118 … | if (cb) cb(err, msg) |
117 | 119 … | } |
118 | 120 … | } |
119 | 121 … | |
122 … | + var fileInput = api.file_input(file => { | |
123 … | + files.push(file) | |
124 … | + filesById[file.link] = file | |
120 | 125 … | |
121 | - var publishBtn = h('button', 'Publish', {onclick: publish}) | |
122 | - var composer = | |
123 | - h('div.compose', h('div.column', ta, | |
124 | - accessories = h('div.row.compose__controls', | |
125 | - //hidden until you focus the textarea | |
126 | - {style: {display: opts.shrink === false ? '' : 'none'}}, | |
127 | - api.file_input(function (file) { | |
128 | - files.push(file) | |
129 | - filesById[file.link] = file | |
126 … | + var embed = file.type.indexOf('image/') === 0 ? '!' : '' | |
130 | 127 … | |
131 | - var embed = file.type.indexOf('image/') === 0 ? '!' : '' | |
132 | - ta.value += embed + '['+file.name+']('+file.link+')' | |
133 | - console.log('added:', file) | |
134 | - }), | |
135 | - publishBtn) | |
136 | - ) | |
137 | - ) | |
128 … | + textArea.value += embed + '['+file.name+']('+file.link+')' | |
129 … | + composer.className = 'Compose -expanded' | |
130 … | + console.log('added:', file) | |
131 … | + }) | |
132 … | + var publishBtn = h('button', {'ev-click': publish}, 'Publish' ) | |
133 … | + var actions = h('section.actions', [ | |
134 … | + fileInput, publishBtn | |
135 … | + ]) | |
138 | 136 … | |
139 | - suggest(ta, function (name, cb) { | |
137 … | + var composer = h('Compose', { | |
138 … | + className: opts.shrink === false ? '-expanded' : '-contracted' | |
139 … | + }, [ | |
140 … | + textArea, | |
141 … | + actions | |
142 … | + ]) | |
143 … | + | |
144 … | + suggest(textArea, (name, cb) => { | |
140 | 145 … | cont.para(api.suggest_mentions(name)) |
141 | - (function (err, ary) { | |
142 | - cb(null, ary.reduce(function (a, b) { | |
146 … | + ((err, ary) => { | |
147 … | + cb(null, ary.reduce((a, b) => { | |
143 | 148 … | if(!b) return a |
144 | 149 … | return a.concat(b) |
145 | 150 … | }, [])) |
146 | 151 … | }) |
147 | 152 … | }, {}) |
148 | 153 … | |
149 | 154 … | return composer |
150 | - | |
151 | 155 … | } |
152 | 156 … | |
153 | 157 … | } |
154 | 158 … | |
159 … | +function id (e) { return e } | |
155 | 160 … |
modules_basic/index.js | ||
---|---|---|
@@ -11,8 +11,10 @@ | ||
11 | 11 … | "follow.js": require('./follow.js'), |
12 | 12 … | "invite.js": require('./invite.js'), |
13 | 13 … | "like.js": require('./like.js'), |
14 | 14 … | "markdown.js": require('./markdown.js'), |
15 … | + "message-author.js": require('./message-author.js'), | |
16 … | + "message-backlinks.js": require('./message-backlinks.js'), | |
15 | 17 … | "message-link.js": require('./message-link.js'), |
16 | 18 … | "message-name.js": require('./message-name.js'), |
17 | 19 … | "message.js": require('./message.js'), |
18 | 20 … | "names.js": require('./names.js'), |
@@ -20,8 +22,9 @@ | ||
20 | 22 … | "private.js": require('./private.js'), |
21 | 23 … | "pub.js": require('./pub.js'), |
22 | 24 … | "public.js": require('./public.js'), |
23 | 25 … | "relationships.js": require('./relationships.js'), |
26 … | + "reply.js": require('./reply.js'), | |
24 | 27 … | "search-box.js": require('./search-box.js'), |
25 | 28 … | "setup.js": require('./setup'), |
26 | 29 … | "suggest-mentions.js": require('./suggest-mentions.js'), |
27 | 30 … | "thread.js": require('./thread.js'), |
modules_basic/invite.js | ||
---|---|---|
@@ -34,9 +34,9 @@ | ||
34 | 34 … | invite_accept: function (invite, onProgress, cb) { |
35 | 35 … | var data = self.invite_parse(invite) |
36 | 36 … | if(!data) return cb(new Error('not a valid invite code:' + invite)) |
37 | 37 … | |
38 | - onProgress('connecting...') | |
38 … | + onProgress('Connecting...') | |
39 | 39 … | |
40 | 40 … | api.sbot_gossip_connect(data.remote, function (err) { |
41 | 41 … | if(err) console.log(err) |
42 | 42 … | }) |
@@ -45,21 +45,21 @@ | ||
45 | 45 … | remote: data.invite, |
46 | 46 … | manifest: { invite: {use: 'async'}, getAddress: 'async' } |
47 | 47 … | }, function (err, sbot) { |
48 | 48 … | if(err) return cb(err) |
49 | - onProgress('requesting follow...') | |
49 … | + onProgress('Requesting follow...') | |
50 | 50 … | console.log(sbot) |
51 | 51 … | sbot.invite.use({feed: id}, function (err, msg) { |
52 | 52 … | |
53 | 53 … | //if they already follow us, just check we actually follow them. |
54 | 54 … | if(err) api.follower_of(id, data.key, function (_err, follows) { |
55 | - if(follows) cb(err) | |
55 … | + if(follows) { location.hash = '' } | |
56 | 56 … | else next() |
57 | 57 … | }) |
58 | 58 … | else next() |
59 | 59 … | |
60 | 60 … | function next () { |
61 | - onProgress('following...') | |
61 … | + onProgress('Following...') | |
62 | 62 … | |
63 | 63 … | //remove the seed from the shs address. |
64 | 64 … | //then it's correct address. |
65 | 65 … | //this should make the browser connect to this as remote. |
@@ -87,15 +87,17 @@ | ||
87 | 87 … | //connect to server |
88 | 88 … | //request follow |
89 | 89 … | //post pub announce |
90 | 90 … | //post follow pub |
91 | - var div = h('div.column', | |
92 | - h('div', | |
93 | - "you have been invited to join:", h('br'), | |
94 | - h('code', data.invite) | |
91 … | + var div = h('div.column.scroller__wrapper', | |
92 … | + h('div.column.scroller__content', {style: 'margin-top: 25%;'}, | |
93 … | + h('h1', {innerHTML: 'The <a href="https://scuttlebot.io">Secure Scuttlebutt</a> Lite Client'}), | |
94 … | + h('p', "You've been invited to join:"), | |
95 … | + h('p', h('code', data.invite)) | |
95 | 96 … | ), |
96 | - h('button', 'accept', {onclick: attempt}), | |
97 | - progress | |
97 … | + h('p', h('button', 'Accept', {onclick: attempt})), | |
98 … | + progress, | |
99 … | + h('p', "Once you're in, give yourself a name and photo. And don't forget to say 'Hello!'") | |
98 | 100 … | ) |
99 | 101 … | |
100 | 102 … | function attempt () { |
101 | 103 … | self.invite_accept(invite, function (message) { |
modules_basic/like.js | ||
---|---|---|
@@ -9,8 +9,9 @@ | ||
9 | 9 … | //var message_link = plugs.first(exports.message_link = []) |
10 | 10 … | //var sbot_links = plugs.first(exports.sbot_links = []) |
11 | 11 … | |
12 | 12 … | exports.needs = { |
13 … | + avatar_name: 'first', | |
13 | 14 … | message_confirm: 'first', |
14 | 15 … | message_link: 'first', |
15 | 16 … | sbot_links: 'first' |
16 | 17 … | } |
@@ -48,11 +49,20 @@ | ||
48 | 49 … | } |
49 | 50 … | |
50 | 51 … | if(votes.length === 1) |
51 | 52 … | digs.textContent = ' 1 Dig' |
52 | - if(votes.length > 1) | |
53 … | + else if(votes.length > 1) | |
53 | 54 … | digs.textContent = ' ' + votes.length + ' Digs' |
54 | 55 … | |
56 … | + pull( | |
57 … | + pull.values(votes.map(vote => { | |
58 … | + return api.avatar_name(vote.source) | |
59 … | + })), | |
60 … | + pull.collect(function (err, ary) { | |
61 … | + digs.title = ary.map(x => x.innerHTML).join(" ") | |
62 … | + }) | |
63 … | + ) | |
64 … | + | |
55 | 65 … | return digs |
56 | 66 … | } |
57 | 67 … | |
58 | 68 … | exports.message_action = function (msg, sbot) { |
modules_basic/markdown.js | ||
---|---|---|
@@ -1,32 +1,26 @@ | ||
1 | 1 … | var markdown = require('ssb-markdown') |
2 | 2 … | var h = require('hyperscript') |
3 | 3 … | var ref = require('ssb-ref') |
4 | 4 … | |
5 | -//var plugs = require('../plugs') | |
6 | -//var blob_url = plugs.first(exports.blob_url = []) | |
7 | -//var emoji_url = plugs.first(exports.emoji_url = []) | |
8 | - | |
9 | 5 … | exports.needs = { |
10 | 6 … | blob_url: 'first', |
11 | -// emoji_url: 'first' | |
7 … | + emoji_url: 'first' | |
12 | 8 … | } |
13 | 9 … | |
14 | 10 … | exports.gives = 'markdown' |
15 | 11 … | |
16 | 12 … | exports.create = function (api) { |
17 | 13 … | |
18 | -//emoji is in extra, disable for a sec | |
14 … | + function renderEmoji(emoji) { | |
15 … | + var url = api.emoji_url(emoji) | |
16 … | + if (!url) return ':' + emoji + ':' | |
17 … | + return '<img src="' + encodeURI(url) + '"' | |
18 … | + + ' alt=":' + escape(emoji) + ':"' | |
19 … | + + ' title=":' + escape(emoji) + ':"' | |
20 … | + + ' class="emoji">' | |
21 … | + } | |
19 | 22 … | |
20 | -// function renderEmoji(emoji) { | |
21 | -// var url = api.emoji_url(emoji) | |
22 | -// if (!url) return ':' + emoji + ':' | |
23 | -// return '<img src="' + encodeURI(url) + '"' | |
24 | -// + ' alt=":' + escape(emoji) + ':"' | |
25 | -// + ' title=":' + escape(emoji) + ':"' | |
26 | -// + ' class="emoji">' | |
27 | -// } | |
28 | - | |
29 | 23 … | return function (content) { |
30 | 24 … | if('string' === typeof content) |
31 | 25 … | content = {text: content} |
32 | 26 … | //handle patchwork style mentions. |
@@ -37,9 +31,9 @@ | ||
37 | 31 … | }) |
38 | 32 … | |
39 | 33 … | var md = h('div.markdown') |
40 | 34 … | md.innerHTML = markdown.block(content.text, { |
41 | - // emoji: renderEmoji, | |
35 … | + emoji: renderEmoji, | |
42 | 36 … | toUrl: function (id) { |
43 | 37 … | if(ref.isBlob(id)) return api.blob_url(id) |
44 | 38 … | return '#'+(mentions[id]?mentions[id]:id) |
45 | 39 … | } |
modules_basic/message.js | ||
---|---|---|
@@ -1,122 +1,89 @@ | ||
1 | -var h = require('hyperscript') | |
2 | -var u = require('../util') | |
3 | -var pull = require('pull-stream') | |
1 … | +const fs = require('fs') | |
2 … | +const pull = require('pull-stream') | |
3 … | +const u = require('../util') | |
4 … | +const h = require('../h') | |
4 | 5 … | |
5 | -//var plugs = require('../plugs') | |
6 | -// | |
7 | -//var message_content = plugs.first(exports.message_content = []) | |
8 | -//var message_content_mini = plugs.first(exports.message_content_mini = []) | |
9 | -// | |
10 | -//var avatar = plugs.first(exports.avatar = []) | |
11 | -//var avatar_name = plugs.first(exports.avatar_name = []) | |
12 | -//var avatar_link = plugs.first(exports.avatar_link = []) | |
13 | -//var message_meta = plugs.map(exports.message_meta = []) | |
14 | -//var message_action = plugs.map(exports.message_action = []) | |
15 | -//var message_link = plugs.first(exports.message_link = []) | |
16 | -// | |
17 | -//var sbot_links = plugs.first(exports.sbot_links = []) | |
18 | - | |
19 | 6 … | exports.needs = { |
20 | - message_content: 'first', | |
21 | - message_content_mini: 'first', | |
22 | - avatar: 'first', | |
23 | 7 … | avatar_name: 'first', |
24 | 8 … | avatar_link: 'first', |
25 | - message_meta: 'map', | |
26 | 9 … | message_action: 'map', |
10 … | + message_author: 'first', | |
11 … | + message_backlinks: 'first', | |
12 … | + message_content: 'first', | |
13 … | + message_content_mini: 'first', | |
14 … | + message_title: 'first', | |
27 | 15 … | message_link: 'first', |
28 | -// sbot_links: 'first' | |
16 … | + message_meta: 'map', | |
29 | 17 … | } |
30 | 18 … | |
31 | -exports.gives = 'message_render' | |
32 | - | |
33 | -function message_content_mini_fallback(msg) { | |
34 | - return h('code', msg.value.content.type) | |
19 … | +exports.gives = { | |
20 … | + message_render: true, | |
21 … | + mcss: true | |
35 | 22 … | } |
36 | 23 … | |
37 | 24 … | exports.create = function (api) { |
38 | - | |
39 | - function mini(msg, el) { | |
40 | - var div = h('div.message.message--mini', | |
41 | - h('div.row', | |
42 | - h('div', | |
43 | - api.avatar_link(msg.value.author, api.avatar_name(msg.value.author)), | |
44 | - h('span.message_content', el)), | |
45 | - h('div.message_meta.row', api.message_meta(msg)) | |
46 | - ) | |
47 | - ) | |
48 | - div.setAttribute('tabindex', '0') | |
49 | - return div | |
25 … | + return { | |
26 … | + message_render, | |
27 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
50 | 28 … | } |
51 | 29 … | |
52 | - return function (msg, sbot) { | |
53 | - var el = api.message_content_mini(msg) | |
54 | - if(el) return mini(msg, el) | |
30 … | + function message_render (msg) { | |
31 … | + var content = api.message_content_mini(msg) | |
32 … | + if (content) return mini(msg, content) | |
55 | 33 … | |
56 | - var el = api.message_content(msg) | |
57 | - if(!el) return mini(msg, message_content_mini_fallback(msg)) | |
34 … | + content = api.message_content(msg) | |
35 … | + if (!content) return mini(msg, message_content_mini_fallback(msg)) | |
58 | 36 … | |
59 | - var links = [] | |
60 | - for(var k in CACHE) { | |
61 | - var _msg = CACHE[k] | |
62 | - if(Array.isArray(_msg.content.mentions)) { | |
63 | - for(var i = 0; i < _msg.content.mentions.length; i++) | |
64 | - if(_msg.content.mentions[i].link == msg.key) | |
65 | - links.push(k) | |
37 … | + var msgEl = h('Message', { | |
38 … | + 'ev-keydown': navigateToMessageOnEnter, | |
39 … | + attributes: { | |
40 … | + tabindex: '0' | |
66 | 41 … | } |
67 | - } | |
42 … | + }, [ | |
43 … | + h('header.author', api.message_author(msg)), | |
44 … | + h('section.title', api.message_title(msg)), | |
45 … | + h('section.meta', api.message_meta(msg)), | |
46 … | + h('section.content', content), | |
47 … | + h('section.action', api.message_action(msg)), | |
48 … | + h('footer.backlinks', api.message_backlinks(msg)) | |
49 … | + ]) | |
50 … | + return msgEl | |
68 | 51 … | |
69 | - var backlinks = h('div.backlinks') | |
70 | - if(links.length) | |
71 | - backlinks.appendChild(h('label', 'backlinks:', | |
72 | - h('div', links.map(function (key) { | |
73 | - return api.message_link(key) | |
74 | - })) | |
75 | - )) | |
52 … | + function navigateToMessageOnEnter (ev) { | |
53 … | + // on enter, hit first meta. | |
54 … | + if(ev.keyCode == 13) { | |
76 | 55 … | |
56 … | + // unless in an input | |
57 … | + if (ev.target.nodeName === 'INPUT' | |
58 … | + || ev.target.nodeName === 'TEXTAREA') return | |
77 | 59 … | |
78 | - // pull( | |
79 | - // sbot_links({dest: msg.key, rel: 'mentions', keys: true}), | |
80 | - // pull.collect(function (err, links) { | |
81 | - // if(links.length) | |
82 | - // backlinks.appendChild(h('label', 'backlinks:', | |
83 | - // h('div', links.map(function (link) { | |
84 | - // return message_link(link.key) | |
85 | - // })) | |
86 | - // )) | |
87 | - // }) | |
88 | - // ) | |
60 … | + // HACK! (mw) | |
61 … | + // there's no exported api to open a new tab. :/ | |
62 … | + // it's only done in `app.js` module in an`onhashchange` handler. | |
63 … | + // sooooooo yeah this shit for now :) | |
64 … | + var wtf = h('a', { href: `#${msg.key}` }) | |
65 … | + msgEl.appendChild(wtf) | |
66 … | + wtf.click() | |
67 … | + msgEl.removeChild(wtf) | |
68 … | + } | |
69 … | + } | |
70 … | + } | |
89 | 71 … | |
90 | - var msg = h('div.message', | |
91 | - h('div.title.row', | |
92 | - h('div.avatar', api.avatar(msg.value.author, 'thumbnail')), | |
93 | - h('div.message_meta.row', api.message_meta(msg)) | |
94 | - ), | |
95 | - h('div.message_content', el), | |
96 | - h('div.message_actions.row', | |
97 | - h('div.actions', api.message_action(msg), | |
98 | - h('a', {href: '#' + msg.key}, 'Reply') | |
99 | - ) | |
100 | - ), | |
101 | - backlinks, | |
102 | - {onkeydown: function (ev) { | |
103 | - //on enter, hit first meta. | |
104 | - if(ev.keyCode == 13) { | |
72 … | + function mini(msg, el) { | |
73 … | + return h('Message -mini', { | |
74 … | + attributes: { | |
75 … | + tabindex: '0' | |
76 … | + } | |
77 … | + }, [ | |
78 … | + h('header.author', api.message_author(msg, { size: 'mini' })), | |
79 … | + h('section.meta', api.message_meta(msg)), | |
80 … | + h('section.content', el) | |
81 … | + ]) | |
82 … | + } | |
83 … | +} | |
105 | 84 … | |
106 | - // unless in an input | |
107 | - if (ev.target.nodeName === 'INPUT' | |
108 | - || ev.target.nodeName === 'TEXTAREA') return | |
109 | 85 … | |
110 | - msg.querySelector('.enter').click() | |
111 | - } | |
112 | - }} | |
113 | - ) | |
114 | - | |
115 | - // ); hyperscript does not seem to set attributes correctly. | |
116 | - msg.setAttribute('tabindex', '0') | |
117 | - | |
118 | - return msg | |
119 | - } | |
86 … | +function message_content_mini_fallback(msg) { | |
87 … | + return h('code', msg.value.content.type) | |
120 | 88 … | } |
121 | 89 … | |
122 | - |
modules_basic/post.js | ||
---|---|---|
@@ -9,24 +9,35 @@ | ||
9 | 9 … | //var message_link = plugs.first(exports.message_link = []) |
10 | 10 … | //var markdown = plugs.first(exports.markdown = []) |
11 | 11 … | // |
12 | 12 … | |
13 | -exports.needs = { message_link: 'first', markdown: 'first' } | |
13 … | +exports.needs = { | |
14 … | + message_link: 'first', | |
15 … | + markdown: 'first' | |
16 … | +} | |
14 | 17 … | |
15 | -exports.gives = 'message_content' | |
18 … | +exports.gives = { | |
19 … | + message_content: true, | |
20 … | + message_title: true | |
21 … | +} | |
16 | 22 … | |
17 | 23 … | exports.create = function (api) { |
18 | - return function (data) { | |
24 … | + return { | |
25 … | + message_content, | |
26 … | + message_title | |
27 … | + } | |
28 … | + | |
29 … | + function message_content (data) { | |
19 | 30 … | if(!data.value.content || !data.value.content.text) return |
20 | 31 … | |
21 | - var root = data.value.content.root | |
22 | - var re = !root ? null : h('span', 're: ', api.message_link(root)) | |
23 | - | |
24 | 32 … | return h('div', |
25 | - re, | |
26 | 33 … | api.markdown(data.value.content) |
27 | 34 … | ) |
35 … | + } | |
28 | 36 … | |
37 … | + function message_title (data) { | |
38 … | + var root = data.value.content && data.value.content.root | |
39 … | + return !root ? null : h('span', 're: ', api.message_link(root)) | |
29 | 40 … | } |
30 | 41 … | } |
31 | 42 … | |
32 | 43 … |
modules_basic/private.js | ||
---|---|---|
@@ -4,18 +4,8 @@ | ||
4 | 4 … | var pull = require('pull-stream') |
5 | 5 … | var Scroller = require('pull-scroll') |
6 | 6 … | var ref = require('ssb-ref') |
7 | 7 … | |
8 | -//var plugs = require('../plugs') | |
9 | -// | |
10 | -//var message_render = plugs.first(exports.message_render = []) | |
11 | -//var message_compose = plugs.first(exports.message_compose = []) | |
12 | -//var message_unbox = plugs.first(exports.message_unbox = []) | |
13 | -//var sbot_log = plugs.first(exports.sbot_log = []) | |
14 | -//var sbot_whoami = plugs.first(exports.sbot_whoami = []) | |
15 | -//var avatar_image_link = plugs.first(exports.avatar_image_link = []) | |
16 | -//var emoji_url = plugs.first(exports.emoji_url = []) | |
17 | - | |
18 | 8 … | function map(ary, iter) { |
19 | 9 … | if(Array.isArray(ary)) return ary.map(iter) |
20 | 10 … | } |
21 | 11 … | |
@@ -25,9 +15,9 @@ | ||
25 | 15 … | message_unbox: 'first', |
26 | 16 … | sbot_log: 'first', |
27 | 17 … | sbot_whoami: 'first', |
28 | 18 … | avatar_image_link: 'first', |
29 | -// emoji_link: 'first' | |
19 … | + emoji_url: 'first' | |
30 | 20 … | } |
31 | 21 … | |
32 | 22 … | exports.gives = { |
33 | 23 … | builtin_tabs: true, |
@@ -115,9 +105,9 @@ | ||
115 | 105 … | }, |
116 | 106 … | |
117 | 107 … | message_content_mini: function (msg, sbot) { |
118 | 108 … | if (typeof msg.value.content === 'string') { |
119 | - var icon = false //api.emoji_url('lock') | |
109 … | + var icon = api.emoji_url('lock') | |
120 | 110 … | return icon |
121 | 111 … | ? h('img', {className: 'emoji', src: icon}) |
122 | 112 … | : 'PRIVATE' |
123 | 113 … | } |
modules_basic/thread.js | ||
---|---|---|
@@ -120,10 +120,10 @@ | ||
120 | 120 … | meta.root = thread[0].value.content.root || thread[0].key |
121 | 121 … | meta.channel = thread[0].value.content.channel |
122 | 122 … | |
123 | 123 … | var recps = thread[0].value.content.recps |
124 | - var private = thread[0].value.private | |
125 | - if(private) { | |
124 … | + var priv = thread[0].value['private'] | |
125 … | + if(priv) { | |
126 | 126 … | if(recps) |
127 | 127 … | meta.recps = recps |
128 | 128 … | else |
129 | 129 … | meta.recps = [thread[0].value.author, self_id] |
modules_basic/timestamp.js | ||
---|---|---|
@@ -1,28 +1,46 @@ | ||
1 | -var h = require('hyperscript') | |
2 | -var human = require('human-time') | |
1 … | +const fs = require('fs') | |
2 … | +const h = require('../h') | |
3 … | +const human = require('human-time') | |
3 | 4 … | |
4 | 5 … | exports.needs = {} |
5 | 6 … | |
6 | -exports.gives = 'message_meta' | |
7 … | +exports.gives = { | |
8 … | + timestamp: true, | |
9 … | + mcss: true | |
10 … | +} | |
7 | 11 … | |
8 | 12 … | exports.create = function () { |
9 | - | |
10 | - function updateTimestampEl(el) { | |
11 | - el.firstChild.nodeValue = human(new Date(el.timestamp)) | |
12 | - return el | |
13 | - } | |
14 | - | |
15 | 13 … | setInterval(function () { |
16 | - var els = [].slice.call(document.querySelectorAll('.timestamp')) | |
14 … | + var els = [].slice.call(document.querySelectorAll('.Timestamp')) | |
17 | 15 … | els.forEach(updateTimestampEl) |
18 | 16 … | }, 60e3) |
19 | 17 … | |
20 | - return function (msg) { | |
21 | - return updateTimestampEl(h('a.enter.timestamp', { | |
22 | - href: '#'+msg.key, | |
23 | - timestamp: msg.value.timestamp, | |
24 | - title: new Date(msg.value.timestamp) | |
25 | - }, '')) | |
18 … | + return { | |
19 … | + timestamp, | |
20 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
26 | 21 … | } |
27 | 22 … | |
23 … | + function updateTimestampEl (el) { | |
24 … | + var timestamp = Number(el.getAttribute('data-timestamp')) | |
25 … | + var display = human(new Date(timestamp)).replace(/minutes/, 'mins') | |
26 … | + el.querySelector('a').firstChild.nodeValue = display | |
27 … | + return el | |
28 … | + } | |
29 … | + | |
30 … | + function timestamp (msg) { | |
31 … | + var { key, value } = msg | |
32 … | + var { timestamp } = value | |
33 … | + var el = h('Timestamp', { | |
34 … | + attributes: { | |
35 … | + 'data-timestamp': timestamp | |
36 … | + } | |
37 … | + }, [ | |
38 … | + h('a', { | |
39 … | + href: `#${key}`, | |
40 … | + title: new Date(timestamp) | |
41 … | + }, '') | |
42 … | + ]) | |
43 … | + updateTimestampEl(el) | |
44 … | + return el | |
45 … | + } | |
28 | 46 … | } |
modules_basic/about.mcss | ||
---|---|---|
@@ -1,0 +1,53 @@ | ||
1 … | +About { | |
2 … | + display: flex | |
3 … | + flex-wrap: wrap | |
4 … | + | |
5 … | + section { | |
6 … | + header { | |
7 … | + a { | |
8 … | + -target { | |
9 … | + | |
10 … | + } | |
11 … | + -name { | |
12 … | + | |
13 … | + } | |
14 … | + -image { | |
15 … | + | |
16 … | + } | |
17 … | + } | |
18 … | + } | |
19 … | + | |
20 … | + -name { | |
21 … | + display: flex | |
22 … | + | |
23 … | + header { | |
24 … | + | |
25 … | + } | |
26 … | + | |
27 … | + section { | |
28 … | + margin-left: .25rem | |
29 … | + } | |
30 … | + } | |
31 … | + | |
32 … | + -image { | |
33 … | + header { | |
34 … | + | |
35 … | + } | |
36 … | + | |
37 … | + section { | |
38 … | + | |
39 … | + } | |
40 … | + } | |
41 … | + | |
42 … | + -description { | |
43 … | + header { | |
44 … | + | |
45 … | + } | |
46 … | + | |
47 … | + section { | |
48 … | + padding-left: 1em; | |
49 … | + padding-right: 1em; | |
50 … | + } | |
51 … | + } | |
52 … | + } | |
53 … | +} |
modules_basic/compose.mcss | ||
---|---|---|
@@ -1,0 +1,90 @@ | ||
1 … | +Compose { | |
2 … | + display: flex | |
3 … | + flex-direction: column | |
4 … | + | |
5 … | + padding: 1rem .5rem 1rem 7.3rem | |
6 … | + | |
7 … | + textarea { | |
8 … | + border: 1px solid gainsboro | |
9 … | + } | |
10 … | + | |
11 … | + section.actions { | |
12 … | + display: flex | |
13 … | + flex-direction: row | |
14 … | + align-items: baseline | |
15 … | + | |
16 … | + margin-top: .4rem | |
17 … | + | |
18 … | + input[type="file"] { | |
19 … | + flex-grow: 1 | |
20 … | + | |
21 … | + padding: .5rem 0 | |
22 … | + | |
23 … | + width: 5.5rem | |
24 … | + color: transparent | |
25 … | + | |
26 … | + ::-webkit-file-upload-button { | |
27 … | + visibility: hidden | |
28 … | + } | |
29 … | + | |
30 … | + ::before { | |
31 … | + $composeButton | |
32 … | + padding-top: .3rem | |
33 … | + | |
34 … | + content: '📎' | |
35 … | + font-size: 1rem | |
36 … | + | |
37 … | + outline: none | |
38 … | + white-space: nowrap | |
39 … | + -webkit-user-select: none | |
40 … | + } | |
41 … | + | |
42 … | + :active, :focus { | |
43 … | + outline: none | |
44 … | + box-shadow: none | |
45 … | + } | |
46 … | + } | |
47 … | + | |
48 … | + button { | |
49 … | + $composeButton | |
50 … | + | |
51 … | + text-transform: uppercase | |
52 … | + font-weight: bold | |
53 … | + font-size: .7rem | |
54 … | + } | |
55 … | + } | |
56 … | + | |
57 … | + -expanded { | |
58 … | + textarea { | |
59 … | + height: 200px | |
60 … | + transition: height .15s ease-out | |
61 … | + } | |
62 … | + | |
63 … | + section.actions { | |
64 … | + display: flex | |
65 … | + } | |
66 … | + } | |
67 … | + | |
68 … | + -contracted { | |
69 … | + textarea { | |
70 … | + height: 50px | |
71 … | + transition: height .15s ease-in | |
72 … | + } | |
73 … | + | |
74 … | + section.actions { | |
75 … | + display: none | |
76 … | + } | |
77 … | + } | |
78 … | + | |
79 … | +} | |
80 … | + | |
81 … | +$composeButton { | |
82 … | + background: #fff | |
83 … | + color: #666 | |
84 … | + border: 1px solid #bbb | |
85 … | + border-radius: .5rem | |
86 … | + padding: .5rem | |
87 … | + margin: 0 | |
88 … | + cursor: pointer | |
89 … | +} | |
90 … | + |
modules_basic/message-author.js | ||
---|---|---|
@@ -1,0 +1,39 @@ | ||
1 … | +const fs = require('fs') | |
2 … | +const h = require('../h') | |
3 … | +const { when }= require('@mmckegg/mutant') | |
4 … | + | |
5 … | +exports.needs = { | |
6 … | + avatar_link: 'first', | |
7 … | + avatar_image: 'first', | |
8 … | + avatar_name: 'first', | |
9 … | + timestamp: 'first' | |
10 … | +} | |
11 … | + | |
12 … | +exports.gives = { | |
13 … | + message_author: true, | |
14 … | + mcss: true | |
15 … | +} | |
16 … | + | |
17 … | +exports.create = function (api) { | |
18 … | + return { | |
19 … | + message_author, | |
20 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
21 … | + } | |
22 … | + | |
23 … | + function message_author (msg, opts = {}) { | |
24 … | + var { size = 'small' } = opts | |
25 … | + var { value } = msg | |
26 … | + var { author } = value | |
27 … | + | |
28 … | + return h('MessageAuthor', { | |
29 … | + className: `-${size}` | |
30 … | + }, [ | |
31 … | + when(size !== 'mini', | |
32 … | + h('section -image', api.avatar_link(author, api.avatar_image(author, 'thumbnail'))) | |
33 … | + ), | |
34 … | + h('section -name', api.avatar_link(author, api.avatar_name(author))), | |
35 … | + h('section -timestamp', api.timestamp(msg)) | |
36 … | + ]) | |
37 … | + } | |
38 … | +} | |
39 … | + |
modules_basic/message-author.mcss | ||
---|---|---|
@@ -1,0 +1,38 @@ | ||
1 … | +MessageAuthor { | |
2 … | + display: flex | |
3 … | + flex-direction: column | |
4 … | + | |
5 … | + section { | |
6 … | + -image { | |
7 … | + margin-bottom: .3rem | |
8 … | + } | |
9 … | + | |
10 … | + -name { | |
11 … | + max-width: 7rem | |
12 … | + a { $textPrimary } | |
13 … | + } | |
14 … | + | |
15 … | + -timestamp { | |
16 … | + | |
17 … | + } | |
18 … | + } | |
19 … | + | |
20 … | + -mini { | |
21 … | + flex-direction: row | |
22 … | + | |
23 … | + section { | |
24 … | + margin-right: .5rem | |
25 … | + | |
26 … | + -name { | |
27 … | + position: initial | |
28 … | + left: initial | |
29 … | + min-width: 6.5rem | |
30 … | + max-width: none | |
31 … | + } | |
32 … | + | |
33 … | + -timestamp { | |
34 … | + | |
35 … | + } | |
36 … | + } | |
37 … | + } | |
38 … | +} |
modules_basic/message-backlinks.js | ||
---|---|---|
@@ -1,0 +1,48 @@ | ||
1 … | +const fs = require('fs') | |
2 … | +const h = require('../h') | |
3 … | + | |
4 … | +exports.needs = { | |
5 … | + message_name: 'first' | |
6 … | +} | |
7 … | + | |
8 … | +exports.gives = { | |
9 … | + message_backlinks: true, | |
10 … | + mcss: true | |
11 … | +} | |
12 … | + | |
13 … | +exports.create = function (api) { | |
14 … | + return { | |
15 … | + message_backlinks, | |
16 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
17 … | + } | |
18 … | + | |
19 … | + function message_backlinks (msg) { | |
20 … | + var links = [] | |
21 … | + for(var k in CACHE) { | |
22 … | + var _msg = CACHE[k] | |
23 … | + var mentions = _msg.content.mentions | |
24 … | + | |
25 … | + if(Array.isArray(mentions)) { | |
26 … | + for(var i = 0; i < mentions.length; i++) | |
27 … | + if(mentions[i].link == msg.key) | |
28 … | + links.push(k) | |
29 … | + } | |
30 … | + } | |
31 … | + | |
32 … | + if (links.length === 0) return null | |
33 … | + | |
34 … | + var hrefList = h('ul') | |
35 … | + links.forEach(link => { | |
36 … | + api.message_name(link, (err, name) => { | |
37 … | + if (err) throw err | |
38 … | + hrefList.appendChild(h('li', | |
39 … | + h('a -backlink', { href: `#${link}` }, name) | |
40 … | + )) | |
41 … | + }) | |
42 … | + }) | |
43 … | + return h('MessageBacklinks', [ | |
44 … | + h('header', 'backlinks:'), | |
45 … | + hrefList | |
46 … | + ]) | |
47 … | + } | |
48 … | +} |
modules_basic/message-backlinks.mcss | ||
---|---|---|
@@ -1,0 +1,21 @@ | ||
1 … | +MessageBacklinks { | |
2 … | + font-size: .9rem | |
3 … | + margin-top: .5rem | |
4 … | + | |
5 … | + header { | |
6 … | + $textSubtle | |
7 … | + } | |
8 … | + | |
9 … | + ul { | |
10 … | + padding-left: 1rem | |
11 … | + | |
12 … | + li { | |
13 … | + a { | |
14 … | + -backlink { | |
15 … | + $textSubtle | |
16 … | + | |
17 … | + } | |
18 … | + } | |
19 … | + } | |
20 … | + } | |
21 … | +} |
modules_basic/message.mcss | ||
---|---|---|
@@ -1,0 +1,80 @@ | ||
1 … | +Message { | |
2 … | + padding: 1rem .5rem 1rem 7.5rem | |
3 … | + border-top: solid 1px gainsboro | |
4 … | + min-height: 5rem | |
5 … | + | |
6 … | + position: relative | |
7 … | + display: flex | |
8 … | + flex-direction: row | |
9 … | + flex-wrap: wrap | |
10 … | + justify-content: flex-end | |
11 … | + | |
12 … | + header.author { | |
13 … | + position: absolute | |
14 … | + left: .5rem | |
15 … | + } | |
16 … | + | |
17 … | + section.title { | |
18 … | + flex-grow: 1 | |
19 … | + font-size: .9rem | |
20 … | + } | |
21 … | + | |
22 … | + section.meta { | |
23 … | + display: flex | |
24 … | + a { | |
25 … | + margin-left: .2rem | |
26 … | + $textSubtle | |
27 … | + } | |
28 … | + | |
29 … | + input{ | |
30 … | + margin-right: 0 | |
31 … | + order: 99 | |
32 … | + } | |
33 … | + } | |
34 … | + | |
35 … | + section.content { | |
36 … | + flex-basis: 100% | |
37 … | + | |
38 … | + (img) { | |
39 … | + max-width: 100% | |
40 … | + } | |
41 … | + } | |
42 … | + | |
43 … | + section.action { | |
44 … | + flex-basis: 100% | |
45 … | + display: flex | |
46 … | + justify-content: flex-end | |
47 … | + | |
48 … | + a { | |
49 … | + margin-left: .5em | |
50 … | + } | |
51 … | + } | |
52 … | + | |
53 … | + footer.backlinks { | |
54 … | + flex-basis: 100% | |
55 … | + } | |
56 … | + | |
57 … | + | |
58 … | + -mini { | |
59 … | + font-size: .9rem | |
60 … | + justify-content: flex-start | |
61 … | + padding: .25rem .5rem | |
62 … | + min-height: inherit | |
63 … | + | |
64 … | + header.author { | |
65 … | + order: 0 | |
66 … | + position: initial | |
67 … | + left: initial | |
68 … | + } | |
69 … | + | |
70 … | + section.content { | |
71 … | + order: 1 | |
72 … | + flex-basis: initial | |
73 … | + flex-grow: 1 | |
74 … | + } | |
75 … | + | |
76 … | + section.meta { | |
77 … | + order: 2 | |
78 … | + } | |
79 … | + } | |
80 … | +} |
modules_basic/reply.js | ||
---|---|---|
@@ -1,0 +1,9 @@ | ||
1 … | +var h = require('../h') | |
2 … | + | |
3 … | +exports.gives = 'message_action' | |
4 … | + | |
5 … | +exports.create = function () { | |
6 … | + return function (msg) { | |
7 … | + return h('a', { href: '#' + msg.key }, 'Reply') | |
8 … | + } | |
9 … | +} |
modules_basic/timestamp.mcss | ||
---|---|---|
@@ -1,0 +1,7 @@ | ||
1 … | +Timestamp { | |
2 … | + a { | |
3 … | + $textSubtle | |
4 … | + font-size: .8rem | |
5 … | + } | |
6 … | +} | |
7 … | + |
modules_core/app.js | ||
---|---|---|
@@ -1,13 +1,19 @@ | ||
1 | 1 … | var plugs = require('../plugs') |
2 | 2 … | var h = require('hyperscript') |
3 … | +var insertCss = require('insert-css') | |
3 | 4 … | |
4 | 5 … | module.exports = { |
5 | - needs: {screen_view: 'first'}, | |
6 … | + needs: { | |
7 … | + screen_view: 'first', | |
8 … | + styles: 'first' | |
9 … | + }, | |
6 | 10 … | gives: 'app', |
7 | 11 … | create: function (api) { |
8 | 12 … | return function () { |
9 | - document.head.appendChild(h('style', require('../style.css.json'))) | |
13 … | + process.nextTick(function () { | |
14 … | + insertCss(api.styles()) | |
15 … | + }) | |
10 | 16 … | |
11 | 17 … | window.addEventListener('error', window.onError = function (e) { |
12 | 18 … | document.body.appendChild(h('div.error', |
13 | 19 … | h('h1', e.message), |
modules_core/file-input.js | ||
---|---|---|
@@ -3,16 +3,12 @@ | ||
3 | 3 … | var pull = require('pull-stream') |
4 | 4 … | var mime = require('simple-mime')('application/octect-stream') |
5 | 5 … | var split = require('split-buffer') |
6 | 6 … | |
7 | -var plugs = require('../plugs') | |
8 | - | |
9 | -var add = plugs.first(exports.sbot_blobs_add = []) | |
10 | - | |
11 | 7 … | module.exports = { |
12 | 8 … | needs: {sbot_blobs_add: 'first'}, |
13 | 9 … | gives: 'file_input', |
14 | - create: function () { | |
10 … | + create: function (api) { | |
15 | 11 … | return function FileInput(onAdded) { |
16 | 12 … | return h('input', { type: 'file', |
17 | 13 … | onchange: function (ev) { |
18 | 14 … | var file = ev.target.files[0] |
@@ -20,9 +16,9 @@ | ||
20 | 16 … | var reader = new FileReader() |
21 | 17 … | reader.onload = function () { |
22 | 18 … | pull( |
23 | 19 … | pull.values(split(new Buffer(reader.result), 64*1024)), |
24 | - add(function (err, blob) { | |
20 … | + api.sbot_blobs_add(function (err, blob) { | |
25 | 21 … | if(err) return console.error(err) |
26 | 22 … | onAdded({ |
27 | 23 … | link: blob, |
28 | 24 … | name: file.name, |
modules_core/index.js | ||
---|---|---|
@@ -2,11 +2,14 @@ | ||
2 | 2 … | // "_screen_view.js": require('./_screen_view.js'), |
3 | 3 … | "app.js": require('./app.js'), |
4 | 4 … | "blob-url.js": require('./blob-url.js'), |
5 | 5 … | "crypto.js": require('./crypto.js'), |
6 … | + "external-confirm.js": require('./external-confirm.js'), | |
6 | 7 … | "file-input.js": require('./file-input.js'), |
7 | 8 … | "menu.js": require('./menu.js'), |
8 | 9 … | "message-confirm.js": require('./message-confirm.js'), |
9 | 10 … | "tabs.js": require('./tabs.js'), |
10 | - "sbot.js": require('./sbot.js') | |
11 … | + "sbot.js": require('./sbot.js'), | |
12 … | + "styles.js": require('./styles.js'), | |
13 … | + "style-mixins.js": require('./style-mixins.js') | |
11 | 14 … | } |
12 | 15 … |
modules_core/message-confirm.js | ||
---|---|---|
@@ -1,27 +1,33 @@ | ||
1 … | +var fs = require('fs') | |
1 | 2 … | var lightbox = require('hyperlightbox') |
2 | -var h = require('hyperscript') | |
3 … | +var h = require('../h') | |
3 | 4 … | var u = require('../util') |
4 | 5 … | var self_id = require('../keys').id |
5 | 6 … | //publish or add |
6 | 7 … | |
7 | 8 … | var plugs = require('../plugs') |
8 | 9 … | |
9 | 10 … | exports.needs = { |
10 | - publish: 'first', message_content: 'first', avatar: 'first', | |
11 … | + publish: 'first', | |
12 … | + message_render: 'first', | |
13 … | + avatar: 'first', | |
11 | 14 … | message_meta: 'map' |
12 | 15 … | } |
13 | 16 … | |
14 | -exports.gives = 'message_confirm' | |
17 … | +exports.gives = { | |
18 … | + message_confirm: 'true', | |
19 … | + mcss: 'true' | |
20 … | +} | |
15 | 21 … | |
16 | -//var publish = plugs.first(exports.sbot_publish = []) | |
17 | -//var message_content = plugs.first(exports.message_content = []) | |
18 | -//var avatar = plugs.first(exports.avatar = []) | |
19 | -//var message_meta = plugs.map(exports.message_meta = []) | |
20 | -// | |
21 | 22 … | exports.create = function (api) { |
22 | - return function (content, cb) { | |
23 … | + return { | |
24 … | + message_confirm, | |
25 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
26 … | + } | |
23 | 27 … | |
28 … | + function message_confirm (content, cb) { | |
29 … | + | |
24 | 30 … | cb = cb || function () {} |
25 | 31 … | |
26 | 32 … | var lb = lightbox() |
27 | 33 … | document.body.appendChild(lb) |
@@ -36,32 +42,33 @@ | ||
36 | 42 … | content: content |
37 | 43 … | } |
38 | 44 … | } |
39 | 45 … | |
40 | - var okay = h('button', 'okay', {onclick: function () { | |
41 | - lb.remove() | |
42 | - api.publish(content, cb) | |
43 | - }}) | |
46 … | + var okay = h('button', { | |
47 … | + 'ev-click': () => { | |
48 … | + lb.remove() | |
49 … | + api.publish(content, cb) | |
50 … | + }}, | |
51 … | + 'okay' | |
52 … | + ) | |
44 | 53 … | |
45 | - var cancel = h('button', 'Cancel', {onclick: function () { | |
46 | - lb.remove() | |
47 | - cb(null) | |
48 | - }}) | |
54 … | + var cancel = h('button', { | |
55 … | + 'ev-click': () => { | |
56 … | + lb.remove() | |
57 … | + cb(null) | |
58 … | + }}, | |
59 … | + 'Cancel' | |
60 … | + ) | |
49 | 61 … | |
50 | 62 … | okay.addEventListener('keydown', function (ev) { |
51 | 63 … | if(ev.keyCode === 27) cancel.click() //escape |
52 | 64 … | }) |
53 | 65 … | |
54 | - lb.show(h('div.column.message-confirm', | |
55 | - h('div.message', | |
56 | - h('div.title.row', | |
57 | - h('div.avatar', api.avatar(msg.value.author, 'thumbnail')), | |
58 | - h('div.message_meta.row', api.message_meta(msg)) | |
59 | - ), | |
60 | - h('div.message_content', api.message_content(msg) | |
61 | - || h('pre', JSON.stringify(msg, null, 2))) | |
62 | - ), | |
63 | - h('div.row.message-confirm__controls', okay, cancel) | |
66 … | + lb.show(h('MessageConfirm', [ | |
67 … | + h('header -preview_description', h('h1', 'Preview')), | |
68 … | + h('section -message_preview', api.message_render(msg)), | |
69 … | + h('section -actions', [okay, cancel]) | |
70 … | + ] | |
64 | 71 … | )) |
65 | 72 … | |
66 | 73 … | okay.focus() |
67 | 74 … | } |
modules_core/sbot.js | ||
---|---|---|
@@ -66,9 +66,10 @@ | ||
66 | 66 … | } |
67 | 67 … | |
68 | 68 … | createClient(keys, { |
69 | 69 … | manifest: require('../manifest.json'), |
70 | - remote: require('../config')().remote | |
70 … | + remote: require('../config')().remote, | |
71 … | + caps: config.caps | |
71 | 72 … | }, function (err, _sbot) { |
72 | 73 … | if(err) |
73 | 74 … | return notify(err) |
74 | 75 … |
modules_core/tabs.js | ||
---|---|---|
@@ -15,9 +15,9 @@ | ||
15 | 15 … | //var screen_view = plugs.first(exports._screen_view = []) |
16 | 16 … | //var search_box = plugs.first(exports.search_box = []) |
17 | 17 … | //var menu = plugs.first(exports.menu = []) |
18 | 18 … | |
19 | -exports.needs = {screen_view: 'first', search_box: 'first', menu: 'first'} | |
19 … | +exports.needs = {screen_view: 'first', search_box: 'first', menu: 'first', 'external_confirm':'first'} | |
20 | 20 … | |
21 | 21 … | exports.gives = 'screen_view' |
22 | 22 … | |
23 | 23 … | exports.create = function (api) { |
@@ -94,9 +94,9 @@ | ||
94 | 94 … | if (link.getAttribute('href') === '#') return |
95 | 95 … | |
96 | 96 … | //open external links. |
97 | 97 … | //this ought to be made into something more runcible |
98 | - if(open.isExternal(link.href)) return open(link.href) | |
98 … | + if(link.href && open.isExternal(link.href)) return api.external_confirm(link.href) | |
99 | 99 … | |
100 | 100 … | if(tabs.has(path)) |
101 | 101 … | return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey) |
102 | 102 … |
modules_core/external-confirm.js | ||
---|---|---|
@@ -1,0 +1,44 @@ | ||
1 … | +var lightbox = require('hyperlightbox') | |
2 … | +var h = require('hyperscript') | |
3 … | +var open = require('open-external') | |
4 … | + | |
5 … | +exports.gives = 'external_confirm' | |
6 … | + | |
7 … | +exports.create = function (api) { | |
8 … | + return function (href) { | |
9 … | + var lb = lightbox() | |
10 … | + document.body.appendChild(lb) | |
11 … | + | |
12 … | + var okay = h('button', 'open', {onclick: function () { | |
13 … | + lb.remove() | |
14 … | + open(href) | |
15 … | + }}) | |
16 … | + | |
17 … | + var cancel = h('button', 'Cancel', {onclick: function () { | |
18 … | + lb.remove() | |
19 … | + }}) | |
20 … | + | |
21 … | + okay.addEventListener('keydown', function (ev) { | |
22 … | + if (ev.keyCode === 27) cancel.click() // escape | |
23 … | + }) | |
24 … | + | |
25 … | + lb.show(h('div.column', | |
26 … | + h('div', [ | |
27 … | + h('div.title.row', [ | |
28 … | + h('strong.row', [ | |
29 … | + 'Do you want to open this external link in your default browser:' | |
30 … | + ]) | |
31 … | + ]), | |
32 … | + h('div', [ | |
33 … | + h('pre', href) | |
34 … | + ]) | |
35 … | + ]), | |
36 … | + h('div.row', [ | |
37 … | + okay, | |
38 … | + cancel | |
39 … | + ]) | |
40 … | + )) | |
41 … | + | |
42 … | + okay.focus() | |
43 … | + } | |
44 … | +} |
modules_core/message-confirm.mcss | ||
---|---|---|
@@ -1,0 +1,39 @@ | ||
1 … | +MessageConfirm { | |
2 … | + section { | |
3 … | + -preview_description { | |
4 … | + } | |
5 … | + | |
6 … | + -message_preview { | |
7 … | + background-color: white | |
8 … | + | |
9 … | + div { | |
10 … | + border: none | |
11 … | + | |
12 … | + header.author { | |
13 … | + div { | |
14 … | + section { | |
15 … | + -timestamp { | |
16 … | + display: none | |
17 … | + } | |
18 … | + } | |
19 … | + } | |
20 … | + } | |
21 … | + | |
22 … | + section.action { | |
23 … | + display: none | |
24 … | + } | |
25 … | + } | |
26 … | + } | |
27 … | + | |
28 … | + -actions { | |
29 … | + margin-top: 1rem | |
30 … | + display: flex | |
31 … | + justify-content: flex-end | |
32 … | + | |
33 … | + button { | |
34 … | + margin: 0 0 0 1rem | |
35 … | + } | |
36 … | + } | |
37 … | + } | |
38 … | +} | |
39 … | + |
modules_core/style-mixins.js | ||
---|---|---|
@@ -1,0 +1,22 @@ | ||
1 … | + | |
2 … | +const mixins = ` | |
3 … | + $textPrimary { | |
4 … | + color: #222 | |
5 … | + } | |
6 … | + | |
7 … | + $textSubtle { | |
8 … | + color: gray | |
9 … | + } | |
10 … | +` | |
11 … | + | |
12 … | +module.exports = { | |
13 … | + gives: { | |
14 … | + mcss: true | |
15 … | + }, | |
16 … | + create: function (api) { | |
17 … | + return { | |
18 … | + mcss: () => mixins | |
19 … | + } | |
20 … | + } | |
21 … | +} | |
22 … | + |
modules_core/styles.js | ||
---|---|---|
@@ -1,0 +1,34 @@ | ||
1 … | +var h = require('../h') | |
2 … | +var compile = require('micro-css') | |
3 … | +var fs = require('fs') | |
4 … | +var Path = require('path') | |
5 … | + | |
6 … | +// TODO distribute these styles across all | |
7 … | +// the relevant modules, not as a core style. | |
8 … | +var coreStyle = fs.readFileSync(Path.join(__dirname, '../style.css')) | |
9 … | + | |
10 … | +module.exports = { | |
11 … | + needs: { | |
12 … | + mcss: 'map', | |
13 … | + css: 'map' | |
14 … | + }, | |
15 … | + gives: { | |
16 … | + mcss: true, | |
17 … | + css: true, | |
18 … | + styles: true | |
19 … | + }, | |
20 … | + create: function (api) { | |
21 … | + var styles = '' | |
22 … | + process.nextTick(function () { | |
23 … | + var mcss = api.mcss().join('\n') | |
24 … | + var css = api.css().join('\n') | |
25 … | + styles = coreStyle + compile(mcss) + css | |
26 … | + }) | |
27 … | + return { | |
28 … | + styles: function () { return styles }, | |
29 … | + // export empty styles | |
30 … | + mcss: function () { return '' }, | |
31 … | + css: function () { return '' } | |
32 … | + } | |
33 … | + } | |
34 … | +} |
modules_extra/channel.js | ||
---|---|---|
@@ -3,14 +3,8 @@ | ||
3 | 3 … | var pull = require('pull-stream') |
4 | 4 … | var Scroller = require('pull-scroll') |
5 | 5 … | var mfr = require('map-filter-reduce') |
6 | 6 … | |
7 | -//var plugs = require('../plugs') | |
8 | -//var message_render = plugs.first(exports.message_render = []) | |
9 | -//var message_compose = plugs.first(exports.message_compose = []) | |
10 | -//var sbot_log = plugs.first(exports.sbot_log = []) | |
11 | -//var sbot_query = plugs.first(exports.sbot_query = []) | |
12 | - | |
13 | 7 … | exports.needs = { |
14 | 8 … | message_render: 'first', |
15 | 9 … | message_compose: 'first', |
16 | 10 … | sbot_log: 'first', |
@@ -60,16 +54,16 @@ | ||
60 | 54 … | |
61 | 55 … | pull( |
62 | 56 … | api.sbot_log({old: false}), |
63 | 57 … | pull.filter(matchesChannel), |
64 | - Scroller(div, content, message_render, true, false) | |
58 … | + Scroller(div, content, api.message_render, true, false) | |
65 | 59 … | ) |
66 | 60 … | |
67 | 61 … | pull( |
68 | 62 … | api.sbot_query({reverse: true, query: [ |
69 | 63 … | {$filter: {value: {content: {channel: channel}}}} |
70 | 64 … | ]}), |
71 | - Scroller(div, content, message_render, false, false) | |
65 … | + Scroller(div, content, api.message_render, false, false) | |
72 | 66 … | ) |
73 | 67 … | |
74 | 68 … | return div |
75 | 69 … | } |
modules_extra/git.js | ||
---|---|---|
@@ -33,9 +33,10 @@ | ||
33 | 33 … | |
34 | 34 … | exports.gives = { |
35 | 35 … | message_action: true, |
36 | 36 … | message_meta: true, |
37 | - message_content: true | |
37 … | + message_content: true, | |
38 … | + message_title: true | |
38 | 39 … | } |
39 | 40 … | |
40 | 41 … | |
41 | 42 … | var self_id = require('../keys').id |
@@ -301,10 +302,8 @@ | ||
301 | 302 … | if(c.type === 'git-repo') { |
302 | 303 … | var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT |
303 | 304 … | var forksT |
304 | 305 … | var div = h('div', |
305 | - h('p', 'git repo ', repoName(msg.key)), | |
306 | - c.upstream ? h('p', 'fork of ', repoLink(c.upstream)) : '', | |
307 | 306 … | h('p', h('code', 'ssb://' + msg.key)), |
308 | 307 … | h('div.git-table-wrapper', {style: {'max-height': '12em'}}, |
309 | 308 … | h('table', |
310 | 309 … | branchesT = tableRows(h('tr', |
@@ -409,9 +408,8 @@ | ||
409 | 408 … | } |
410 | 409 … | |
411 | 410 … | if(c.type === 'git-update') { |
412 | 411 … | return [ |
413 | - h('p', 'pushed to ', repoLink(c.repo)), | |
414 | 412 … | c.refs ? h('ul', Object.keys(c.refs).map(function (ref) { |
415 | 413 … | var rev = c.refs[ref] |
416 | 414 … | return h('li', |
417 | 415 … | shortRefName(ref) + ': ', |
@@ -449,25 +447,54 @@ | ||
449 | 447 … | } |
450 | 448 … | |
451 | 449 … | if(c.type === 'issue') { |
452 | 450 … | return h('div', |
453 | - h('p', 'opened issue on ', repoLink(c.project)), | |
454 | 451 … | c.title ? h('h4', c.title) : '', |
455 | 452 … | api.markdown(c) |
456 | 453 … | ) |
457 | 454 … | } |
458 | 455 … | |
459 | 456 … | if(c.type === 'pull-request') { |
460 | 457 … | return h('div', |
461 | - h('p', 'opened pull-request ', | |
462 | - 'to ', repoLink(c.repo), ':', c.branch, ' ', | |
463 | - 'from ', repoLink(c.head_repo), ':', c.head_branch), | |
464 | 458 … | c.title ? h('h4', c.title) : '', |
465 | 459 … | api.markdown(c) |
466 | 460 … | ) |
467 | 461 … | } |
468 | 462 … | }, |
469 | 463 … | |
464 … | + message_title: function (msg) { | |
465 … | + var c = msg.value.content | |
466 … | + | |
467 … | + if(c.type === 'git-repo') { | |
468 … | + return h('div', [ | |
469 … | + h('p', 'git repo ', repoName(msg.key)), | |
470 … | + c.upstream ? h('p', 'fork of ', repoLink(c.upstream)) : '' | |
471 … | + ]) | |
472 … | + } | |
473 … | + | |
474 … | + if(c.type === 'git-update') { | |
475 … | + return h('p', 'pushed to ', repoLink(c.repo)) | |
476 … | + } | |
477 … | + | |
478 … | + if(c.type === 'issue-edit' || (c.type === 'post' && c.text === '')) { | |
479 … | + return h('div', [ | |
480 … | + c.issue ? renderIssueEdit(c) : null, | |
481 … | + c.issues ? c.issues.map(renderIssueEdit) : null | |
482 … | + ]) | |
483 … | + } | |
484 … | + | |
485 … | + if(c.type === 'issue') { | |
486 … | + return h('p', 'opened issue on ', repoLink(c.project)) | |
487 … | + } | |
488 … | + | |
489 … | + if(c.type === 'pull-request') { | |
490 … | + return h('p', 'opened pull-request ', [ | |
491 … | + 'to ', repoLink(c.repo), ':', c.branch, ' ', | |
492 … | + 'from ', repoLink(c.head_repo), ':', c.head_branch | |
493 … | + ]) | |
494 … | + } | |
495 … | + }, | |
496 … | + | |
470 | 497 … | message_meta: function (msg, sbot) { |
471 | 498 … | var type = msg.value.content.type |
472 | 499 … | if (type === 'issue' || type === 'pull-request') { |
473 | 500 … | var el = h('em', '...') |
@@ -515,5 +542,4 @@ | ||
515 | 542 … | } |
516 | 543 … | } |
517 | 544 … | } |
518 | 545 … | |
519 | - |
modules_extra/index.js | ||
---|---|---|
@@ -1,13 +1,14 @@ | ||
1 | 1 … | module.exports = { |
2 | - "audio-mp3.js": require('./audio-mp3.js'), | |
2 … | + // "audio-mp3.js": require('./audio-mp3.js'), | |
3 | 3 … | "blob.js": require('./blob.js'), |
4 | 4 … | "channel.js": require('./channel.js'), |
5 | 5 … | "emoji.js": require('./emoji.js'), |
6 | 6 … | "suggest-emoji.js": require('./suggest-emoji.js'), |
7 | 7 … | "dns.js": require('./dns.js'), |
8 | 8 … | "git.js": require('./git.js'), |
9 | 9 … | "git-ssb.js": require('./git-ssb.js'), |
10 … | + "key.js": require('./key.js'), | |
10 | 11 … | "notifications.js": require('./notifications.js'), |
11 | 12 … | "meta-image.js": require('./meta-image.js'), |
12 | 13 … | "music-release-cc.js": require('./music-release-cc.js'), |
13 | 14 … | "music-release.js": require('./music-release.js'), |
modules_extra/network.js | ||
---|---|---|
@@ -1,92 +1,84 @@ | ||
1 | -var isVisible = require('is-visible').isVisible | |
2 | -var h = require('hyperscript') | |
1 … | +const fs = require('fs') | |
2 … | +// const { isVisible } = require('is-visible') | |
3 … | +const h = require('../h') | |
4 … | +const human = require('human-time') | |
3 | 5 … | |
6 … | +const { | |
7 … | + Struct, Value, Dict, | |
8 … | + dictToCollection, map: mutantMap, when, computed | |
9 … | +} = require('@mmckegg/mutant') | |
10 … | + | |
4 | 11 … | //var avatar = plugs.first(exports.avatar = []) |
5 | 12 … | //var sbot_gossip_peers = plugs.first(exports.sbot_gossip_peers = []) |
6 | 13 … | //var sbot_gossip_connect = plugs.first(exports.sbot_gossip_connect = []) |
7 | 14 … | |
8 | 15 … | exports.needs = { |
9 | - avatar: 'first', | |
16 … | + avatar_image_link: 'first', | |
17 … | + avatar_name_link: 'first', | |
10 | 18 … | sbot_gossip_peers: 'first', |
11 | 19 … | sbot_gossip_connect: 'first' |
12 | 20 … | } |
13 | 21 … | |
14 | 22 … | exports.gives = { |
15 | 23 … | menu_items: true, |
16 | 24 … | builtin_tabs: true, |
17 | - screen_view: true | |
25 … | + screen_view: true, | |
26 … | + mcss: true | |
18 | 27 … | } |
19 | 28 … | |
20 | 29 … | //sbot_gossip_connect |
21 | 30 … | //sbot_gossip_add |
22 | 31 … | |
23 | -var human = require('human-time') | |
24 | 32 … | |
25 | 33 … | function legacyToMultiServer(addr) { |
26 | 34 … | return 'net:'+addr.host + ':'+addr.port + '~shs:'+addr.key.substring(1).replace('.ed25519','') |
27 | 35 … | } |
28 | 36 … | |
29 | -//types of peers | |
30 | - | |
31 | - | |
32 | 37 … | //on the same wifi network |
33 | -function isLocal (e) { | |
38 … | +function isLocal (peer) { | |
34 | 39 … | // don't rely on private ip address, because |
35 | 40 … | // cjdns creates fake private ip addresses. |
36 | - return ip.isPrivate(e.host) && e.type === 'local' | |
41 … | + return ip.isPrivate(peer.host) && peer.type === 'local' | |
37 | 42 … | } |
38 | 43 … | |
39 | 44 … | |
40 | -//pub is running scuttlebot >=8 | |
41 | -//have connected successfully. | |
42 | -function isLongterm (e) { | |
43 | - return e.ping && e.ping.rtt && e.ping.rtt.mean > 0 | |
44 | -} | |
45 … | +function getType (peer) { | |
46 … | + return ( | |
47 … | + isLongterm(peer) ? 'modern' | |
48 … | + : isLegacy(peer) ? 'legacy' | |
49 … | + : isInactive(peer) ? 'inactive' | |
50 … | + : isUnattempted(peer) ? 'unattempted' | |
51 … | + : 'other' //should never happen | |
52 … | + ) | |
45 | 53 … | |
46 | -//pub is running scuttlebot < 8 | |
47 | -//have connected sucessfully | |
48 | -function isLegacy (peer) { | |
49 | - return /connect/.test(peer.state) || (peer.duration && peer.duration.mean) > 0 && !isLongterm(peer) | |
50 | -} | |
54 … | + //pub is running scuttlebot >=8 | |
55 … | + //have connected successfully. | |
56 … | + function isLongterm (peer) { | |
57 … | + return peer.ping && peer.ping.rtt && peer.ping.rtt.mean > 0 | |
58 … | + } | |
51 | 59 … | |
52 | -//tried to connect, but failed. | |
53 | -function isInactive (e) { | |
54 | - return e.stateChange && (e.duration && e.duration.mean == 0) | |
55 | -} | |
60 … | + //pub is running scuttlebot < 8 | |
61 … | + //have connected sucessfully | |
62 … | + function isLegacy (peer) { | |
63 … | + return /connect/.test(peer.state) || (peer.duration && peer.duration.mean) > 0 && !isLongterm(peer) | |
64 … | + } | |
56 | 65 … | |
57 | -//havn't tried to connect peer yet. | |
58 | -function isUnattempted (e) { | |
59 | - return !e.stateChange | |
60 | -} | |
66 … | + //tried to connect, but failed. | |
67 … | + function isInactive (peer) { | |
68 … | + return peer.stateChange && (peer.duration && peer.duration.mean == 0) | |
69 … | + } | |
61 | 70 … | |
62 | -function getType (e) { | |
63 | - return ( | |
64 | - isLongterm(e) ? 'modern' | |
65 | - : isLegacy(e) ? 'legacy' | |
66 | - : isInactive(e) ? 'inactive' | |
67 | - : isUnattempted(e) ? 'unattempted' | |
68 | - : 'other' //should never happen | |
69 | - ) | |
71 … | + //havn't tried to connect peer yet. | |
72 … | + function isUnattempted (peer) { | |
73 … | + return !peer.stateChange | |
74 … | + } | |
70 | 75 … | } |
71 | 76 … | |
72 | -function origin (e) { | |
73 | - return e.source === 'local' ? 0 : 1 | |
77 … | +function origin (peer) { | |
78 … | + return peer.source === 'local' ? 0 : 1 | |
74 | 79 … | } |
75 | 80 … | |
76 | -var states = { | |
77 | - connected: 3, | |
78 | - connecting: 2 | |
79 | -} | |
80 | - | |
81 | -var types = { | |
82 | - modern: 4, | |
83 | - legacy: 3, | |
84 | - inactive: 2, | |
85 | - unattempted: 1, | |
86 | - other: 0 | |
87 | -} | |
88 | - | |
89 | 81 … | function round(n) { |
90 | 82 … | return Math.round(n*100)/100 |
91 | 83 … | } |
92 | 84 … | |
@@ -99,78 +91,163 @@ | ||
99 | 91 … | else |
100 | 92 … | return round(s)+'ms' |
101 | 93 … | } |
102 | 94 … | |
95 … | +function peerListSort (a, b) { | |
96 … | + var states = { | |
97 … | + connected: 3, | |
98 … | + connecting: 2 | |
99 … | + } | |
103 | 100 … | |
101 … | + //types of peers | |
102 … | + var types = { | |
103 … | + modern: 4, | |
104 … | + legacy: 3, | |
105 … | + inactive: 2, | |
106 … | + unattempted: 1, | |
107 … | + other: 0 | |
108 … | + } | |
104 | 109 … | |
105 | -exports.create = function (api) { | |
110 … | + return ( | |
111 … | + (states[b.state] || 0) - (states[a.state] || 0) | |
112 … | + || origin(b) - origin(a) | |
113 … | + || types[getType(b)] - types[getType(a)] | |
114 … | + || b.stateChange - a.stateChange | |
115 … | + ) | |
116 … | +} | |
106 | 117 … | |
107 | - return { | |
108 | - menu_items: function () { | |
109 | - return h('a', {href: '#/network'}, '/network') | |
110 | - }, | |
118 … | +function formatDate (time) { | |
119 … | + return new Date(time).toString() | |
120 … | +} | |
111 | 121 … | |
112 | - builtin_tabs: function () { | |
113 | - return ['/network'] | |
114 | - }, | |
122 … | +function humanDate (time) { | |
123 … | + return human(new Date(time)).replace(/minute/, 'min').replace(/second/, 'sec') | |
124 … | +} | |
115 | 125 … | |
116 | - screen_view: function (path) { | |
126 … | +exports.create = function (api) { | |
117 | 127 … | |
118 | - if(path !== '/network') return | |
128 … | + return { | |
129 … | + menu_items: () => h('a', {href: '#/network'}, '/network'), | |
130 … | + builtin_tabs: () => ['/network'], | |
131 … | + screen_view, | |
132 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
133 … | + } | |
119 | 134 … | |
120 | - var ol = h('ul.network') | |
135 … | + function screen_view (path) { | |
136 … | + if (path !== '/network') return | |
121 | 137 … | |
122 | - ;(function poll () { | |
138 … | + var peers = obs_gossip_peers(api) | |
123 | 139 … | |
124 | - //if this tab isn't open, don't update. | |
125 | - //todo: make a better way to do this... | |
126 | - if(!isVisible(ol)) | |
127 | - return setTimeout(poll, 1000) | |
140 … | + return h('div', { style: {'overflow':'auto'}, className: 'column scroller' }, [ | |
141 … | + h('Network', [ | |
142 … | + mutantMap(peers, peer => { | |
143 … | + var { key, ping, source, state, stateChange } = peer | |
128 | 144 … | |
129 | - api.sbot_gossip_peers(function (err, list) { | |
130 | - ol.innerHTML = '' | |
131 | - list.sort(function (a, b) { | |
132 | - return ( | |
133 | - (states[b.state] || 0) - (states[a.state] || 0) | |
134 | - || origin(b) - origin(a) | |
135 | - || types[getType(b)] - types[getType(a)] | |
136 | - || b.stateChange - a.stateChange | |
137 | - ) | |
138 | - }).forEach(function (peer) { | |
139 | - ol.appendChild(h('div', | |
140 | - api.avatar(peer.key, 'thumbnail'), | |
141 | - h('div', | |
142 | - peer.state || 'not connected', | |
143 | - ' ', | |
144 | - getType(peer), | |
145 | - ' ', | |
146 | - //TODO: show nicer details, with labels. etc. | |
147 | - (peer.ping && peer.ping.rtt) ? duration(peer.ping.rtt.mean) : '', | |
148 | - ' ', | |
149 | - (peer.ping && peer.ping.skew) ? duration(peer.ping.skew.mean) : '', | |
150 | - h('label', | |
151 | - {title: new Date(peer.stateChange).toString()}, | |
152 | - peer.stateChange && ('(' + human(new Date(peer.stateChange))) + ')') | |
153 | - ), | |
154 | - 'source:'+peer.source, | |
155 | - h('pre', legacyToMultiServer(peer)), | |
156 | - h('button', 'connect', {onclick: function () { | |
157 | - api.sbot_gossip_connect(peer, function (err) { | |
145 … | + return h('NetworkConnection', [ | |
146 … | + h('section.avatar', [ | |
147 … | + api.avatar_image_link(key()), | |
148 … | + ]), | |
149 … | + h('section.name', [ | |
150 … | + api.avatar_name_link(key()), | |
151 … | + ]), | |
152 … | + h('section.type', [ | |
153 … | + computed(peer, getType), | |
154 … | + ]), | |
155 … | + h('section.source', [ | |
156 … | + h('label', 'source:'), | |
157 … | + h('code', source) | |
158 … | + ]), | |
159 … | + h('section.state', [ | |
160 … | + h('label', 'state:'), | |
161 … | + h('i', { | |
162 … | + className: computed(state, (state) => '-'+state) | |
163 … | + }), | |
164 … | + h('code', when(state, state, 'not connected')) | |
165 … | + ]), | |
166 … | + h('section.actions', [ | |
167 … | + h('button', { | |
168 … | + 'ev-click': () => { | |
169 … | + api.sbot_gossip_connect(peer(), (err) => { | |
158 | 170 … | if(err) console.error(err) |
159 | - else console.log('connected to', peer) | |
171 … | + else console.log('connected to', peer()) | |
160 | 172 … | }) |
161 | - }}) | |
173 … | + }}, | |
174 … | + 'connect' | |
162 | 175 … | ) |
163 | - ) | |
164 | - }) | |
165 | - | |
166 | - setTimeout(poll, 5000) | |
176 … | + ]), | |
177 … | + h('section.time-ago', [ | |
178 … | + h('div', | |
179 … | + { title: computed(stateChange, formatDate) }, | |
180 … | + [ computed(stateChange, humanDate) ] | |
181 … | + ) | |
182 … | + ]), | |
183 … | + h('section.ping', [ | |
184 … | + h('div.rtt', [ | |
185 … | + h('label', 'rtt:'), | |
186 … | + h('code', computed(ping.rtt.mean, duration)) | |
187 … | + ]), | |
188 … | + h('div.skew', [ | |
189 … | + h('label', 'skew:'), | |
190 … | + h('code', computed(ping.skew.mean, duration)) | |
191 … | + ]), | |
192 … | + ]), | |
193 … | + h('section.address', [ | |
194 … | + h('code', computed(peer, legacyToMultiServer)) | |
195 … | + ]) | |
196 … | + ]) | |
167 | 197 … | }) |
198 … | + ]) | |
199 … | + ]) | |
200 … | + } | |
201 … | +} | |
168 | 202 … | |
169 | - })() | |
203 … | +function obs_gossip_peers (api) { | |
204 … | + var timer = null | |
205 … | + var state = Dict({}, { | |
206 … | + onListen: () => { | |
207 … | + timer = setInterval(refresh, 5e3) | |
208 … | + }, | |
209 … | + onUnlisten: () => { | |
210 … | + clearInterval(timer) | |
211 … | + } | |
212 … | + }) | |
170 | 213 … | |
171 | - return h('div.column.scroll-y', ol) | |
172 | - } | |
214 … | + refresh() | |
215 … | + | |
216 … | + return dictToCollection.values(state) | |
217 … | + | |
218 … | + function refresh () { | |
219 … | + api.sbot_gossip_peers((err, peers) => { | |
220 … | + peers.forEach(data => { | |
221 … | + var id = legacyToMultiServer(data) | |
222 … | + var current = state.get(id) | |
223 … | + if (!current) { | |
224 … | + current = Peer() | |
225 … | + current.set(data) | |
226 … | + state.put(id, current) | |
227 … | + } else { | |
228 … | + current.set(data) | |
229 … | + } | |
230 … | + }) | |
231 … | + }) | |
173 | 232 … | } |
174 | 233 … | } |
175 | 234 … | |
235 … | +function Peer () { | |
236 … | + var peer = Struct({ | |
237 … | + key: Value(), | |
238 … | + ping: Struct({ | |
239 … | + rtt: Struct({ | |
240 … | + mean: Value() | |
241 … | + }), | |
242 … | + skew: Struct({ | |
243 … | + mean: Value() | |
244 … | + }) | |
245 … | + }), | |
246 … | + source: Value(), | |
247 … | + state: Value(), | |
248 … | + stateChange: Value() | |
249 … | + }) | |
176 | 250 … | |
251 … | + return peer | |
252 … | +} | |
253 … | + |
modules_extra/raw.js | ||
---|---|---|
@@ -28,10 +28,11 @@ | ||
28 | 28 … | return h('input', { |
29 | 29 … | type: 'checkbox', |
30 | 30 … | title: 'View Data', |
31 | 31 … | onclick: function () { |
32 | - var msgEl = this.parentNode.parentNode.parentNode | |
33 | - var msgContentEl = msgEl.querySelector('.message_content') | |
32 … | + // HACK (mw) yo we need a better way to replace the content | |
33 … | + var msgEl = this.parentNode.parentNode | |
34 … | + var msgContentEl = msgEl.querySelector('.\\.content') | |
34 | 35 … | if (this.checked) { |
35 | 36 … | // move away the content |
36 | 37 … | while (el = msgContentEl.firstChild) |
37 | 38 … | tmp.appendChild(el) |
modules_extra/key.js | ||
---|---|---|
@@ -1,0 +1,62 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | +var u = require('../util') | |
3 … | +var pull = require('pull-stream') | |
4 … | +var Scroller = require('pull-scroll') | |
5 … | + | |
6 … | +//var plugs = require('../plugs') | |
7 … | +// var message_render = plugs.first(exports.message_render = []) | |
8 … | +// var message_compose = plugs.first(exports.message_compose = []) | |
9 … | +// var sbot_log = plugs.first(exports.sbot_log = []) | |
10 … | + | |
11 … | + | |
12 … | +exports.gives = { | |
13 … | + menu_items: true, screen_view: true | |
14 … | +} | |
15 … | + | |
16 … | +exports.create = function (api) { | |
17 … | + return { | |
18 … | + menu_items: function () { | |
19 … | + return h('a', {href: '#/key'}, '/key') | |
20 … | + }, | |
21 … | + screen_view: function (path, sbot) { | |
22 … | + if(path === '/key') { | |
23 … | + if(process.title === 'browser') { | |
24 … | + var importKey = h('textarea', {placeholder: 'import an existing public/private key', name: 'textarea'}) | |
25 … | + var importRemote = h('textarea', {placeholder: 'import an existing remote', name: 'textarea'}) | |
26 … | + var content = h('div.column.scroller__content') | |
27 … | + var div = h('div.column.scroller', | |
28 … | + {style: {'overflow':'auto'}}, | |
29 … | + h('div.scroller__wrapper', | |
30 … | + h('div.column.scroller__content', | |
31 … | + h('div.message', | |
32 … | + h('p', {innerHTML: 'Your secret key is: <pre><code>' + localStorage['browser/.ssb/secret'] + '</code></pre>'}), | |
33 … | + h('form', | |
34 … | + importKey, | |
35 … | + h('button', {onclick: function (e){ | |
36 … | + localStorage['browser/.ssb/secret'] = importKey.value.replace(/\s+/g, ' ') | |
37 … | + alert('Your public/private key has been updated') | |
38 … | + e.preventDefault() | |
39 … | + }}, 'Import'), | |
40 … | + h('p', {innerHTML: 'Your ws remote is: <pre>' + localStorage.remote + '</pre>'}), | |
41 … | + h('form', | |
42 … | + importRemote, | |
43 … | + h('button', {onclick: function (e){ | |
44 … | + localStorage.remote = importRemote.value | |
45 … | + alert('Your websocket remote has been updated') | |
46 … | + e.preventDefault() | |
47 … | + }}, 'Import') | |
48 … | + ) | |
49 … | + ) | |
50 … | + ) | |
51 … | + ) | |
52 … | + ) | |
53 … | + ) | |
54 … | + return div | |
55 … | + } else { | |
56 … | + return h('p', 'Your key is saved at .ssb/secret') | |
57 … | + } | |
58 … | + } | |
59 … | + } | |
60 … | + } | |
61 … | +} | |
62 … | + |
modules_extra/network.mcss | ||
---|---|---|
@@ -1,0 +1,117 @@ | ||
1 … | +Network { | |
2 … | + div { | |
3 … | + margin: .8rem | |
4 … | + border-bottom: 1px solid gainsboro | |
5 … | + } | |
6 … | + | |
7 … | +} | |
8 … | + | |
9 … | +NetworkConnection { | |
10 … | + padding-left: 6rem | |
11 … | + padding-bottom: .8rem | |
12 … | + position: relative | |
13 … | + | |
14 … | + display: flex | |
15 … | + flex-direction: row | |
16 … | + flex-wrap: wrap | |
17 … | + align-items: center | |
18 … | + | |
19 … | + section.avatar { | |
20 … | + position: absolute | |
21 … | + left: 0 | |
22 … | + top: 0 | |
23 … | + | |
24 … | + a img { | |
25 … | + height: 3rem | |
26 … | + width: 3rem | |
27 … | + } | |
28 … | + } | |
29 … | + | |
30 … | + section.time-ago { | |
31 … | + position: absolute | |
32 … | + left: 0 | |
33 … | + top: 3.5rem | |
34 … | + | |
35 … | + font-size: .8rem | |
36 … | + $textSubtle | |
37 … | + } | |
38 … | + | |
39 … | + section.name { | |
40 … | + flex-basis: 100% | |
41 … | + | |
42 … | + margin-right: 1rem | |
43 … | + } | |
44 … | + | |
45 … | + section.type { | |
46 … | + flex-basis: 100% | |
47 … | + | |
48 … | + font-size: .8rem | |
49 … | + $textSubtle | |
50 … | + } | |
51 … | + | |
52 … | + section.source { | |
53 … | + flex-basis: 100% | |
54 … | + label { margin-right: .5rem } | |
55 … | + } | |
56 … | + | |
57 … | + section.state { | |
58 … | + display: flex | |
59 … | + flex-direction: row | |
60 … | + align-items: center | |
61 … | + | |
62 … | + margin-right: 2rem | |
63 … | + | |
64 … | + label { margin-right: .5rem } | |
65 … | + i { | |
66 … | + width: 1rem | |
67 … | + height: 1rem | |
68 … | + border-radius: .5rem | |
69 … | + | |
70 … | + margin: 0 .2rem 0 .5rem | |
71 … | + | |
72 … | + background: #ccc | |
73 … | + | |
74 … | + -connecting, -disconnecting { | |
75 … | + background: #95cc95 | |
76 … | + } | |
77 … | + | |
78 … | + -connected { | |
79 … | + background: #06e206 | |
80 … | + } | |
81 … | + } | |
82 … | + } | |
83 … | + | |
84 … | + section.actions { | |
85 … | + button { | |
86 … | + padding: .2rem | |
87 … | + font-size: .7rem | |
88 … | + } | |
89 … | + } | |
90 … | + | |
91 … | + | |
92 … | + section.ping { | |
93 … | + flex-basis: 100% | |
94 … | + display: flex | |
95 … | + | |
96 … | + font-size: .8rem | |
97 … | + $textSubtle | |
98 … | + | |
99 … | + div.rtt { | |
100 … | + flex-basis: 8rem | |
101 … | + label { margin-right: .5rem } | |
102 … | + } | |
103 … | + | |
104 … | + div.skew { | |
105 … | + flex-basis: 8rem | |
106 … | + label { margin-right: .5rem } | |
107 … | + } | |
108 … | + } | |
109 … | + | |
110 … | + section.address{ | |
111 … | + flex-basis: 100% | |
112 … | + | |
113 … | + font-size: .8rem | |
114 … | + $textSubtle | |
115 … | + } | |
116 … | +} | |
117 … | + |
package.json | ||
---|---|---|
@@ -1,29 +1,34 @@ | ||
1 | 1 … | { |
2 | 2 … | "name": "patchbay", |
3 | 3 … | "description": "a pluggable patchwork", |
4 | - "version": "6.0.1", | |
4 … | + "version": "6.1.10", | |
5 | 5 … | "homepage": "https://github.com/dominictarr/patchbay", |
6 | 6 … | "repository": { |
7 | 7 … | "type": "git", |
8 | 8 … | "url": "git://github.com/dominictarr/patchbay.git" |
9 | 9 … | }, |
10 | 10 … | "dependencies": { |
11 … | + "@mmckegg/mutant": "^3.12.0", | |
12 … | + "brfs": "^1.4.3", | |
11 | 13 … | "cont": "^1.0.3", |
12 | 14 … | "dataurl-": "^0.1.0", |
13 | 15 … | "depject": "^3.0.0", |
16 … | + "es2040": "^1.2.4", | |
14 | 17 … | "hjson": "^2.0.3", |
15 | 18 … | "human-time": "0.0.1", |
16 | 19 … | "hypercombo": "0.1.0", |
17 | 20 … | "hypercrop": "^1.0.1", |
18 | 21 … | "hyperfile": "^1.1.0", |
19 | - "hyperlightbox": "^0.1.3", | |
22 … | + "hyperlightbox": "1.0.0", | |
20 | 23 … | "hyperprogress": "0.1.0", |
21 | 24 … | "hyperscript": "^1.4.7", |
22 | 25 … | "hypertabs": "^3.0.0", |
23 | - "is-visible": "^2.0.4", | |
26 … | + "insert-css": "^2.0.0", | |
27 … | + "is-visible": "^2.1.1", | |
24 | 28 … | "kvgraph": "^0.1.0", |
25 | 29 … | "map-filter-reduce": "^3.0.1", |
30 … | + "micro-css": "^0.6.2", | |
26 | 31 … | "mime-types": "^2.1.11", |
27 | 32 … | "moment": "^2.13.0", |
28 | 33 … | "open-external": "^0.1.1", |
29 | 34 … | "peaks.js": "^0.4.7", |
@@ -31,11 +36,12 @@ | ||
31 | 36 … | "pull-many": "^1.0.7", |
32 | 37 … | "pull-next": "^0.0.1", |
33 | 38 … | "pull-paramap": "^1.1.6", |
34 | 39 … | "pull-reconnect": "^0.0.3", |
35 | - "pull-scroll": "^0.2.0", | |
40 … | + "pull-scroll": "^1.0.1", | |
36 | 41 … | "pull-stream": "^3.4.5", |
37 | 42 … | "scuttlebot": "^8.7.2", |
43 … | + "setimmediate": "^1.0.5", | |
38 | 44 … | "simple-mime": "^0.1.0", |
39 | 45 … | "split-buffer": "^1.0.0", |
40 | 46 … | "ssb-avatar": "^0.2.0", |
41 | 47 … | "ssb-blobs": "^0.1.7", |
@@ -49,22 +55,29 @@ | ||
49 | 55 … | "ssb-mentions": "^0.1.0", |
50 | 56 … | "ssb-query": "^0.1.1", |
51 | 57 … | "ssb-ref": "^2.6.2", |
52 | 58 … | "ssb-sort": "^1.0.0", |
53 | - "ssb-ws": "^0.6.2", | |
59 … | + "ssb-ws": "^1.0.1", | |
54 | 60 … | "suggest-box": "^2.2.1", |
55 | 61 … | "text-node-searcher": "^1.1.0", |
56 | - "visualize-buffer": "0.0.0" | |
62 … | + "visualize-buffer": "0.0.1" | |
57 | 63 … | }, |
58 | 64 … | "devDependencies": { |
59 | 65 … | "browselectrify": "^1.0.1", |
60 | 66 … | "electro": "^2.0.3", |
61 | - "electron": "^1.4.10" | |
67 … | + "electron": "^1.4.10", | |
68 … | + "indexhtmlify": "^1.3.1" | |
62 | 69 … | }, |
70 … | + "browserify": { | |
71 … | + "transform": [ | |
72 … | + "brfs", | |
73 … | + "es2040" | |
74 … | + ] | |
75 … | + }, | |
63 | 76 … | "scripts": { |
64 | - "lite": "node scripts/style.js && mkdir -p build && browserify index.js | indexhtmlify > build/index.html", | |
77 … | + "lite": "mkdir -p build && browserify index.js | indexhtmlify --title patchbay > build/index.html", | |
65 | 78 … | "start": "electro index.js", |
66 | - "bundle": "node scripts/style.js && mkdir -p build && browselectrify index.js > build/bundle.js", | |
79 … | + "bundle": "mkdir -p build && browselectrify index.js > build/bundle.js", | |
67 | 80 … | "rebuild": "npm rebuild --runtime=electron --target=$(electron -v) --abi=$(electron --abi) --disturl=https://atom.io/download/atom-shell", |
68 | 81 … | "graph": "node index.js | dot -Tsvg > graph.svg", |
69 | 82 … | "test": "set -e; for t in test/*.js; do node $t; done" |
70 | 83 … | }, |
scripts/create-index.js | ||
---|---|---|
@@ -1,8 +1,0 @@ | ||
1 | -var fs = require('fs') | |
2 | -var path = require('path') | |
3 | - | |
4 | -fs.writeFileSync( | |
5 | - path.join(__dirname, '..', 'style.css.json'), | |
6 | - JSON.stringify(fs.readFileSync(path.join(__dirname, '..', 'style.css'), 'utf8')) | |
7 | -) | |
8 | - |
scripts/style.js | ||
---|---|---|
@@ -1,8 +1,0 @@ | ||
1 | -var fs = require('fs') | |
2 | -var path = require('path') | |
3 | - | |
4 | -fs.writeFileSync( | |
5 | - path.join(__dirname, '..', 'style.css.json'), | |
6 | - JSON.stringify(fs.readFileSync(path.join(__dirname, '..', 'style.css'), 'utf8')) | |
7 | -) | |
8 | - |
style.css | ||
---|---|---|
@@ -1,6 +1,7 @@ | ||
1 | 1 … | body { |
2 | 2 … | font-family: sans-serif; |
3 … | + color: #222; | |
3 | 4 … | } |
4 | 5 … | |
5 | 6 … | h1, h2, h3, h4, h5, h6, p, ul, ol { |
6 | 7 … | margin-top: .35em; |
@@ -101,9 +102,9 @@ | ||
101 | 102 … | border-radius: .2em; |
102 | 103 … | font-family: sans-serif; |
103 | 104 … | } |
104 | 105 … | |
105 | -input:focus, .compose:focus, .message:focus, button:focus { | |
106 … | +input:focus, .compose:focus, button:focus { | |
106 | 107 … | outline: none; |
107 | 108 … | border-color: #0088cc; |
108 | 109 … | box-shadow: 0 0 4px #0088cc; |
109 | 110 … | } |
@@ -162,106 +163,15 @@ | ||
162 | 163 … | margin-left: auto; |
163 | 164 … | margin-right: auto; |
164 | 165 … | } |
165 | 166 … | |
166 | -/* compose */ | |
167 | - | |
168 | -.compose { | |
169 | - width: 100%; | |
170 | - margin-top: .5em; | |
171 | - margin-bottom: .5em; | |
172 | - border: 1px solid #f5f5f5; | |
173 | -} | |
174 | - | |
175 | -.compose button { | |
176 | - float: right; | |
177 | -} | |
178 | - | |
179 | 167 … | /* messages */ |
168 … | +/* is .title used any more? */ | |
180 | 169 … | |
181 | -.message { | |
182 | - position: relative; | |
183 | - flex-basis: 0; | |
184 | - margin-top: .25em; | |
185 | - margin-bottom: .25em; | |
186 | - padding: .2em; | |
187 | - border-top: 1px solid #f5f5f5; | |
188 | - // border-bottom: 1px solid #f5f5f5; | |
189 | - background: white; | |
190 | -} | |
191 | - | |
192 | -.message:hover { | |
193 | - background: #f9f9f9; | |
194 | -} | |
195 | - | |
196 | -.message--mini { | |
197 | - font-size: 1em; | |
198 | - margin: 0; | |
199 | - padding: .2em; | |
200 | - border: none; | |
201 | - background: inherit; | |
202 | -} | |
203 | - | |
204 | -.message_content div > span { | |
205 | - font-size: 0.9em; | |
206 | - margin-bottom: 0.7em; | |
207 | - display: block; | |
208 | - color: #888; | |
209 | -} | |
210 | - | |
211 | -.message_content--mini div > span { | |
212 | - display: inline-block; | |
213 | -} | |
214 | - | |
215 | -.message_content div > span a { | |
216 | - color: #005d8c; | |
217 | -} | |
218 | - | |
219 | -.message_meta input { | |
220 | - font-size: .8em; | |
221 | -} | |
222 | - | |
223 | -.message_meta { | |
224 | - margin-left: auto; | |
225 | -} | |
226 | - | |
227 | -.message_meta > * { | |
228 | - margin-left: .5ex; | |
229 | -} | |
230 | - | |
231 | -.message_actions { | |
232 | - position: absolute; | |
233 | - bottom: 0; right: 0; | |
234 | - margin-right: .5ex; | |
235 | - margin-bottom: .5ex; | |
236 | -} | |
237 | - | |
238 | 170 … | .title { |
239 | 171 … | padding: .5ex; |
240 | 172 … | } |
241 | 173 … | |
242 | -.message img { | |
243 | - max-width: 100%; | |
244 | -} | |
245 | - | |
246 | -.message > .title > .avatar { | |
247 | - margin-left: 0; | |
248 | -} | |
249 | - | |
250 | -.message_content { | |
251 | - padding: .5ex; | |
252 | -} | |
253 | - | |
254 | -.actions > * { | |
255 | - padding-left: 5px; | |
256 | - margin-left: 1px; | |
257 | -} | |
258 | - | |
259 | -.actions > :not(:last-child) { | |
260 | - border-right: 2px solid #eee; | |
261 | - padding-right: 5px; | |
262 | -} | |
263 | - | |
264 | 174 … | .emoji { |
265 | 175 … | height: 1em; |
266 | 176 … | width: 1em; |
267 | 177 … | vertical-align: top; |
@@ -315,11 +225,16 @@ | ||
315 | 225 … | .avatar--thumbnail { |
316 | 226 … | width: 2.5em; |
317 | 227 … | height: 2.5em; |
318 | 228 … | float: left; |
319 | - margin-right: .5ex; | |
229 … | + margin: 0 .25ex; | |
320 | 230 … | } |
321 | 231 … | |
232 … | +.\.meta .avatar--thumbnail { | |
233 … | + width: 1.9em; | |
234 … | + height: 1.9em; | |
235 … | +} | |
236 … | + | |
322 | 237 … | .avatar--fullsize { |
323 | 238 … | width: 50%; |
324 | 239 … | } |
325 | 240 … | |
@@ -338,19 +253,22 @@ | ||
338 | 253 … | |
339 | 254 … | /* lightbox - used in message-confirm */ |
340 | 255 … | |
341 | 256 … | .lightbox { |
257 … | + position: fixed; | |
258 … | + left: 0px; | |
259 … | + right: 0px; | |
260 … | + top: 50px; | |
342 | 261 … | overflow: auto; |
343 | - padding: 1.5em; | |
344 | - margin-top: 3em; | |
345 | - margin-bottom: 3em; | |
346 | - width: 600px; | |
262 … | + width: 650px; | |
263 … | + padding: 25px; | |
264 … | + margin: auto; | |
265 … | + | |
266 … | + z-index: 2; | |
267 … | + | |
347 | 268 … | background: #f5f5f5; |
348 | - margin-left: auto; | |
349 | - margin-right: auto; | |
350 | 269 … | border: 1px solid #eee; |
351 | 270 … | border-radius: .2em; |
352 | - z-index: 2; | |
353 | 271 … | } |
354 | 272 … | |
355 | 273 … | /* searchprompt */ |
356 | 274 … | |
@@ -359,9 +277,10 @@ | ||
359 | 277 … | width: 85%; |
360 | 278 … | height: 2em; |
361 | 279 … | margin-top: .3em; |
362 | 280 … | border-radius: 1em; |
363 | - padding-left: .2em; | |
281 … | + padding-left: .7em; | |
282 … | + padding-right: 0.5em; | |
364 | 283 … | margin-left: 1em; |
365 | 284 … | margin-right: 1em; |
366 | 285 … | } |
367 | 286 … |
Built with git-ssb-web