git ssb

16+

Dominic / patchbay



Tree: 0b83856cdd663c07cc28d7f3f0f3a883b41881da

Files: 0b83856cdd663c07cc28d7f3f0f3a883b41881da / app / html / page / search.js

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

Built with git-ssb-web