repo_description: A decentralized messaging and sharing app built on top of Secure Scuttlebutt (SSB).
repo_topics: decentralized decentralized-messaging electron javascript linux macos nodejs off-grid offline p2p scuttlebutt server social social-network ssb windows
code: const pull = require('pull-stream')
  const pullCat = require('pull-cat')
  
  module.exports = function (ssb) {
    return {
      linear: function ({ lt, gt, reverse, limit, query, old, live }) {
        // handle markers passed in to lt / gt
        const opts = { reverse, old, live, private: true }
        if (lt && typeof lt.timestamp === 'number') lt = lt.timestamp
        if (gt && typeof gt.timestamp === 'number') gt = gt.timestamp
        if (typeof lt === 'number') opts.lt = lt
        if (typeof gt === 'number') opts.gt = gt
  
        const matchesQuery = searchFilter(query)
        const marker = { marker: true, timestamp: null }
  
        const stream = pull(
          ssb.createLogStream(opts),
          pull.through(msg => {
            marker.timestamp = msg.timestamp
          }),
          pull.filter(matchesQuery)
        )
  
        // TRUNCATE
        if (typeof limit === 'number') {
          let count = 0
          return pullCat([
            pull(
              stream,
              pull.take(limit),
              pull.through(() => {
                count += 1
              })
            ),
  
            // send truncated marker for resuming search
            pull(
              pull.values([marker]),
              pull.filter(() => count === limit)
            )
          ])
        } else {
          return stream
        }
      },
      privateLinear: function ({ reverse, query, old, live, author }) {
        // handle markers passed in to lt / gt
        const opts = { reverse, old, live, private: true }
        const filter = {
          timestamp: { $gt: 0 }
        }
  
        if (author) {
          filter.value = {
            author
          }
        }
  
        opts.query = [
          { $filter: filter }
        ]
  
        const matchesQuery = searchFilter(query)
  
        return pull(
          ssb.private.read(opts),
          pull.filter(matchesQuery)
        )
      }
    }
  }
  
  function searchFilter (terms) {
    return function (msg) {
      if (msg.sync) return true
      const c = msg && msg.value && msg.value.content
      return c && (
        msg.key === terms[0] || andSearch(terms.map(function (term) {
          return new RegExp('\\b' + term + '\\b', 'i')
        }), [c.text, c.name, c.title])
      )
    }
  }
  
  function andSearch (terms, inputs) {
    for (let i = 0; i < terms.length; i++) {
      let match = false
      for (let j = 0; j < inputs.length; j++) {
        if (terms[i].test(inputs[j])) match = true
      }
      // if a term was not matched by anything, filter this one
      if (!match) return false
    }
    return true
  }
  