git ssb

16+

Dominic / patchbay



Tree: f715d01c1a678fca975815e634302f528078efbf

Files: f715d01c1a678fca975815e634302f528078efbf / modules_extra / search.js

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

Built with git-ssb-web