git ssb

10+

Matt McKegg / patchwork



Tree: 77570f8a228dbe5f1d398a92623d4c25675654ec

Files: 77570f8a228dbe5f1d398a92623d4c25675654ec / modules / page / html / render / search.js

4256 bytesRaw
1var { h, Value, when, computed } = require('mutant')
2var pull = require('pull-stream')
3var TextNodeSearcher = require('text-node-searcher')
4var whitespace = /\s+/
5var pullAbortable = require('pull-abortable')
6var Scroller = require('../../../../lib/scroller')
7var nest = require('depnest')
8var Proxy = require('mutant/proxy')
9var ref = require('ssb-ref')
10
11exports.needs = nest({
12 'sbot.pull.stream': 'first',
13 'keys.sync.id': 'first',
14 'message.html.render': 'first',
15 'intl.sync.i18n': 'first',
16 'sbot.pull.backlinks': 'first'
17})
18
19exports.gives = nest('page.html.render')
20
21exports.create = function (api) {
22 const i18n = api.intl.sync.i18n
23 return nest('page.html.render', function channel (path) {
24 if (path[0] !== '?') return
25
26 var query = path.substr(1).trim()
27 var done = Value(false)
28 var loading = Proxy(true)
29 var count = Value(0)
30 var updates = Value(0)
31 var aborter = null
32
33 const searchHeader = h('div', {className: 'PageHeading'}, [
34 h('h1', [h('strong', i18n('Search Results:')), ' ', query])
35 ])
36
37 var updateLoader = h('a Notifier -loader', { href: '#', 'ev-click': refresh }, [
38 'Show ', h('strong', [updates]), ' ', plural(updates, i18n('update'), i18n('updates'))
39 ])
40
41 var content = Proxy()
42 var container = h('Scroller', {
43 style: { overflow: 'auto' }
44 }, [
45 h('div.wrapper', [
46 h('SearchPage', [
47 searchHeader,
48 content,
49 when(loading, h('Loading -search'), h('div', {
50 style: {
51 'padding': '60px 0',
52 'font-size': '150%'
53 }
54 }, [h('strong', i18n('Search completed.')), ' ', count, ' ', plural(count, i18n('result found'), i18n('results found'))]))
55 ])
56 ])
57 ])
58
59 var realtimeAborter = pullAbortable()
60
61 pull(
62 getStream(query, true),
63 realtimeAborter,
64 pull.drain(msg => {
65 updates.set(updates() + 1)
66 })
67 )
68
69 refresh()
70
71 return h('SplitView', {
72 hooks: [
73 RemoveHook(() => {
74 // terminate search if removed from dom
75 // this is triggered whenever a new search is started
76 realtimeAborter.abort()
77 aborter && aborter.abort()
78 })
79 ],
80 uniqueKey: 'search'
81 }, [
82 h('div.main', [
83 when(updates, updateLoader),
84 container
85 ])
86 ])
87
88 // scoped
89
90 function refresh () {
91 if (aborter) {
92 aborter.abort()
93 }
94
95 aborter = pullAbortable()
96
97 updates.set(0)
98 content.set(h('section.content'))
99
100 var scroller = Scroller(container, content(), renderMsg, err => {
101 if (err) console.log(err)
102 done.set(true)
103 })
104
105 pull(
106 getStream(query, false),
107 pull.through(() => count.set(count() + 1)),
108 aborter,
109 pull.filter(msg => msg.value),
110 scroller
111 )
112
113 loading.set(computed([done, scroller.queue], (done, queue) => {
114 return !done
115 }))
116 }
117
118 function renderMsg (msg) {
119 var el = h('FeedEvent', api.message.html.render(msg))
120 highlight(el, createOrRegExp(query.split(whitespace)))
121 return el
122 }
123 })
124
125 function getStream (query, realtime = false) {
126 if (ref.isLink(query) || query.startsWith('#')) {
127 return api.sbot.pull.backlinks({
128 query: [ {$filter: { dest: query }} ],
129 reverse: true,
130 old: !realtime,
131 index: 'DTA' // use asserted timestamps
132 })
133 } else {
134 if (realtime) {
135 return api.sbot.pull.stream(sbot => sbot.patchwork.linearSearch({old: false, query: query.split(whitespace)}))
136 } else {
137 return api.sbot.pull.stream(sbot => sbot.search.query({query}))
138 }
139 }
140 }
141}
142
143function createOrRegExp (ary) {
144 return new RegExp(ary.map(function (e) {
145 return '\\b' + e + '\\b'
146 }).join('|'), 'i')
147}
148
149function highlight (el, query) {
150 if (el) {
151 var searcher = new TextNodeSearcher({container: el})
152 searcher.query = query
153 searcher.highlight()
154 return el
155 }
156}
157
158function RemoveHook (fn) {
159 return function (element) {
160 return fn
161 }
162}
163
164function plural (value, single, many) {
165 return computed(value, (value) => {
166 if (value === 1) {
167 return single
168 } else {
169 return many
170 }
171 })
172}
173

Built with git-ssb-web