The Controller

// for more info: https://docs.redsift.com/docs/client-code-siftcontroller
loadView(state) {
  console.log('counter: loadView', state);
  // ...
  switch (state.type) {
    // ...
    case 'summary':
      return {
        html: 'summary.html',
        data: {} //this.getX()
      };
    default:
      console.error('counter: unknown Sift type: ', state.type);
  }
}
// ...

The Controller has access to the stored data and we provide convenience methods to access this data. You’ll never need to query the client data-store directly. The loadView() method of the controller is called when the Sift is ready to be loaded. You’ll need to return two important things from the loadView() method:

  1. The path to the html file you would like the Sift to display to the user (as mentioned above relative to the public directory)

  2. The data to be passed on to the View when it is displayed.
    The Sift then prepares your View and calls presentView() when ready. The data that you returned from loadView() is now available inside your View and you can now display it where appropriate.

📘

Frontend Data Flow Summary

Controller(.js) ⇒ loadView() ⇒ *.html ⇒ View(.js) ⇒ presentView()

Data subscription and updates

The frontend receives updates when changes happen in the exported buckets. The Controller can subscribe for events from Storage on an array of buckets (here we’re interested in count ) with the help of the internal storage object like this:

this.storage.subscribe(['count', 'emails'], );

['count', 'emails'].forEach(store => this.storage.subscribe([store], this._suHandler))

The last argument is a callback function that will be called once an event is received. The implementation in controller.js with the relevant sections uncommented and updated will look like this:

// ...
constructor() {
  // You have to call the super() method to initialize the base class.
  super();
  // commented out and replaced with callback auto-bind on line 30:
  // this._suHandler = this.onStorageUpdate.bind(this);
}

loadView(state) {
  console.log('counter: loadView', state);
  
  ['count', 'emails'].forEach(store => this.storage.subscribe([store], this.onStorageUpdate))
  switch (state.type) {
    case 'email-thread':
      return {
        html: 'email-thread.html',
        data: {}
      };
    case 'summary':
      return {
        html: 'summary.html',
        data: this.getData()
      };
    default:
      console.error('counter: unknown Sift type: ', state.type);
    }
  }

  // Event: storage update (arrow function auto-binds `this`)
  onStorageUpdate = (value) => {
    console.log('counter: onStorageUpdate: ', value);
    return this.getData().then(xe => {
      // Publish events from 'x' to view
      this.publish('dataUpdate', xe);
    });
  }

  getData() {
    return Promise.all([
    	this.storage.get({
        bucket: 'count',
        keys: ['word_count']
      }).then((values) => {
        console.log('counter: getData - counts - returned:', values);
        return {
          messageTotal: values[0].value || 0,
          wordTotal: values[1].value || 0,
          wpmTotal: ((values[1].value || 0) / (values[0].value || 1)).toFixed(2)
        };
      }),
      this.storage.getAll({
        bucket: 'emails'
      }).then((values) => {
        console.log('counter: getData - emails - returned:', values);
        return values.map(({ value }) => JSON.parse(value));
      }),
    ]).then(([counts, emails]) => ({
    	counts,
      emails
    }));
  }

}
// ...

So here we have a few new things going for us. The subscription to events from storage that we mentioned earlier happens in line 10 and the callback we are using is declared in line 5 and provides a reference to the onStorageUpdate() method in line 29. That’s all needed but it’s just glue code, here are the important bits:

  • Events from storage are only on a bucket level, that’s why we need the getData() method to fetch them for us which is basically wrapping a call to Storage. We can see it’s used in two places (lines 20 and 30). The first populates the view when in loads, the second fetches the new data after an update.
  • The publish() method in line 32 which publishes the results from the call getData() to the View. Well that’s the intention, since at the moment nobody has subscribed for "dataUpdate" events.