Files: a36543131c127121355391b4c743f49b7280b2aa / lib / about.js
4476 bytesRaw
1 | var pull = require('pull-stream') |
2 | var multicb = require('multicb') |
3 | var cat = require('pull-cat') |
4 | var u = require('./util') |
5 | |
6 | module.exports = About |
7 | |
8 | function About(app, myId, follows) { |
9 | this.app = app |
10 | this.myId = myId |
11 | this.follows = follows |
12 | } |
13 | |
14 | About.prototype.createAboutOpStream = function (id) { |
15 | return pull( |
16 | this.app.sbot.links({dest: id, rel: 'about', values: true, reverse: true, private: true, meta: false}), |
17 | this.app.unboxMessages(), |
18 | pull.map(function (msg) { |
19 | var c = msg.value.content |
20 | if (typeof c !== 'object' || c === null) return [] |
21 | return Object.keys(c).filter(function (key) { |
22 | return key !== 'about' |
23 | && key !== 'type' |
24 | && key !== 'recps' |
25 | && key !== 'reason' |
26 | }).map(function (key) { |
27 | var value = c[key] |
28 | return { |
29 | id: msg.key, |
30 | author: msg.value.author, |
31 | timestamp: msg.value.timestamp, |
32 | prop: key, |
33 | value: value, |
34 | remove: value && value.remove, |
35 | } |
36 | }) |
37 | }), |
38 | pull.flatten() |
39 | ) |
40 | } |
41 | |
42 | About.prototype.createAboutStreams = function (id) { |
43 | var ops = this.createAboutOpStream(id) |
44 | var scalars = {/* author: {prop: value} */} |
45 | var sets = {/* author: {prop: {link}} */} |
46 | |
47 | var setsDone = multicb({pluck: 1, spread: true}) |
48 | setsDone()(null, pull.values([])) |
49 | return { |
50 | scalars: pull( |
51 | ops, |
52 | pull.unique(function (op) { |
53 | return op.author + '-' + op.prop + '-' |
54 | }), |
55 | pull.filter(function (op) { |
56 | return !op.remove |
57 | }) |
58 | ), |
59 | sets: u.readNext(setsDone) |
60 | } |
61 | } |
62 | |
63 | function computeTopAbout(aboutByFeed) { |
64 | var propValueCounts = {/* prop: {value: count} */} |
65 | var topValues = {/* prop: value */} |
66 | var topValueCounts = {/* prop: count */} |
67 | for (var feed in aboutByFeed) { |
68 | var feedAbout = aboutByFeed[feed] |
69 | for (var prop in feedAbout) { |
70 | var value = feedAbout[prop] |
71 | var valueCounts = propValueCounts[prop] || (propValueCounts[prop] = {}) |
72 | var count = (valueCounts[value] || 0) + 1 |
73 | valueCounts[value] = count |
74 | if (count > (topValueCounts[prop] || 0)) { |
75 | topValueCounts[prop] = count |
76 | topValues[prop] = value |
77 | } |
78 | } |
79 | } |
80 | return topValues |
81 | } |
82 | |
83 | About.prototype.get = function (dest, cb) { |
84 | var self = this |
85 | var myAbout = [] |
86 | var aboutByFeed = {} |
87 | var aboutByFeedFollowed = {} |
88 | this.follows.getFollows(this.myId, function (err, follows) { |
89 | if (err) return cb(err) |
90 | pull( |
91 | cat([ |
92 | dest[0] === '%' && self.app.pullGetMsg(dest), |
93 | self.app.sbot.links({ |
94 | rel: 'about', |
95 | dest: dest, |
96 | values: true, |
97 | private: true, |
98 | meta: false |
99 | }) |
100 | ]), |
101 | self.app.unboxMessages(), |
102 | pull.drain(function (msg) { |
103 | var author = msg.value.author |
104 | var c = msg.value.content |
105 | if (!c) return |
106 | if (msg.key === dest && c.type === 'about') { |
107 | // don't describe an about message with itself |
108 | return |
109 | } |
110 | var about = author === self.myId ? myAbout : |
111 | follows[author] ? |
112 | aboutByFeedFollowed[author] || (aboutByFeedFollowed[author] = {}) : |
113 | aboutByFeed[author] || (aboutByFeed[author] = {}) |
114 | |
115 | if (c.name) about.name = c.name |
116 | if (c.title) about.title = c.title |
117 | if (c.image) { |
118 | about.image = u.linkDest(c.image) |
119 | about.imageLink = u.toLink(c.image) |
120 | } |
121 | if (c.description) about.description = c.description |
122 | if (c.publicWebHosting) about.publicWebHosting = c.publicWebHosting |
123 | }, function (err) { |
124 | if (err) return cb(err) |
125 | var destAbout = aboutByFeedFollowed[dest] || aboutByFeed[dest] |
126 | // bias the author's choices by giving them an extra vote |
127 | if (destAbout) { |
128 | if (follows[dest]) aboutByFeedFollowed._author = destAbout |
129 | else aboutByFeed._author = destAbout |
130 | } |
131 | var about = {} |
132 | var followedAbout = computeTopAbout(aboutByFeedFollowed) |
133 | var topAbout = computeTopAbout(aboutByFeed) |
134 | for (var k in topAbout) about[k] = topAbout[k] |
135 | // prefer followed feeds' choices |
136 | for (var k in followedAbout) about[k] = followedAbout[k] |
137 | // if we follow the destination/author feed, prefer its choices |
138 | if (follows[dest]) for (var k in destAbout) about[k] = destAbout[k] |
139 | // always prefer own choices |
140 | for (var k in myAbout) about[k] = myAbout[k] |
141 | cb(null, about) |
142 | }) |
143 | ) |
144 | }) |
145 | } |
146 |
Built with git-ssb-web