Files: 4f3e54eaa5e60f7e8551798b984fbf3d4e0886c1 / contact / obs.js
3656 bytesRaw
1 | var nest = require('depnest') |
2 | var { Value, Dict, 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', 'blocking', 'blockers'], |
12 | 'sbot.hook.publish': true |
13 | }) |
14 | |
15 | exports.create = function (api) { |
16 | var cacheLoading = false |
17 | var cache = Dict() |
18 | var sync = Value(false) |
19 | |
20 | return nest({ |
21 | 'contact.obs': { |
22 | following: following, |
23 | followers: followers, |
24 | blocking: blocking, |
25 | blockers: blockers |
26 | }, |
27 | 'sbot.hook.publish': function (msg) { |
28 | if (!isContact(msg)) return |
29 | |
30 | // HACK: make interface more responsive when sbot is busy |
31 | var source = msg.value.author |
32 | var dest = msg.value.content.contact |
33 | var tristate = ( // from ssb-friends |
34 | msg.value.content.following ? true |
35 | : msg.value.content.flagged || msg.value.content.blocking ? false |
36 | : null |
37 | ) |
38 | |
39 | update(source, { [dest]: tristate }) |
40 | } |
41 | }) |
42 | |
43 | // states: |
44 | // true = following, |
45 | // null = neutral (may have unfollowed), |
46 | // false = blocking |
47 | |
48 | function following (key) { |
49 | var obs = computed(get(key), state => { |
50 | return Object.keys(state) |
51 | .reduce((sofar, next) => { |
52 | if (state[next] === true) return [...sofar, next] |
53 | else return sofar |
54 | }, []) |
55 | }) |
56 | |
57 | obs.sync = sync |
58 | return obs |
59 | } |
60 | |
61 | function followers (key) { |
62 | var obs = computed(cache, cache => { |
63 | return Object.keys(cache) |
64 | .reduce((sofar, next) => { |
65 | if (cache[next][key] === true) return [...sofar, next] |
66 | else return sofar |
67 | }, []) |
68 | }) |
69 | |
70 | obs.sync = sync |
71 | return obs |
72 | } |
73 | |
74 | function blocking (key) { |
75 | var obs = computed(get(key), state => { |
76 | return Object.keys(state) |
77 | .reduce((sofar, next) => { |
78 | if (state[next] === false) return [...sofar, next] |
79 | else return sofar |
80 | }, []) |
81 | }) |
82 | |
83 | obs.sync = sync |
84 | return obs |
85 | } |
86 | |
87 | function blockers (key) { |
88 | var obs = computed(cache, cache => { |
89 | return Object.keys(cache) |
90 | .reduce((sofar, next) => { |
91 | if (cache[next][key] === false) return [...sofar, next] |
92 | else return sofar |
93 | }, []) |
94 | }) |
95 | |
96 | obs.sync = sync |
97 | return obs |
98 | } |
99 | |
100 | |
101 | function loadCache () { |
102 | pull( |
103 | api.sbot.pull.stream(sbot => sbot.friends.stream({live: true})), |
104 | pull.drain(item => { |
105 | if (!sync()) { |
106 | // initial dump |
107 | for (var source in item) { |
108 | if (ref.isFeed(source)) update(source, item[source]) |
109 | } |
110 | sync.set(true) |
111 | } else { |
112 | // handle realtime updates |
113 | update(item.from, {[item.to]: item.value}) |
114 | } |
115 | }) |
116 | ) |
117 | } |
118 | |
119 | function update (sourceId, values) { |
120 | // ssb-friends: values = { |
121 | // keyA: true|null|false (friend, neutral, block) |
122 | // keyB: true|null|false (friend, neutral, block) |
123 | // } |
124 | var state = get(sourceId) |
125 | var lastState = state() |
126 | var changed = false |
127 | for (var targetId in values) { |
128 | if (values[targetId] != lastState[targetId]) { |
129 | lastState[targetId] = values[targetId] |
130 | changed = true |
131 | } |
132 | } |
133 | |
134 | if (changed) { |
135 | state.set(lastState) |
136 | } |
137 | } |
138 | |
139 | function get (id) { |
140 | if (!ref.isFeed(id)) throw new Error('Contact state requires an id!') |
141 | if (!cacheLoading) { |
142 | cacheLoading = true |
143 | loadCache() |
144 | } |
145 | if (!cache.has(id)) { |
146 | cache.put(id, Value({})) |
147 | } |
148 | return cache.get(id) |
149 | } |
150 | } |
151 | |
152 | function isContact (msg) { |
153 | return msg.value && msg.value.content && msg.value.content.type === 'contact' |
154 | } |
155 | |
156 |
Built with git-ssb-web