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