-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
130 lines (111 loc) · 3.52 KB
/
index.js
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
var pull = require('pull-stream')
var Paramap = require('pull-paramap')
var merge = require('lodash.merge')
var profileAvatar = require('./lib/avatar')
var startsWith = require('./lib/starts-with')
var reduceMatches = require('./lib/reduce-matches')
var watch = require('./lib/watch')
exports.name = 'suggest'
exports.version = require('./package.json').version
exports.manifest = {
profile: 'async'
}
exports.init = function (ssb, config) {
var state = {
suggestCache: {},
updateQueue: new Set(),
following: new Set(),
recentAuthors: [],
quiting: false
}
ssb.close.hook(function (fn, args) {
state.quiting = true
return fn.apply(this, args)
})
var avatar = ssb.patchwork ? ssb.patchwork.profile.avatar : profileAvatar(ssb, state)
// start update loop after 5 seconds
setTimeout(updateLoop, 5e3)
setTimeout(() => watch.follows(ssb, state), 3e3)
setTimeout(() => watch.newAbouts(ssb, state), 5e3)
setTimeout(() => watch.recentAuthors(ssb, state), 10e3)
function updateLoop () {
if (state.updateQueue.size) {
var ids = Array.from(state.updateQueue)
state.updateQueue.clear()
update(ids, () => {
if (state.updateQueue.size) {
updateLoop()
} else {
setTimeout(updateLoop, 10e3)
}
})
} else {
setTimeout(updateLoop, 10e3)
}
}
function update (ids, cb) {
if (!Array.isArray(ids)) return cb()
if (!ids.length) return cb()
pull(
pull.values(ids),
Paramap((id, cb) => avatar({ id }, cb), 10),
pull.drain(
item => {
state.suggestCache[item.id] = item
},
cb
)
)
}
return {
profile: function suggestProfile ({ text, limit, defaultIds = [] }, cb) {
defaultIds = Array.from(new Set(defaultIds)) // uniq!
.filter(feedId => feedId !== ssb.id) // not me
update(defaultIds.filter(id => !state.suggestCache[id]), function (err) {
if (err) return cb(err)
if (typeof text === 'string' && text.trim().length) {
let matches = getMatches(state.suggestCache, text)
let result = reduceMatches(text, matches, defaultIds, state.recentAuthors, state.following)
if (limit) {
result = result.slice(0, limit)
}
result = result
.map(match => merge(
match.avatar,
{ matchedName: match.name },
{ following: state.following.has(match.avatar.id) } // add following attribute
))
cb(null, result)
} else if (defaultIds && defaultIds.length) {
let result = defaultIds
.map(id => state.suggestCache[id])
.filter(Boolean)
.map(x => merge(x, { following: state.following.has(x.id) }))
cb(null, result)
} else {
let result = state.recentAuthors.slice(-(limit || 20)).reverse()
result = result
.map(feedId => state.suggestCache[feedId])
.filter(Boolean)
.map(x => merge(x, { following: state.following.has(x.id) }))
cb(null, result)
}
})
}
}
}
function getMatches (cache, text) {
var matches = []
var values = Object.values(cache)
values.forEach((avatar) => {
if (startsWith(avatar.name, text)) {
matches.push({ name: avatar.name, avatar })
}
avatar.names
.filter(name => name !== avatar.name && startsWith(name, text))
.forEach(alias => {
matches.push({ name: alias, avatar })
})
})
return Object.values(matches)
}