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