git ssb

7+

dinoworm ๐Ÿ› / patchcore



Tree: 4f3e54eaa5e60f7e8551798b984fbf3d4e0886c1

Files: 4f3e54eaa5e60f7e8551798b984fbf3d4e0886c1 / contact / obs.js

3656 bytesRaw
1var nest = require('depnest')
2var { Value, Dict, computed } = require('mutant')
3var pull = require('pull-stream')
4var ref = require('ssb-ref')
5
6exports.needs = nest({
7 'sbot.pull.stream': 'first'
8})
9
10exports.gives = nest({
11 'contact.obs': ['following', 'followers', 'blocking', 'blockers'],
12 'sbot.hook.publish': true
13})
14
15exports.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
152function isContact (msg) {
153 return msg.value && msg.value.content && msg.value.content.type === 'contact'
154}
155
156

Built with git-ssb-web