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 { h, Value, Dict, dictToCollection, map, computed, resolve, watch } = require('mutant')
  
  module.exports = function (renderPage, defaultViews) {
    const views = Dict({})
  
    const forwardHistory = []
    const backHistory = []
  
    // if defaultViews changes, load them!
    watch(defaultViews, (defaultViews) => {
      if (defaultViews) {
        defaultViews.forEach((view) => {
          if (!views.has(view)) {
            views.put(view, renderPage(view))
          }
        })
      }
    })
  
    const lastViewed = {}
  
    // delete cached view after 5 mins of last seeing
    setInterval(() => {
      views.keys().forEach((view) => {
        if (!(resolve(defaultViews) || []).includes(view)) {
          if (lastViewed[view] !== true && Date.now() - lastViewed[view] > (5 * 60e3) && view !== currentView()) {
            views.delete(view)
          }
        }
      })
    }, 60e3)
  
    const canGoForward = Value(false)
    const canGoBack = Value(false)
    const currentView = Value((resolve(defaultViews) || [])[0] || null)
  
    const viewCollection = dictToCollection(views)
    const html = h('div.main', map(viewCollection, (item) => {
      return h('div.view', {
        attributes: {
          'data-href': item.key
        },
        hidden: computed([item.key, currentView], (a, b) => a !== b)
      }, [item.value])
    }))
  
    return {
      get: views.get,
      defaultViews,
      canGoForward,
      canGoBack,
      currentView,
      setView,
      goBack,
      goForward,
      html
    }
  
    // scoped
  
    function goBack () {
      if (backHistory.length) {
        canGoForward.set(true)
        forwardHistory.push(currentView())
  
        const view = backHistory.pop()
        loadView(view)
  
        currentView.set(view)
        canGoBack.set(backHistory.length > 0)
      }
    }
  
    function goForward () {
      if (forwardHistory.length) {
        backHistory.push(currentView())
  
        const view = forwardHistory.pop()
        loadView(view)
  
        currentView.set(view)
        canGoForward.set(forwardHistory.length > 0)
        canGoBack.set(true)
      }
    }
  
    function loadView (view) {
      if (!views.has(view)) {
        const page = renderPage(view)
        if (page) {
          if (page.uniqueKey) {
            views.keys().forEach(k => {
              if (views.get(k).uniqueKey === page.uniqueKey) {
                views.delete(k)
              }
            })
          }
          views.put(view, page)
        }
      }
    }
  
    function setView (view, anchor) {
      loadView(view)
  
      if (views.has(view)) {
        if (lastViewed[view] !== true) {
          lastViewed[view] = Date.now()
        }
  
        if (currentView() && lastViewed[currentView()] !== true) {
          lastViewed[currentView()] = Date.now()
        }
  
        const viewElement = views.get(view)
  
        if (viewElement && typeof viewElement.setAnchor === 'function') {
          viewElement.setAnchor(anchor)
        }
  
        if (view !== currentView()) {
          canGoForward.set(false)
          canGoBack.set(true)
          forwardHistory.length = 0
          backHistory.push(currentView())
          currentView.set(view)
        }
      }
    }
  }
  