git ssb

10+

Matt McKegg / patchwork



Tree: b8dcb5e1ae317132d4e8498b59e5279fcf92490f

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

4212 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 nextStepper = require('../../../../lib/next-stepper')
8var nest = require('depnest')
9var Proxy = require('mutant/proxy')
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})
17
18exports.gives = nest('page.html.render')
19
20exports.create = function (api) {
21 const i18n = api.intl.sync.i18n
22 return nest('page.html.render', function channel (path) {
23 if (path[0] !== '?') return
24
25 var queryStr = path.substr(1).trim()
26 var query = queryStr.split(whitespace)
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.join(' ')])
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 api.sbot.pull.stream(sbot => sbot.patchwork.linearSearch({old: false, query})),
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 api.sbot.pull.stream(sbot => nextStepper(getStream, {
107 reverse: true,
108 limit: 5,
109 query
110 })),
111 pull.through(() => count.set(count() + 1)),
112 aborter,
113 pull.filter(msg => msg.value),
114 scroller
115 )
116
117 loading.set(computed([done, scroller.queue], (done, queue) => {
118 return !done && queue < 5
119 }))
120 }
121
122 function getStream (opts) {
123 if (opts.lt != null && !opts.lt.marker) {
124 // if an lt has been specified that is not a marker, assume stream is finished
125 return pull.empty()
126 } else {
127 return api.sbot.pull.stream(sbot => sbot.patchwork.linearSearch(opts))
128 }
129 }
130
131 function renderMsg (msg) {
132 var el = h('FeedEvent', api.message.html.render(msg))
133 highlight(el, createOrRegExp(query))
134 return el
135 }
136 })
137}
138
139function createOrRegExp (ary) {
140 return new RegExp(ary.map(function (e) {
141 return '\\b' + e + '\\b'
142 }).join('|'), 'i')
143}
144
145function highlight (el, query) {
146 if (el) {
147 var searcher = new TextNodeSearcher({container: el})
148 searcher.query = query
149 searcher.highlight()
150 return el
151 }
152}
153
154function RemoveHook (fn) {
155 return function (element) {
156 return fn
157 }
158}
159
160function plural (value, single, many) {
161 return computed(value, (value) => {
162 if (value === 1) {
163 return single
164 } else {
165 return many
166 }
167 })
168}
169

Built with git-ssb-web