Files: eb221fb4cfcd450d2458461fa14d5079ee0a3bd7 / feed / obs / thread.js
3315 bytesRaw
1 | var nest = require('depnest') |
2 | var sort = require('ssb-sort') |
3 | var ref = require('ssb-ref') |
4 | var { Array: MutantArray, Value, map, concat, computed } = require('mutant') |
5 | |
6 | exports.needs = nest({ |
7 | 'backlinks.obs.for': 'first', |
8 | 'sbot.async.get': 'first', |
9 | 'message.sync.unbox': 'first', |
10 | 'message.sync.root': 'first', |
11 | 'contact.obs.blocking': 'first', |
12 | 'keys.sync.id': 'first' |
13 | }) |
14 | |
15 | exports.gives = nest('feed.obs.thread') |
16 | |
17 | exports.create = function (api) { |
18 | return nest('feed.obs.thread', thread) |
19 | |
20 | function thread (rootId, { branch } = {}) { |
21 | if (!ref.isLink(rootId)) throw new Error('an id must be specified') |
22 | var sync = Value(false) |
23 | |
24 | const blocking = api.contact.obs.blocking(api.keys.sync.id()) |
25 | |
26 | var prepend = MutantArray() |
27 | api.sbot.async.get(rootId, (err, value) => { |
28 | sync.set(true) |
29 | if (!err) { |
30 | var msg = unboxIfNeeded({key: rootId, value}) |
31 | if (blocking().includes(msg.value.author)) msg.isBlocked = true |
32 | prepend.push(Value(msg)) |
33 | } |
34 | }) |
35 | |
36 | var backlinks = api.backlinks.obs.for(rootId) |
37 | var replies = map(computed(backlinks, (msgs) => { |
38 | return msgs.filter(msg => { |
39 | return !blocking().includes(msg.value.author) && |
40 | msg.value.content.type !== 'vote' && ( |
41 | api.message.sync.root(msg) === rootId || |
42 | matchAny(msg.value.content.branch, rootId) |
43 | ) |
44 | }) |
45 | }), x => Value(x), { |
46 | // avoid refresh of entire list when items added |
47 | comparer: (a, b) => a === b |
48 | }) |
49 | |
50 | var messages = concat([prepend, replies]) |
51 | |
52 | var result = { |
53 | messages, |
54 | lastId: computed(messages, (messages) => { |
55 | var branches = sort.heads(messages) |
56 | if (branches.length <= 1) { |
57 | branches = branches[0] |
58 | } |
59 | return branches |
60 | }), |
61 | rootId: computed(messages, (messages) => { |
62 | if (branch && messages.length) { |
63 | return messages[0].value.content.root |
64 | } else { |
65 | return rootId |
66 | } |
67 | }), |
68 | branchId: computed(messages, (messages) => { |
69 | if (branch) return rootId |
70 | }), |
71 | previousKey: function (msg) { |
72 | return PreviousKey(result.messages, msg) |
73 | }, |
74 | isPrivate: computed(messages, msgs => { |
75 | if (!msgs[0]) return false |
76 | |
77 | return msgs[0].value.private || false |
78 | }), |
79 | channel: computed(messages, msgs => { |
80 | if (!msgs[0]) return undefined |
81 | |
82 | return msgs[0].value.content.channel |
83 | }), |
84 | recps: computed(messages, msgs => { |
85 | if (!msgs[0]) return undefined |
86 | |
87 | return msgs[0].value.content.recps |
88 | }) |
89 | } |
90 | |
91 | result.sync = computed([backlinks.sync, sync], (a, b) => a && b, {idle: true}) |
92 | return result |
93 | } |
94 | |
95 | function unboxIfNeeded (msg) { |
96 | if (msg.value && typeof msg.value.content === 'string') { |
97 | return api.message.sync.unbox(msg) || msg |
98 | } else { |
99 | return msg |
100 | } |
101 | } |
102 | } |
103 | |
104 | function PreviousKey (collection, item) { |
105 | return computed(collection, (c) => { |
106 | var index = collection.indexOf(item) |
107 | if (~index) { |
108 | var previous = c[index - 1] |
109 | if (previous) { |
110 | return previous.key |
111 | } |
112 | } |
113 | }) |
114 | } |
115 | |
116 | function matchAny (valueOrArray, compare) { |
117 | if (valueOrArray === compare) { |
118 | return true |
119 | } else if (Array.isArray(valueOrArray)) { |
120 | return valueOrArray.includes(compare) |
121 | } |
122 | } |
123 |
Built with git-ssb-web