git ssb

7+

dinoworm 🐛 / patchcore



Commit 84289a1e105783bad299705f084b4a07b225fa38

Merge remote-tracking branch 'origin/master' into patch-router

mix irving committed on 7/16/2017, 6:36:37 AM
Parent: 4d1aa8528ed9342d5d663af75cb08beb82f0fbcc
Parent: af2008b48a9d597cb4374d5e2fb4523a29106aa1

Files changed

about/obs.jschanged
feed/obs/recent.jschanged
feed/obs/thread.jschanged
feed/pull/rollup.jschanged
index.jschanged
lib/timeAgo.jschanged
message/async/name.jschanged
message/html/action/like.jschanged
message/obs/backlinks.jschanged
message/obs/likes.jschanged
package.jsonchanged
sbot.jschanged
backlinks/obs.jsadded
about/obs.jsView
@@ -27,9 +27,10 @@
2727 ]
2828 })
2929
3030 exports.create = function (api) {
31- var sync = Value(false)
31 + var syncValue = Value(false)
32 + var sync = computed(syncValue, x => x)
3233 var cache = null
3334
3435 return nest({
3536 'about.obs': {
@@ -51,30 +52,34 @@
5152 groupedValues
5253 }
5354 })
5455
55-
5656 function valueFrom (id, key, author) {
5757 if (!ref.isLink(id)) throw new Error('About requires an ssb ref!')
58- return computed([get(id), key, author], getValueFrom)
58 + return withSync(computed([get(id), key, author], getValueFrom))
5959 }
6060
6161 function latestValue (id, key) {
6262 if (!ref.isLink(id)) throw new Error('About requires an ssb ref!')
63- return computed([get(id), key], getLatestValue)
63 + return withSync(computed([get(id), key], getLatestValue))
6464 }
6565
6666 function socialValue (id, key, defaultValue) {
6767 if (!ref.isLink(id)) throw new Error('About requires an ssb ref!')
6868 var yourId = api.keys.sync.id()
69- return computed([get(id), key, id, yourId, defaultValue], getSocialValue)
69 + return withSync(computed([get(id), key, id, yourId, defaultValue], getSocialValue))
7070 }
7171
7272 function groupedValues (id, key) {
7373 if (!ref.isLink(id)) throw new Error('About requires an ssb ref!')
74- return computed([get(id), key], getGroupedValues)
74 + return withSync(computed([get(id), key], getGroupedValues))
7575 }
7676
77 + function withSync (obs) {
78 + obs.sync = sync
79 + return obs
80 + }
81 +
7782 function get (id) {
7883 if (!ref.isLink(id)) throw new Error('About requires an ssb ref!')
7984 load()
8085 if (!cache[id]) {
@@ -108,10 +113,10 @@
108113 state.set(lastState)
109114 }
110115 }
111116
112- if (!sync()) {
113- sync.set(true)
117 + if (!syncValue()) {
118 + syncValue.set(true)
114119 }
115120 })
116121 )
117122 }
feed/obs/recent.jsView
@@ -15,9 +15,9 @@
1515 exports.create = function (api) {
1616 return nest('feed.obs.recent', function (limit) {
1717 var stream = pull(
1818 pullCat([
19- api.sbot.pull.log({reverse: true, limit: limit || 500}),
19 + api.sbot.pull.log({reverse: true, limit: limit || 50}),
2020 api.sbot.pull.log({old: false})
2121 ])
2222 )
2323
feed/obs/thread.jsView
@@ -1,16 +1,14 @@
11 var nest = require('depnest')
2-var pull = require('pull-stream')
3-var pullCat = require('pull-cat')
42 var sort = require('ssb-sort')
53 var ref = require('ssb-ref')
6-var { map, computed } = require('mutant')
4 +var { Array: MutantArray, Value, map, concat, computed } = require('mutant')
75
86 exports.needs = nest({
9- 'sbot.pull.links': 'first',
7 + 'backlinks.obs.for': 'first',
108 'sbot.async.get': 'first',
11- 'lib.obs.pullLookup': 'first',
12- 'message.sync.unbox': 'first'
9 + 'message.sync.unbox': 'first',
10 + 'message.sync.root': 'first'
1311 })
1412
1513 exports.gives = nest('feed.obs.thread')
1614
@@ -18,38 +16,42 @@
1816 return nest('feed.obs.thread', thread)
1917
2018 function thread (rootId, { branch } = {}) {
2119 if (!ref.isLink(rootId)) throw new Error('an id must be specified')
20 + var sync = Value(false)
2221
23- var rootMessageStream = pull(
24- pull.values([rootId]),
25- pull.asyncMap((key, cb) => {
26- return api.sbot.async.get(key, (err, value) => cb(err, {key, value}))
27- })
28- )
29-
30- var messageLookup = api.lib.obs.pullLookup(pull(
31- pullCat([
32- rootMessageStream,
33- api.sbot.pull.links({ rel: branch ? 'branch' : 'root', dest: rootId, keys: true, values: true, live: true })
34- ]),
35- unboxIfNeeded()
36- ), 'key')
37-
38- var orderedIds = computed(messageLookup, (lookup) => {
39- var msgs = Object.keys(lookup).map(k => lookup[k])
40- return sort(msgs).map(getKey)
22 + var prepend = MutantArray()
23 + api.sbot.async.get(rootId, (err, value) => {
24 + sync.set(true)
25 + if (!err) {
26 + prepend.push(
27 + Value(unboxIfNeeded({key: rootId, value}))
28 + )
29 + }
4130 })
4231
43- var messages = map(orderedIds, (id) => {
44- return messageLookup.get(id)
32 + var backlinks = api.backlinks.obs.for(rootId)
33 + var replies = map(computed(backlinks, (msgs) => {
34 + return msgs.filter(msg => {
35 + return msg.value.content.type !== 'vote' && (
36 + api.message.sync.root(msg) === rootId ||
37 + matchAny(msg.value.content.branch, rootId)
38 + )
39 + })
40 + }), x => Value(x), {
41 + // avoid refresh of entire list when items added
42 + comparer: (a, b) => a === b
4543 })
4644
45 + var messages = concat([prepend, replies])
46 +
4747 var result = {
4848 messages,
4949 lastId: computed(messages, (messages) => {
5050 var branches = sort.heads(messages)
51- if(branches.length <= 1) branches = branches[0]
51 + if (branches.length <= 1) {
52 + branches = branches[0]
53 + }
5254 return branches
5355 }),
5456 rootId: computed(messages, (messages) => {
5557 if (branch && messages.length) {
@@ -80,28 +82,21 @@
8082 return msgs[0].value.content.recps
8183 })
8284 }
8385
84- result.sync = messageLookup.sync
85-
86 + result.sync = computed([backlinks.sync, sync], (a, b) => a && b, {idle: true})
8687 return result
8788 }
8889
89- function unboxIfNeeded () {
90- return pull.map(function (msg) {
91- if (msg.sync || (msg.value && typeof msg.value.content === 'object')) {
92- return msg
93- } else {
94- return api.message.sync.unbox(msg)
95- }
96- })
90 + function unboxIfNeeded (msg) {
91 + if (msg.value && typeof msg.value.content === 'string') {
92 + return api.message.sync.unbox(msg) || msg
93 + } else {
94 + return msg
95 + }
9796 }
9897 }
9998
100-function getKey (msg) {
101- return msg.key
102-}
103-
10499 function PreviousKey (collection, item) {
105100 return computed(collection, (c) => {
106101 var index = collection.indexOf(item)
107102 if (~index) {
@@ -111,4 +106,12 @@
111106 }
112107 }
113108 })
114109 }
110 +
111 +function matchAny (valueOrArray, compare) {
112 + if (valueOrArray === compare) {
113 + return true
114 + } else if (Array.isArray(valueOrArray)) {
115 + return valueOrArray.includes(compare)
116 + }
117 +}
feed/pull/rollup.jsView
@@ -4,53 +4,98 @@
44
55 var pull = require('pull-stream')
66 var nest = require('depnest')
77 var extend = require('xtend')
8 +var HLRU = require('hashlru')
9 +var resolve = require('mutant/resolve')
10 +var onceTrue = require('mutant/once-true')
811
912 exports.needs = nest({
10- 'sbot.pull.backlinks': 'first',
13 + 'backlinks.obs.for': 'first',
1114 'sbot.async.get': 'first',
1215 'message.sync.root': 'first',
1316 'message.sync.unbox': 'first'
1417 })
1518
1619 exports.gives = nest('feed.pull.rollup', true)
1720
1821 exports.create = function (api) {
22 + // cache mostly just to avoid reading the same roots over and over again
23 + // not really big enough for multiple refresh cycles
24 + var cache = HLRU(100)
25 +
1926 return nest('feed.pull.rollup', function (rootFilter) {
27 + var seen = new Set()
2028 return pull(
21- pull.map(msg => api.message.sync.root(msg) || msg.key),
22- pull.unique(),
23- Lookup(),
29 + pull.map(msg => {
30 + if (msg.value) {
31 + var root = api.message.sync.root(msg)
32 + if (!root) {
33 + // already a root, pass thru!
34 + return msg
35 + } else {
36 + return root
37 + }
38 + }
39 + }),
40 +
41 + // UNIQUE
42 + pull.filter(idOrMsg => {
43 + if (idOrMsg) {
44 + if (idOrMsg.key) idOrMsg = idOrMsg.key
45 + if (typeof idOrMsg === 'string') {
46 + var key = idOrMsg
47 + if (!seen.has(key)) {
48 + seen.add(key)
49 + return true
50 + }
51 + }
52 + }
53 + }),
54 +
55 + // LOOKUP (if needed)
56 + pull.asyncMap((keyOrMsg, cb) => {
57 + if (keyOrMsg.value) {
58 + cb(null, keyOrMsg)
59 + } else {
60 + var key = keyOrMsg
61 + if (cache.has(key)) {
62 + cb(null, cache.get(key))
63 + } else {
64 + api.sbot.async.get(key, (_, value) => {
65 + var msg = {key, value}
66 + if (msg.value) {
67 + cache.set(key, msg)
68 + }
69 + cb(null, msg)
70 + })
71 + }
72 + }
73 + }),
74 +
75 + // UNBOX (if needed)
76 + pull.map(msg => {
77 + if (msg.value && typeof msg.value.content === 'string') {
78 + var unboxed = api.message.sync.unbox(msg)
79 + if (unboxed) return unboxed
80 + }
81 + return msg
82 + }),
83 +
84 + // FILTER
2485 pull.filter(msg => msg && msg.value && !api.message.sync.root(msg)),
2586 pull.filter(rootFilter || (() => true)),
26- AddReplies()
27- )
28- })
2987
30- // scoped
31- function Lookup () {
32- return pull.asyncMap((key, cb) => {
33- api.sbot.async.get(key, (_, value) => {
34- if (value && typeof value.content === 'string') {
35- value = api.message.sync.unbox(value)
36- }
37- cb(null, {key, value})
38- })
39- })
40- }
41-
42- function AddReplies () {
43- return pull.asyncMap((rootMessage, cb) => {
44- pull(
45- api.sbot.pull.backlinks({
46- query: [{$filter: { dest: rootMessage.key }}]
47- }),
48- pull.filter(msg => (api.message.sync.root(msg) || rootMessage.key) === rootMessage.key),
49- pull.collect((err, replies) => {
50- if (err) return cb(err)
88 + // ADD REPLIES
89 + pull.asyncMap((rootMessage, cb) => {
90 + // use global backlinks cache
91 + var backlinks = api.backlinks.obs.for(rootMessage.key)
92 + onceTrue(backlinks.sync, () => {
93 + var replies = resolve(backlinks).filter((msg) => {
94 + return api.message.sync.root(msg) === rootMessage.key
95 + })
5196 cb(null, extend(rootMessage, { replies }))
5297 })
53- )
54- })
55- }
98 + })
99 + )
100 + })
56101 }
index.jsView
@@ -2,7 +2,7 @@
22
33 module.exports = {
44 patchcore: bulk(__dirname, [
55 './+(config|emoji|invite|keys|sbot).js',
6- './+(about|blob|channel|contact|feed|lib|message|router)/**/*.js'
6 + './+(about|backlinks|blob|channel|contact|feed|lib|message|router)/**/*.js'
77 ])
88 }
lib/timeAgo.jsView
@@ -12,14 +12,16 @@
1212 var timer
1313 var value = Value(Time(timestamp))
1414 return computed([value], (a) => a, {
1515 onListen: () => {
16- timer = setInterval(refresh, 5e3)
16 + timer = setInterval(refresh, 30e3)
1717 refresh()
1818 },
1919 onUnlisten: () => {
2020 clearInterval(timer)
2121 }
22 + }, {
23 + idle: true
2224 })
2325
2426 function refresh () {
2527 value.set(Time(timestamp))
message/async/name.jsView
@@ -1,11 +1,13 @@
11 const nest = require('depnest')
2-const getAvatar = require('ssb-avatar')
32 const ref = require('ssb-ref')
3 +const {resolve, onceTrue} = require('mutant')
44
55 exports.needs = nest({
66 'sbot.async.get': 'first',
77 'sbot.pull.links': 'first',
8 + 'message.sync.unbox': 'first',
9 + 'about.obs.socialValue': 'first',
810 'keys.sync.id': 'first'
911 })
1012 exports.gives = nest('message.async.name')
1113
@@ -15,8 +17,12 @@
1517 return nest('message.async.name', function (id, cb) {
1618 if (!ref.isLink(id)) throw new Error('an id must be specified')
1719 var fallbackName = id.substring(0, 10) + '...'
1820 api.sbot.async.get(id, function (err, value) {
21 + if (value && typeof value.content === 'string') {
22 + value = api.message.sync.unbox(value)
23 + }
24 +
1925 if (err && err.name === 'NotFoundError') {
2026 return cb(null, fallbackName + '...(missing)')
2127 } else if (value.content.type === 'post' && typeof value.content.text === 'string') {
2228 if (value.content.text.trim()) {
@@ -24,21 +30,21 @@
2430 }
2531 } else if (typeof value.content.text === 'string') {
2632 return cb(null, value.content.type + ': ' + titleFromMarkdown(value.content.text, 30))
2733 } else {
28- getAboutName(id, cb)
34 + return getAboutName(id, cb)
2935 }
3036
3137 return cb(null, fallbackName)
3238 })
3339 })
3440
3541 function getAboutName (id, cb) {
36- getAvatar({
37- links: api.sbot.pull.links,
38- get: api.sbot.async.get
39- }, api.keys.sync.id(), id, function (_, avatar) {
40- cb(null, avatar && avatar.name || id.substring(0, 10) + '...')
42 + var name = api.about.obs.socialValue(id, 'name')
43 + var title = api.about.obs.socialValue(id, 'title')
44 +
45 + onceTrue(name.sync, () => {
46 + cb(null, resolve(name) || resolve(title) || id.substring(0, 10) + '...')
4147 })
4248 }
4349 }
4450
message/html/action/like.jsView
@@ -25,24 +25,24 @@
2525 )
2626 })
2727
2828 function publishLike (msg, status = true) {
29- var dig = status ? {
29 + var like = status ? {
3030 type: 'vote',
3131 channel: msg.value.content.channel,
32- vote: { link: msg.key, value: 1, expression: 'Dig' }
32 + vote: { link: msg.key, value: 1, expression: 'Like' }
3333 } : {
3434 type: 'vote',
3535 channel: msg.value.content.channel,
36- vote: { link: msg.key, value: 0, expression: 'Undig' }
36 + vote: { link: msg.key, value: 0, expression: 'Unlike' }
3737 }
3838 if (msg.value.content.recps) {
39- dig.recps = msg.value.content.recps.map(function (e) {
39 + like.recps = msg.value.content.recps.map(function (e) {
4040 return e && typeof e !== 'string' ? e.link : e
4141 })
42- dig.private = true
42 + like.private = true
4343 }
44- api.sbot.async.publish(dig)
44 + api.sbot.async.publish(like)
4545 }
4646 }
4747
4848 function doesLike (likes, userId) {
message/obs/backlinks.jsView
@@ -1,40 +1,38 @@
11 var nest = require('depnest')
2-var MutantPullReduce = require('mutant-pull-reduce')
2 +var computed = require('mutant/computed')
33
44 exports.needs = nest({
5- 'sbot.pull.backlinks': 'first'
5 + 'backlinks.obs.for': 'first'
66 })
77
88 exports.gives = nest('message.obs.backlinks', true)
99
1010 exports.create = function (api) {
1111 return nest({
12 + // DEPRECATED: should use backlinks.obs.for
1213 'message.obs.backlinks': (id) => backlinks(id)
1314 })
1415
1516 function backlinks (id) {
16- return MutantPullReduce(api.sbot.pull.backlinks({
17- query: [
18- {$filter: {
19- dest: id
20- }},
21- {$map: {
22- dest: 'dest',
23- id: 'key',
24- timestamp: 'timestamp',
25- type: ['value', 'content', 'type'],
26- root: ['value', 'content', 'root'],
27- branch: ['value', 'content', 'branch'],
28- author: ['value', 'author']
29- }}
30- ]
31- }), (result, msg) => {
32- if (msg.type !== 'vote' && msg.type !== 'about') {
33- result.push(msg)
34- }
35- return result
17 + return computed([api.backlinks.obs.for(id)], (msgs) => {
18 + return msgs.map(map).filter((backlink) => {
19 + return backlink.type !== 'vote' && backlink.type !== 'about'
20 + })
3621 }, {
37- startValue: []
22 + // objects coming down this stream will be immutable
23 + comparer: (a, b) => a === b
3824 })
3925 }
26 +
27 + function map (msg) {
28 + return {
29 + dest: msg.dest,
30 + id: msg.key,
31 + timestamp: msg.timestamp,
32 + type: msg.value.content.type,
33 + root: msg.value.content.root,
34 + branch: msg.value.content.branch,
35 + author: msg.value.author
36 + }
37 + }
4038 }
message/obs/likes.jsView
@@ -1,14 +1,15 @@
11 var nest = require('depnest')
22 var ref = require('ssb-ref')
3-var MutantPullReduce = require('mutant-pull-reduce')
4-var SortedArray = require('sorted-array-functions')
3 +var MutantArray = require('mutant/array')
4 +var concat = require('mutant/concat')
5 +var watch = require('mutant/watch')
56
67 var { computed } = require('mutant')
78
89 exports.needs = nest({
910 'message.sync.unbox': 'first',
10- 'sbot.pull.backlinks': 'first'
11 + 'backlinks.obs.for': 'first'
1112 })
1213
1314 exports.gives = nest({
1415 'sbot.hook.publish': true,
@@ -30,74 +31,57 @@
3031 if (!c.vote || !c.vote.link) return
3132
3233 activeLikes.forEach((likes) => {
3334 if (likes.id === c.vote.link) {
34- likes.push({
35- dest: c.vote.link,
36- id: msg.key,
37- expression: c.vote.expression,
38- value: c.vote.value,
39- timestamp: msg.value.timestamp,
40- author: msg.value.author
41- })
35 + likes.push(msg)
4236 }
4337 })
4438 },
4539 'message.obs.likes': (id) => {
4640 if (!ref.isLink(id)) throw new Error('an id must be specified')
4741 var obs = get(id)
4842 obs.id = id
49- return computed(obs, getLikes, {
43 + var result = computed(obs, getLikes, {
5044 // allow manual append for simulated realtime
5145 onListen: () => activeLikes.add(obs),
5246 onUnlisten: () => activeLikes.delete(obs)
5347 })
48 + result.sync = obs.sync
49 + return result
5450 }
5551 })
5652
5753 function get (id) {
58- var likes = MutantPullReduce(api.sbot.pull.backlinks({
59- live: true,
60- query: [
61- {$filter: {
62- dest: id,
63- value: {
64- content: {
65- type: 'vote',
66- vote: { link: id }
54 + var backlinks = api.backlinks.obs.for(id)
55 + var merge = MutantArray()
56 +
57 + var likes = computed([backlinks.sync, concat([backlinks, merge])], (sync, backlinks) => {
58 + if (sync) {
59 + return backlinks.reduce((result, msg) => {
60 + var c = msg.value.content
61 + if (c.type === 'vote' && c.vote && c.vote.link === id) {
62 + var value = result[msg.value.author]
63 + if (!value || value[0] < msg.value.timestamp) {
64 + result[msg.value.author] = [msg.value.timestamp, c.vote.value, c.vote.expression]
6765 }
6866 }
69- }},
70- {$map: {
71- dest: 'dest',
72- id: 'key',
73- expression: ['value', 'content', 'vote', 'expression'],
74- value: ['value', 'content', 'vote', 'value'],
75- timestamp: 'timestamp',
76- author: ['value', 'author']
77- }}
78- ]
79- }), (result, msg) => {
80- if (!result[msg.author]) {
81- result[msg.author] = []
67 + return result
68 + }, {})
69 + } else {
70 + return {}
8271 }
83- SortedArray.add(result[msg.author], msg, mostRecent)
84- return result
85- }, {
86- startValue: []
8772 })
73 +
74 + likes.push = merge.push
75 + likes.sync = backlinks.sync
8876 return likes
8977 }
9078 }
9179
9280 function getLikes (likes) {
9381 return Object.keys(likes).reduce((result, id) => {
94- if (likes[id][0].value) {
82 + if (likes[id][1] > 0) {
9583 result.push(id)
9684 }
9785 return result
9886 }, [])
9987 }
100-
101-function mostRecent (a, b) {
102- return b.timestamp - a.timestamp
103-}
package.jsonView
@@ -1,7 +1,7 @@
11 {
22 "name": "patchcore",
3- "version": "1.5.4",
3 + "version": "1.8.1",
44 "description": "minimal core for ssb clients",
55 "main": "index.js",
66 "scripts": {
77 "start": "electro example",
@@ -36,11 +36,12 @@
3636 "color-hash": "^1.0.3",
3737 "depnest": "^1.0.2",
3838 "emoji-named-characters": "^1.0.2",
3939 "es2040": "^1.2.4",
40 + "hashlru": "^2.2.0",
4041 "html-escape": "^2.0.0",
4142 "human-time": "0.0.1",
42- "mutant": "^3.21.0",
43 + "mutant": "^3.21.2",
4344 "mutant-pull-reduce": "^1.1.0",
4445 "pull-abortable": "^4.1.0",
4546 "pull-cat": "^1.1.11",
4647 "pull-reconnect": "0.0.3",
@@ -48,9 +49,8 @@
4849 "sheet-router": "^4.2.3",
4950 "simple-mime": "^0.1.0",
5051 "sorted-array-functions": "^1.0.0",
5152 "split-buffer": "^1.0.0",
52- "ssb-avatar": "^0.2.0",
5353 "ssb-client": "^4.4.0",
5454 "ssb-config": "^2.2.0",
5555 "ssb-feed": "^2.3.0",
5656 "ssb-keys": "^7.0.9",
sbot.jsView
@@ -1,13 +1,12 @@
11 var pull = require('pull-stream')
22 var defer = require('pull-defer')
3-var { onceTrue } = require('mutant')
3 +var { Value, onceTrue, watch, Set: MutantSet } = require('mutant')
44 var ref = require('ssb-ref')
55 var Reconnect = require('pull-reconnect')
66 var createClient = require('ssb-client')
77 var createFeed = require('ssb-feed')
88 var nest = require('depnest')
9-var Value = require('mutant/value')
109 var ssbKeys = require('ssb-keys')
1110
1211 exports.needs = nest({
1312 'config.sync.load': 'first',
@@ -52,13 +51,11 @@
5251
5352 var sbot = null
5453 var connection = Value()
5554 var connectionStatus = Value()
56- var connectedPeers = Value([])
57- var localPeers = Value([])
55 + var connectedPeers = MutantSet()
56 + var localPeers = MutantSet()
5857
59- setInterval(refreshPeers, 1e3)
60-
6158 var rec = Reconnect(function (isConn) {
6259 function notify (value) {
6360 isConn(value); connectionStatus.set(value)
6461 }
@@ -76,9 +73,8 @@
7673 })
7774
7875 connection.set(sbot)
7976 notify()
80- refreshPeers()
8177 })
8278 })
8379
8480 var internal = {
@@ -89,8 +85,38 @@
8985 sbot.add(msg, cb)
9086 })
9187 }
9288
89 + watch(connection, (sbot) => {
90 + if (sbot) {
91 + sbot.gossip.peers((err, peers) => {
92 + if (err) return console.error(err)
93 + connectedPeers.set(peers.filter(x => x.state === 'connected').map(x => x.key))
94 + localPeers.set(peers.filter(x => x.source === 'local').map(x => x.key))
95 + })
96 + pull(
97 + sbot.gossip.changes(),
98 + pull.drain(data => {
99 + if (data.peer) {
100 + if (data.type === 'remove') {
101 + connectedPeers.delete(data.peer.key)
102 + localPeers.delete(data.peer.key)
103 + } else {
104 + if (data.peer.source === 'local') {
105 + localPeers.add(data.peer.key)
106 + }
107 + if (data.peer.state === 'connected') {
108 + connectedPeers.add(data.peer.key)
109 + } else {
110 + connectedPeers.delete(data.peer.key)
111 + }
112 + }
113 + }
114 + })
115 + )
116 + }
117 + })
118 +
93119 var feed = createFeed(internal, keys, {remote: true})
94120
95121 return {
96122 sbot: {
@@ -223,18 +249,8 @@
223249 // cache[msg.key] = msg.value
224250 // api.sbot.hook.feed(msg)
225251 }
226252 }
227-
228- function refreshPeers () {
229- if (sbot) {
230- sbot.gossip.peers((err, peers) => {
231- if (err) return console.error(err)
232- connectedPeers.set(peers.filter(x => x.state === 'connected').map(x => x.key))
233- localPeers.set(peers.filter(x => x.source === 'local').map(x => x.key))
234- })
235- }
236- }
237253 }
238254
239255 function Hash (onHash) {
240256 var buffers = []
backlinks/obs.jsView
@@ -1,0 +1,92 @@
1 +var nest = require('depnest')
2 +var Value = require('mutant/value')
3 +var computed = require('mutant/computed')
4 +var Abortable = require('pull-abortable')
5 +var resolve = require('mutant/resolve')
6 +var pull = require('pull-stream')
7 +var onceIdle = require('mutant/once-idle')
8 +
9 +exports.needs = nest({
10 + 'sbot.pull.backlinks': 'first'
11 +})
12 +
13 +exports.gives = nest('backlinks.obs.for', true)
14 +
15 +exports.create = function (api) {
16 + var cache = {}
17 +
18 + // cycle remove sets for fast cleanup
19 + var newRemove = new Set()
20 + var oldRemove = new Set()
21 +
22 + // run cache cleanup every 5 seconds
23 + // an item will be removed from cache between 5 - 10 seconds after release
24 + // this ensures that the data is still available for a page reload
25 + var timer = setInterval(() => {
26 + oldRemove.forEach(id => {
27 + if (cache[id]) {
28 + cache[id].destroy()
29 + delete cache[id]
30 + }
31 + })
32 + oldRemove.clear()
33 +
34 + // cycle
35 + var hold = oldRemove
36 + oldRemove = newRemove
37 + newRemove = hold
38 + }, 5e3)
39 +
40 + if (timer.unref) timer.unref()
41 +
42 + return nest({
43 + 'backlinks.obs.for': (id) => backlinks(id)
44 + })
45 +
46 + function backlinks (id) {
47 + if (!cache[id]) {
48 + var sync = Value(false)
49 + var aborter = Abortable()
50 + var collection = Value([])
51 +
52 + // try not to saturate the thread
53 + onceIdle(() => {
54 + pull(
55 + api.sbot.pull.backlinks({
56 + query: [ {$filter: { dest: id }} ],
57 + index: 'DTA', // use asserted timestamps
58 + live: true
59 + }),
60 + aborter,
61 + pull.drain((msg) => {
62 + if (msg.sync) {
63 + sync.set(true)
64 + } else {
65 + var value = resolve(collection)
66 + value.push(msg)
67 + collection.set(value)
68 + }
69 + })
70 + )
71 + })
72 +
73 + cache[id] = computed([collection], x => x, {
74 + onListen: () => use(id),
75 + onUnlisten: () => release(id)
76 + })
77 +
78 + cache[id].destroy = aborter.abort
79 + cache[id].sync = sync
80 + }
81 + return cache[id]
82 + }
83 +
84 + function use (id) {
85 + newRemove.delete(id)
86 + oldRemove.delete(id)
87 + }
88 +
89 + function release (id) {
90 + newRemove.add(id)
91 + }
92 +}

Built with git-ssb-web