git ssb

16+

Dominic / patchbay



Tree: 9510d7867738dfa7a4aaee17c183c86bb081f55e

Files: 9510d7867738dfa7a4aaee17c183c86bb081f55e / app / page / search.js

4310 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.page.search')
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.page.search', searchPage)
81
82 function searchPage (path) {
83 var queryStr = path.replace(/^\??/, '').trim()
84 var query = queryStr.split(whitespace)
85 var matchesQuery = searchFilter(query)
86
87 const search = Struct({
88 isLinear: Value(false),
89 linear: Struct({
90 checked: Value(0)
91 }),
92 fulltext: Struct({
93 isDone: Value(false)
94 }),
95 matches: Value(0)
96 })
97 const hasNoFulltextMatches = computed([search.fulltext.isDone, search.matches],
98 (done, matches) => done && matches === 0)
99
100 const searchHeader = h('Search', [
101 h('header', h('h1', query.join(' '))),
102 when(search.isLinear,
103 h('section.details', [
104 h('div.searched', ['Searched: ', search.linear.checked]),
105 h('div.matches', [search.matches, ' matches'])
106 ]),
107 h('section.details', [
108 h('div.searched'),
109 when(hasNoFulltextMatches, h('div.matches', 'No matches'))
110 ])
111 )
112 ])
113 const { filterMenu, filterDownThrough, filterUpThrough, resetFeed } = api.app.html.filter(draw)
114 const { container, content } = api.app.html.scroller({ prepend: [searchHeader, filterMenu] })
115 container.id = path // helps tabs find this tab
116
117 function renderMsg (msg) {
118 var el = api.message.html.render(msg)
119 highlight(el, createOrRegExp(query))
120 return el
121 }
122
123 function draw () {
124 resetFeed({ container, content })
125
126 pull(
127 api.sbot.pull.log({old: false}),
128 pull.filter(matchesQuery),
129 filterUpThrough(),
130 Scroller(container, content, renderMsg, true, false)
131 )
132
133 pull(
134 api.sbot.pull.stream(sbot => next(sbot.fulltext.search, {query: queryStr, reverse: true, limit: 500, live: false})),
135 fallback((err) => {
136 if (err === true) {
137 search.fulltext.isDone.set(true)
138 } else if (/^no source/.test(err.message)) {
139 search.isLinear.set(true)
140 return pull(
141 next(api.sbot.pull.log, {reverse: true, limit: 500, live: false}),
142 pull.through(() => search.linear.checked.set(search.linear.checked() + 1)),
143 pull.filter(matchesQuery)
144 )
145 }
146 }),
147 filterDownThrough(),
148 pull.through(() => search.matches.set(search.matches() + 1)),
149 Scroller(container, content, renderMsg, false, false)
150 )
151 }
152
153 draw()
154
155 return container
156 }
157}
158
159

Built with git-ssb-web