Tuesday, February 11, 2014

Dematerializer - Blogging for developers


Dematerializer (Source)

Inspired by Ghost, Dematerializer was my submission to Static Showdown, a 48hr hackathon with a twist; the app must be static (i.e. no custom back-end). At first it seemed like a huge restriction, however after exploring services like Firebase (a websockets enabled database-as-a-service) I realized that almost anything was possible.

What makes Dematerializer different than other blogging platforms is that posts are written entirely in Markdown (with an auto-scrolling instant preview pane), and that it is completely free and open so that anyone can start blogging instantly. It is similar to the Ghost platform, only far simpler and completely free.

I ended up using AngularJS, Firebase, Gulp, Pure CSS, Google Web Fonts, and a few libraries (lodash.js, marked.js, highlight.js, spin.js). The code is pretty standard AngularJS, and for the most part follows best practices. This was my first time using Gulp (instead of Grunt), and I highly recommend it. When I wrote about re-writing Zoggle in AngularJS, I posted my Gruntfile which minified my AngularJS source. Looking at the Gulp equivalent, it's clear how powerful Gulp can be:
// Concatenate & Minify JS
gulp.task('scripts', function() {
    gulp.src('./app/js/*.js')
        .pipe(concat('all.js'))
        .pipe(ngmin())
        .pipe(gulp.dest('./app/dist'))
        .pipe(rename('all.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./app/dist'));
});

As for the front-end code itself, the Firebase service I wrote for the application provides the core functionality. This code allows users to create, read, update, and delete posts that they own:
.service('DBService', function () {
    this.db = new Firebase('https://glaring-fire-7868.firebaseio.com/')
    this.postDB = this.db.child('posts')
    this.createPost = function (post, uid, cb) {
      this.postDB.child(uid).child(post.id).set(post, cb)
    }
    this.findPost = function (postId, uid, cb) {
      this.postDB.child(uid).child(postId).once('value', cb)
    }
    this.posts = function (uid, cb) {
      if (!cb) {
        cb = uid
        uid = null
      }
      if (uid) {
        this.postDB.child(uid).once('value', cb)
      } else {
        this.postDB.once('value', cb)
      }
    }
    this.removePost = function (postId, uid, cb) {     
        this.postDB.child(uid).child(postId).remove(cb)
    }
  })

Firebase uses a simple json object to define data-access for users. Here was mine:
{
  "rules": {
    ".read": true,
    "posts": {
      "$user": {
        ".read": true,
        ".write": "$user == auth.username",
        "$post": {
          ".read": true,
          ".write": "data.child('$user').val() == auth.username"
        }
      }
    }
  }
}

Finally, I used CSS Flexible boxes, which are an extremely powerful way to have flexible sized content using only CSS. You can see them in action by re-sizing the application, which is quite usable on mobile despite not optimizing for it.