git ssb

10+

Matt McKegg / patchwork



Tree: b7ae6aab41bf1d2ae415e7621d196b09103f4371

Files: b7ae6aab41bf1d2ae415e7621d196b09103f4371 / modules / page / html / render / search.js

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

Built with git-ssb-web