Files: b57e44a4c2db39bc5672f61e5a4fdc3c9abccab7 / about / obs.js
4775 bytesRaw
1 | var {Value, Struct, Dict, computed} = require('mutant') |
2 | var pullPause = require('pull-pause') |
3 | var pull = require('pull-stream') |
4 | var msgs = require('ssb-msgs') |
5 | var nest = require('depnest') |
6 | var ref = require('ssb-ref') |
7 | var colorHash = new (require('color-hash'))() |
8 | |
9 | exports.needs = nest({ |
10 | 'sbot.pull.query': 'first', |
11 | 'blob.sync.url': '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 | ] |
25 | }) |
26 | |
27 | exports.create = function (api) { |
28 | var sync = Value(false) |
29 | var cache = {} |
30 | var cacheLoading = false |
31 | |
32 | return nest({ |
33 | 'about.obs': { |
34 | name: (id) => get(id).displayName, |
35 | description: (id) => get(id).description, |
36 | image: (id) => get(id).image, |
37 | imageUrl: (id) => get(id).imageUrl, |
38 | |
39 | names: (id) => get(id).names, |
40 | images: (id) => get(id).images, |
41 | color: (id) => computed(id, (id) => colorHash.hex(id)) |
42 | } |
43 | }) |
44 | |
45 | function get (id) { |
46 | if (!cacheLoading) { |
47 | cacheLoading = true |
48 | loadCache() |
49 | } |
50 | if (!cache[id]) { |
51 | cache[id] = About(api, id, sync) |
52 | } |
53 | return cache[id] |
54 | } |
55 | |
56 | function loadCache () { |
57 | pull( |
58 | api.sbot.pull.query({ |
59 | query: [ |
60 | {$filter: { |
61 | value: { |
62 | content: { |
63 | type: 'about' |
64 | } |
65 | } |
66 | }}, |
67 | {$map: { |
68 | timestamp: 'timestamp', |
69 | author: ['value', 'author'], |
70 | id: ['value', 'content', 'about'], |
71 | name: ['value', 'content', 'name'], |
72 | image: ['value', 'content', 'image'], |
73 | description: ['value', 'content', 'description'] |
74 | }} |
75 | ], |
76 | live: true |
77 | }), |
78 | pull.drain( |
79 | msg => { |
80 | if (msg.sync) { |
81 | sync.set(true) |
82 | } else if (msgs.isLink(msg.id, 'feed')) { |
83 | get(msg.id).push(msg) |
84 | } |
85 | }, |
86 | () => sync.set(true) |
87 | ) |
88 | ) |
89 | } |
90 | } |
91 | |
92 | function About (api, id, sync) { |
93 | if (!ref.isLink(id)) throw new Error('About requires an id!') |
94 | |
95 | var pauser = pullPause((paused) => {}) |
96 | |
97 | // transparent image |
98 | var fallbackImageUrl = 'data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' |
99 | |
100 | var yourId = api.keys.sync.id() |
101 | var lastestTimestamps = {} |
102 | |
103 | var obs = Struct({ |
104 | assignedNames: Dict(), |
105 | assignedImages: Dict(), |
106 | assignedDescriptions: Dict() |
107 | }, { |
108 | onListen: pauser.resume, |
109 | onUnlisten: pauser.pause |
110 | }) |
111 | |
112 | obs.sync = computed([sync, obs], (v) => v) |
113 | obs.displayName = computed([obs.assignedNames, id, yourId, id.slice(1, 10)], socialValue) |
114 | obs.description = computed([obs.assignedDescriptions, id, yourId], socialValue) |
115 | obs.image = computed([obs.assignedImages, id, yourId], socialValue) |
116 | |
117 | obs.names = computed(obs.assignedNames, indexByValue) |
118 | obs.images = computed(obs.assignedImages, indexByValue) |
119 | |
120 | obs.imageUrl = computed(obs.image, (blobId) => { |
121 | if (blobId) { |
122 | return api.blob.sync.url(blobId) |
123 | } else { |
124 | return fallbackImageUrl |
125 | } |
126 | }) |
127 | |
128 | obs.push = push |
129 | |
130 | return obs |
131 | |
132 | // scoped |
133 | |
134 | function push (msg) { |
135 | if (!lastestTimestamps[msg.author]) { |
136 | lastestTimestamps[msg.author] = { |
137 | name: 0, image: 0, description: 0 |
138 | } |
139 | } |
140 | if (msg.name && lastestTimestamps[msg.author].name < msg.timestamp) { |
141 | lastestTimestamps[msg.author].name = msg.timestamp |
142 | obs.assignedNames.put(msg.author, msg.name) |
143 | } |
144 | if (msg.image && lastestTimestamps[msg.author].image < msg.timestamp) { |
145 | lastestTimestamps[msg.author].image = msg.timestamp |
146 | var obj = msgs.link(msg.image, 'blob') |
147 | if (obj && obj.link) { |
148 | obs.assignedImages.put(msg.author, obj.link) |
149 | } |
150 | } |
151 | if (msg.description && lastestTimestamps[msg.author].description < msg.timestamp) { |
152 | lastestTimestamps[msg.author].description = msg.timestamp |
153 | obs.assignedDescriptions.put(msg.author, msg.description) |
154 | } |
155 | } |
156 | } |
157 | |
158 | function socialValue (lookup, id, yourId, fallback) { |
159 | return lookup[yourId] || lookup[id] || highestRank(lookup) || fallback || null |
160 | } |
161 | |
162 | function highestRank (lookup) { |
163 | var indexed = indexByValue(lookup) |
164 | var highestCount = 0 |
165 | var currentHighest = null |
166 | Object.keys(indexed).forEach((item) => { |
167 | var count = indexed[item].length |
168 | if (count > highestCount) { |
169 | highestCount = count |
170 | currentHighest = item |
171 | } |
172 | }) |
173 | return currentHighest |
174 | } |
175 | |
176 | function indexByValue (lookup) { |
177 | var result = {} |
178 | Object.keys(lookup).forEach((key) => { |
179 | var value = lookup[key] |
180 | if (!result[value]) { |
181 | result[value] = [] |
182 | } |
183 | result[value].push(key) |
184 | }) |
185 | return result |
186 | } |
187 | |
188 | function isAbout (msg) { |
189 | return msg.value && msg.value.content && msg.value.content.type === 'about' |
190 | } |
191 |
Built with git-ssb-web