aboutsummaryrefslogtreecommitdiff
path: root/lib/about.js
blob: fde5e042e3927261bbbd23910a2736757d09460f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
var pull = require('pull-stream')
var multicb = require('multicb')
var cat = require('pull-cat')
var u = require('./util')

module.exports = About

function About(app, myId) {
  this.app = app
  this.myId = myId
}

About.prototype.createAboutOpStream = function (id) {
  return pull(
    this.app.sbot.links({dest: id, rel: 'about', values: true, reverse: true}),
    pull.map(function (msg) {
      var c = msg.value.content || {}
      return Object.keys(c).filter(function (key) {
        return key !== 'about'
          && key !== 'type'
          && key !== 'recps'
      }).map(function (key) {
        var value = c[key]
        if (u.isRef(value)) value = {link: value}
        return {
          id: msg.key,
          author: msg.value.author,
          timestamp: msg.value.timestamp,
          prop: key,
          value: value,
          remove: value && typeof value === 'object' && value.remove,
        }
      })
    }),
    pull.flatten()
  )
}

About.prototype.createAboutStreams = function (id) {
  var ops = this.createAboutOpStream(id)
  var scalars = {/* author: {prop: value} */}
  var sets = {/* author: {prop: {link}} */}

  var setsDone = multicb({pluck: 1, spread: true})
  setsDone()(null, pull.values([]))
  return {
    scalars: pull(
      ops,
      pull.unique(function (op) {
        return op.author + '-' + op.prop + '-' + (op.value ? op.value.link : '')
      }),
      pull.filter(function (op) {
        return !op.remove
      })
    ),
    sets: u.readNext(setsDone)
  }
}

function computeTopAbout(aboutByFeed) {
  var propValueCounts = {/* prop: {value: count} */}
  var topValues = {/* prop: value */}
  var topValueCounts = {/* prop: count */}
  for (var feed in aboutByFeed) {
    var feedAbout = aboutByFeed[feed]
    for (var prop in feedAbout) {
      var value = feedAbout[prop]
      var valueCounts = propValueCounts[prop] || (propValueCounts[prop] = {})
      var count = (valueCounts[value] || 0) + 1
      valueCounts[value] = count
      if (count > (topValueCounts[prop] || 0)) {
        topValueCounts[prop] = count
        topValues[prop] = value
      }
    }
  }
  return topValues
}

About.prototype.get = function (dest, cb) {
  var self = this
  var aboutByFeed = {}
  pull(
    cat([
      dest[0] === '%' && self.app.pullGetMsg(dest),
      self.app.sbot.links({
        rel: 'about',
        dest: dest,
        values: true,
      })
    ]),
    self.app.unboxMessages(),
    pull.drain(function (msg) {
      var author = msg.value.author
      var c = msg.value.content
      if (!c) return
      var about = aboutByFeed[author] || (aboutByFeed[author] = {})
      if (c.name) about.name = c.name
      if (c.image) about.image = u.linkDest(c.image)
      if (c.description) about.description = c.description
    }, function (err) {
      if (err) return cb(err)
      // bias the author's choices by giving them an extra vote
      aboutByFeed._author = aboutByFeed[dest]
      var about = {}
      var myAbout = aboutByFeed[this.myId] || {}
      var topAbout = computeTopAbout(aboutByFeed)
      for (var k in topAbout) about[k] = topAbout[k]
      // always prefer own choices
      for (var k in myAbout) about[k] = myAbout[k]
      cb(null, about)
    })
  )
}