git ssb

16+

Dominic / patchbay



Tree: 91116b86a5d782a02e668da8c5990803728c90d4

Files: 91116b86a5d782a02e668da8c5990803728c90d4 / app / html / page / search.js

4349 bytesRaw
1const nest = require('depnest')
2const { h, Struct, Value, when, computed } = require('mutant')
3const pull = require('pull-stream')
4const Scroller = require('pull-scroll')
5const TextNodeSearcher = require('text-node-searcher')
6
7const next = require('../../../junk/next-stepper')
8
9exports.gives = nest('app.html.page')
10
11exports.needs = nest({
12 'app.html': {
13 filter: 'first',
14 scroller: 'first'
15 },
16 'message.html.render': 'first',
17 'sbot.pull': {
18 log: 'first',
19 stream: 'first'
20 }
21})
22
23var whitespace = /\s+/
24
25function andSearch (terms, inputs) {
26 for (var i = 0; i < terms.length; i++) {
27 var match = false
28 for (var j = 0; j < inputs.length; j++) {
29 if (terms[i].test(inputs[j])) match = true
30 }
31 // if a term was not matched by anything, filter this one
32 if (!match) return false
33 }
34 return true
35}
36
37function searchFilter (terms) {
38 return function (msg) {
39 var c = msg && msg.value && msg.value.content
40 return c && (
41 msg.key === terms[0] ||
42 andSearch(terms.map(function (term) {
43 return new RegExp('\\b' + term + '\\b', 'i')
44 }), [c.text, c.name, c.title])
45 )
46 }
47}
48
49function createOrRegExp (ary) {
50 return new RegExp(ary.map(function (e) {
51 return '\\b' + e + '\\b'
52 }).join('|'), 'i')
53}
54
55function highlight (el, query) {
56 var searcher = new TextNodeSearcher({container: el})
57 searcher.query = query
58 searcher.highlight()
59 return el
60}
61
62function fallback (createReader) {
63 var fallbackRead
64 return function (read) {
65 return function (abort, cb) {
66 read(abort, function next (end, data) {
67 if (end && createReader && (fallbackRead = createReader(end))) {
68 createReader = null
69 read = fallbackRead
70 read(abort, next)
71 } else {
72 cb(end, data)
73 }
74 })
75 }
76 }
77}
78
79exports.create = function (api) {
80 return nest('app.html.page', searchPage)
81
82 function searchPage (path) {
83 if (path.match(/^[@#%\/]/)) return
84
85 var queryStr = path.replace(/^\??/, '').trim()
86 var query = queryStr.split(whitespace)
87 var matchesQuery = searchFilter(query)
88
89 const search = Struct({
90 isLinear: Value(false),
91 linear: Struct({
92 checked: Value(0)
93 }),
94 fulltext: Struct({
95 isDone: Value(false)
96 }),
97 matches: Value(0)
98 })
99 const hasNoFulltextMatches = computed([search.fulltext.isDone, search.matches],
100 (done, matches) => done && matches === 0)
101
102 const searchHeader = h('Search', [
103 h('header', h('h1', query.join(' '))),
104 when(search.isLinear,
105 h('section.details', [
106 h('div.searched', ['Searched: ', search.linear.checked]),
107 h('div.matches', [search.matches, ' matches'])
108 ]),
109 h('section.details', [
110 h('div.searched'),
111 when(hasNoFulltextMatches, h('div.matches', 'No matches'))
112 ])
113 )
114 ])
115 const { filterMenu, filterDownThrough, filterUpThrough, resetFeed } = api.app.html.filter(draw)
116 const { container, content } = api.app.html.scroller({ prepend: [searchHeader, filterMenu] })
117 container.id = path // helps tabs find this tab
118
119 function renderMsg (msg) {
120 var el = api.message.html.render(msg)
121 highlight(el, createOrRegExp(query))
122 return el
123 }
124
125 function draw () {
126 resetFeed({ container, content })
127
128 pull(
129 api.sbot.pull.log({old: false}),
130 pull.filter(matchesQuery),
131 filterUpThrough(),
132 Scroller(container, content, renderMsg, true, false)
133 )
134
135 pull(
136 api.sbot.pull.stream(sbot => next(sbot.fulltext.search, {query: queryStr, reverse: true, limit: 500, live: false})),
137 fallback((err) => {
138 if (err === true) {
139 search.fulltext.isDone.set(true)
140 } else if (/^no source/.test(err.message)) {
141 search.isLinear.set(true)
142 return pull(
143 next(api.sbot.pull.log, {reverse: true, limit: 500, live: false}),
144 pull.through(() => search.linear.checked.set(search.linear.checked() + 1)),
145 pull.filter(matchesQuery)
146 )
147 }
148 }),
149 filterDownThrough(),
150 pull.through(() => search.matches.set(search.matches() + 1)),
151 Scroller(container, content, renderMsg, false, false)
152 )
153 }
154
155 draw()
156
157 return container
158 }
159}
160
161

Built with git-ssb-web