git ssb

7+

dinoworm ๐Ÿ› / patchcore



Tree: d47c6e10a9fe8fb04aee3e2736ee04a7f2896003

Files: d47c6e10a9fe8fb04aee3e2736ee04a7f2896003 / feed / obs / thread.js

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

Built with git-ssb-web