Files: dcb68fb50cc8bee039bf3b455495c4057767384e / about / obs.js
5251 bytesRaw
1 | var {Value, computed} = require('mutant') |
2 | var pull = require('pull-stream') |
3 | var nest = require('depnest') |
4 | var ref = require('ssb-ref') |
5 | var colorHash = new (require('color-hash'))() |
6 | var fallbackImageUrl = 'data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' |
7 | |
8 | exports.needs = nest({ |
9 | 'sbot.pull.stream': 'first', |
10 | 'blob.sync.url': 'first', |
11 | 'about.sync.shortFeedId': 'first', |
12 | 'keys.sync.id': 'first' |
13 | }) |
14 | |
15 | exports.gives = nest({ |
16 | 'about.obs': [ |
17 | 'name', |
18 | 'description', |
19 | 'image', |
20 | 'imageUrl', |
21 | 'names', |
22 | 'images', |
23 | 'color', |
24 | 'latestValue', |
25 | 'valueFrom', |
26 | 'socialValue', |
27 | 'groupedValues' |
28 | ] |
29 | }) |
30 | |
31 | exports.create = function (api) { |
32 | var syncValue = Value(false) |
33 | var sync = computed(syncValue, x => x) |
34 | var cache = null |
35 | |
36 | return nest({ |
37 | 'about.obs': { |
38 | // quick helpers, probably should deprecate! |
39 | name: (id) => socialValue(id, 'name', api.about.sync.shortFeedId(id)), |
40 | description: (id) => socialValue(id, 'description'), |
41 | image: (id) => socialValue(id, 'image'), |
42 | names: (id) => groupedValues(id, 'name'), |
43 | images: (id) => groupedValues(id, 'image'), |
44 | color: (id) => computed(id, (id) => colorHash.hex(id)), |
45 | imageUrl: (id) => computed(socialValue(id, 'image'), (blobId) => { |
46 | return blobId ? api.blob.sync.url(blobId) : fallbackImageUrl |
47 | }), |
48 | |
49 | // custom abouts (the future!) |
50 | valueFrom, |
51 | latestValue, |
52 | socialValue, |
53 | groupedValues |
54 | } |
55 | }) |
56 | |
57 | function valueFrom (id, key, author) { |
58 | if (!ref.isLink(id)) throw new Error('About requires an ssb ref!') |
59 | return withSync(computed([get(id), key, author], getValueFrom)) |
60 | } |
61 | |
62 | function latestValue (id, key) { |
63 | if (!ref.isLink(id)) throw new Error('About requires an ssb ref!') |
64 | return withSync(computed([get(id), key], getLatestValue)) |
65 | } |
66 | |
67 | function socialValue (id, key, defaultValue) { |
68 | if (!ref.isLink(id)) throw new Error('About requires an ssb ref!') |
69 | var yourId = api.keys.sync.id() |
70 | return withSync(computed([get(id), key, id, yourId, defaultValue], getSocialValue)) |
71 | } |
72 | |
73 | function groupedValues (id, key) { |
74 | if (!ref.isLink(id)) throw new Error('About requires an ssb ref!') |
75 | return withSync(computed([get(id), key], getGroupedValues)) |
76 | } |
77 | |
78 | function withSync (obs) { |
79 | obs.sync = sync |
80 | return obs |
81 | } |
82 | |
83 | function get (id) { |
84 | if (!ref.isLink(id)) throw new Error('About requires an ssb ref!') |
85 | load() |
86 | if (!cache[id]) { |
87 | cache[id] = Value({}) |
88 | } |
89 | return cache[id] |
90 | } |
91 | |
92 | function load () { |
93 | if (!cache) { |
94 | cache = {} |
95 | pull( |
96 | api.sbot.pull.stream(sbot => sbot.about.stream({live: true})), |
97 | pull.drain(item => { |
98 | for (var target in item) { |
99 | var state = get(target) |
100 | var lastState = state() |
101 | var values = item[target] |
102 | var changed = false |
103 | for (var key in values) { |
104 | var valuesForKey = lastState[key] = lastState[key] || {} |
105 | for (var author in values[key]) { |
106 | var value = values[key][author] |
107 | if (!valuesForKey[author] || value[1] > valuesForKey[author][1]) { |
108 | valuesForKey[author] = value |
109 | changed = true |
110 | } |
111 | } |
112 | } |
113 | if (changed) { |
114 | state.set(lastState) |
115 | } |
116 | } |
117 | |
118 | if (!syncValue()) { |
119 | syncValue.set(true) |
120 | } |
121 | }) |
122 | ) |
123 | } |
124 | } |
125 | } |
126 | |
127 | function getSocialValue (lookup, key, id, yourId, fallback) { |
128 | var result = lookup[key] ? getValue(lookup[key][yourId]) || getValue(lookup[key][id]) || highestRank(lookup[key]) : null |
129 | if (result != null) { |
130 | return result |
131 | } else { |
132 | return fallback || null |
133 | } |
134 | } |
135 | |
136 | function getGroupedValues (lookup, key) { |
137 | var values = {} |
138 | for (var author in lookup[key]) { |
139 | var value = getValue(lookup[key][author]) |
140 | if (value != null) { |
141 | values[value] = values[value] || [] |
142 | values[value].push(author) |
143 | } |
144 | } |
145 | return values |
146 | } |
147 | |
148 | function highestRank (lookup) { |
149 | var counts = {} |
150 | var highestCount = 0 |
151 | var currentHighest = null |
152 | for (var key in lookup) { |
153 | var value = getValue(lookup[key]) |
154 | if (value != null) { |
155 | counts[value] = (counts[value] || 0) + 1 |
156 | if (counts[value] > highestCount) { |
157 | currentHighest = value |
158 | highestCount = counts[value] |
159 | } |
160 | } |
161 | } |
162 | return currentHighest |
163 | } |
164 | |
165 | function getValue (item) { |
166 | if (item && item[0]) { |
167 | if (typeof item[0] === 'string') { |
168 | return item[0] |
169 | } else if (item[0] && item[0].link && ref.isLink(item[0].link) && !item[0].remove) { |
170 | return item[0].link |
171 | } |
172 | } |
173 | } |
174 | |
175 | function getLatestValue (lookup, key) { |
176 | var latestTime = 0 |
177 | var latestValue = null |
178 | |
179 | if (lookup[key]) { |
180 | for (var author in lookup[key]) { |
181 | if (Array.isArray(lookup[key][author])) { |
182 | var value = lookup[key][author][0] |
183 | var timestamp = lookup[key][author][1] |
184 | if (timestamp > latestTime) { |
185 | latestTime = timestamp |
186 | latestValue = value |
187 | } |
188 | } |
189 | } |
190 | } |
191 | |
192 | return latestValue |
193 | } |
194 | |
195 | function getValueFrom (lookup, key, author) { |
196 | if (lookup[key] && Array.isArray(lookup[key][author])) { |
197 | return lookup[key][author][0] |
198 | } |
199 | } |
200 |
Built with git-ssb-web