git ssb

7+

dinoworm ๐Ÿ› / patchcore



Tree: 5a28f5df1543b0e5813e5fe7dbc74f2ec907ff5a

Files: 5a28f5df1543b0e5813e5fe7dbc74f2ec907ff5a / feed / obs / thread.js

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

Built with git-ssb-web