The Gist:

A juurni of a thousand miles begins with one step. Juurni is an SPA project where I explore indexedDB. It is a, not yet full-featured, journaling app inspired by Dyrii on iOS. I don’t plan to go anywhere with this application, it is just my exploration into indexedDB, its features and API. At the moment, you will need to use the developer console in order to see your journal entries. Maybe later, I will pull the entries onto the UI.

Technically speaking

According to Mozilla, “IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. While Web Storage is useful for storing smaller amounts of data, it is less useful for storing larger amounts of structured data. IndexedDB provides a solution.”

Moreover, “IndexedDB is a transactional database system, like an SQL-based RDBMS. However, unlike SQL-based RDBMSes, which use fixed-column tables, IndexedDB is a JavaScript-based object-oriented database. IndexedDB lets you store and retrieve objects that are indexed with a key; any objects supported by the structured clone algorithm can be stored. You need to specify the database schema, open a connection to your database, and then retrieve and update data within a series of transactions.”

I found this definition intriguing because indexedDB has the potential to push a lot of power forward in a web application. It has the potential to surface some aspects of business logic, to the client-side browser, by way of the datastore and the javascript code used to interact with it. I was able to envision use cases, where a visitor to a site can be entertained as they wait for service, say at a restaurant, a car service dealer, or even at a doctors appointment; and technically speaking, not even need an internet connection. I’m not sure how popular this technology is today, but the applications of indexedDB are intriguing to say the least.

A Journey of a Thousand Miles

As well as it fits, that line came to me as I sat down to write this post, which is months after beginning the project. So why another diary app? I developed Juurni on a whim of frustration with every other journaling app wanting to sync your entries to a cloud repository. I understand…in order to access anytime and anywhere, the entries would have to be stored in a central repository that all instances of the app can access. Journal entries are private in my opinion, private to a degree above technical reason. This belief is the why for this project. Disclaimer, I have not, nor am I committing to delivering a completed product here, this is more or less a lab experiment for me that I may not complete due to other priorities.

Juurni…at a glance

Currently, juurni is an SPA that consists for four controls. A calendar created by YSCoder, a textbox, a text area, and a button. While I hope to enrich the user interface a bit more, I have a simultaneous urge to keep it as simple as possible. The first image below is of the interface, with the second being the developer’s console showing the records stored in the indexedDB.

juurni-ui

The Juurni web interface.

juurni-console

The console showing the indexedDB entries.

The Code

Here is the code behind Juurni, a simple implementation of the indexedDB API.

On page load, I want to initialize the database.


	//global variable to hold a reference to the database.
  	var postDate = new Date();
	var calendarData = {};
	var dailyEntries = [];
	let db = null;

	//this process runs on page launch. the api verifies the database and version. If it doesn't exist,
	//it is created.
	function initiateIndexDB() {
	    const dbName = "Juurnii";
             const dbVersion = "1.0";
	    const request = indexedDB.open(dbName,dbVersion);

	    //databases and datastores (tables) are created in this callback.
	    request.onupgradeneeded = e => {
                  db = e.target.result;
	         //create the data store and define the key field.
	         db.createObjectStore("journal_entries",{keyPath:"logdate"});
	         console.log(`upgrade is called on database name: ${db.name} version: ${db.version}`); };
	         //the datastore can be read/viewed at during this callback.
	         request.onsuccess = e => { db = e.target.result;
	         console.log(`success is called on database name: ${db.name} version: ${db.version}`); };
	         //there has been an error accessing the database or the datastore.
	         request.onerror = e => { console.log('error');
              };
          }

  Then we have the code to save an entry.

  // add a record to the datastore using a indexdb transaction.

     function saveEntry( ) {

          //entry template

          /* journalEntry = {

             logdate: "getMonth()+getDate()+getFullYear()+getHours()+getMinutes()" 070519731213 (July 5, 1973 12:13 pm) non-utc

             title:"Title",

             text: "What happened today?",

             date: getDate()

         }*/

         //read from ui elements.

          let entryTitle = titleElement.value;

          let entryText = textElement.value;

          // build the long date from the selected post date.

          let dateString = postDate.toDateString();

          let timeString = postDate.toTimeString();

          let fullDateString = dateString + " " + timeString;

          //build the timestamp from date parts

          let mm = (postDate.getMonth() + 1).toString();

          let dd = postDate.getDate().toString();

          let yy = postDate.getFullYear().toString();

          // the calendar provides the date, the time is calculated based on the current time.

          let now = new Date();

          let hh = now.getHours().toString();

          let min = now.getMinutes().toString();

          // append time to the postDate variable

          postDate.setHours(now.getHours());

          postDate.setMinutes(now.getMinutes());

          let keyField = mm + dd + yy + hh + min ;

          //if no title is provided then name it untitled.

         if(entryTitle.length === 0)

          entryTitle = "Untitled";         //if the entry text is blank, just return don't save a blank record.

          if(entryText.length === 0 || entryText === ""){

           console.log("warning: entries with no text are not saved.");

           return;

    }

    //create an entry object

    const entry = {

           logdate: keyField,            title: entryTitle,

           date: fullDateString,

           text: entryText

    };

    //connect to the datastore that was created in the onupgradeneeded callback.

    var tx = db.transaction("journal_entries", "readwrite");


    //trap and respond to errors.

    tx.onerror = e => console.log(`Error! ${e.target.error}`);


    //read the datastore into memory.

    var jEntries = tx.objectStore("journal_entries");

    //add the record to the datastore. display a toast message and reset the form.

    jEntries .add(entry );

    $('.toast').toast('show');

    resetForm();

}

//reset form

function resetForm(){

    titleElement.value = "";

    textElement.value = "";

    titleElement.focus();

}

Finally we have the code to activate the calendar control.

// set up the calendar

$(document).ready(function () {

let data = calendarData;

let now = new Date();

let year = now.getFullYear();

let month = now.getMonth();

let day = now.getDate();

// inline calendar call with overrides

let $ca = $('#calendrier').calendar({

     view: 'date',

     data: data,

     monthArray: ['jan', 'fev', 'mar', 'avr', 'mai', 'jui', 'juil', 'aou', 'sep', 'oct', 'nov', 'dec'],

     weekArray:['dim','lun','mar','mer','jeu','ven','sam'],

     date: new Date(year,month,day),

     onSelected: function (view, date, data) {

          console.log('view:' + view);

          console.log('date:' + date);

          console.log('data:' + (data || ''));

          //update the post date on calendar item selection.

          postDate = new Date(date);


          //re-focus after selecting a date.

      titleElement.focus();

        },

     viewChange: function (view, y, m) {

          console.log(view, y, m);

        }

    });

    getEntries();

   titleElement.focus();

});

Comments


Comments are closed