Files: 1c0ca68774c4b8604b4acc3288982a21f90f8401 / contact / obs.js
2699 bytesRaw
1 | var nest = require('depnest') |
2 | var {Value, computed} = require('mutant') |
3 | var pull = require('pull-stream') |
4 | var ref = require('ssb-ref') |
5 | |
6 | exports.needs = nest({ |
7 | 'sbot.pull.stream': 'first' |
8 | }) |
9 | |
10 | exports.gives = nest({ |
11 | 'contact.obs': ['following', 'followers'], |
12 | 'sbot.hook.publish': true |
13 | }) |
14 | |
15 | exports.create = function (api) { |
16 | var cacheLoading = false |
17 | var cache = {} |
18 | var sync = Value(false) |
19 | |
20 | return nest({ |
21 | 'contact.obs': { |
22 | following: (id) => values(get(id), 'following', true), |
23 | followers: (id) => values(get(id), 'followers', true) |
24 | }, |
25 | 'sbot.hook.publish': function (msg) { |
26 | if (isContact(msg)) { |
27 | // HACK: make interface more responsive when sbot is busy |
28 | var source = msg.value.author |
29 | var dest = msg.value.content.contact |
30 | if (typeof msg.value.content.following === 'boolean') { |
31 | update(source, { |
32 | following: { |
33 | [dest]: [msg.value.content] |
34 | } |
35 | }) |
36 | update(dest, { |
37 | followers: { |
38 | [source]: [msg.value.content] |
39 | } |
40 | }) |
41 | } |
42 | } |
43 | } |
44 | }) |
45 | |
46 | function values (state, key, compare) { |
47 | var obs = computed([state, key, compare], getIds) |
48 | obs.sync = sync |
49 | return obs |
50 | } |
51 | |
52 | function loadCache () { |
53 | pull( |
54 | api.sbot.pull.stream(sbot => sbot.contacts.stream({live: true})), |
55 | pull.drain(item => { |
56 | for (var target in item) { |
57 | if (ref.isFeed(target)) update(target, item[target]) |
58 | } |
59 | |
60 | if (!sync()) { |
61 | sync.set(true) |
62 | } |
63 | }) |
64 | ) |
65 | } |
66 | |
67 | function update (id, values) { |
68 | var state = get(id) |
69 | var lastState = state() |
70 | var changed = false |
71 | for (var key in values) { |
72 | var valuesForKey = lastState[key] = lastState[key] || {} |
73 | for (var dest in values[key]) { |
74 | var value = values[key][dest] |
75 | if (!valuesForKey[dest] || value[1] > valuesForKey[dest][1] || !values[1] || !valuesForKey[dest[1]]) { |
76 | valuesForKey[dest] = value |
77 | changed = true |
78 | } |
79 | } |
80 | } |
81 | if (changed) { |
82 | state.set(lastState) |
83 | } |
84 | } |
85 | |
86 | function get (id) { |
87 | if (!ref.isFeed(id)) throw new Error('Contact state requires an id!') |
88 | if (!cacheLoading) { |
89 | cacheLoading = true |
90 | loadCache() |
91 | } |
92 | if (!cache[id]) { |
93 | cache[id] = Value({}) |
94 | } |
95 | return cache[id] |
96 | } |
97 | } |
98 | |
99 | function getIds (state, key, compare) { |
100 | var result = new Set() |
101 | if (state[key]) { |
102 | for (var dest in state[key]) { |
103 | if (state[key][dest][0] === compare) { |
104 | result.add(dest) |
105 | } |
106 | } |
107 | } |
108 | return result |
109 | } |
110 | |
111 | function isContact (msg) { |
112 | return msg.value && msg.value.content && msg.value.content.type === 'contact' |
113 | } |
114 |
Built with git-ssb-web