2013-12-22
Playing with Fire

Playing with Firebase and AngularFire. I’ve modified the example from [http://angularfire.com/tutorial/index.html#gettingstarted]().

Simple Chat

I modified the above example slightly so that if you use the #clear command as your message text, then all messages will be erased. This IS just a toy after all.

{[msg.from]}: {[msg.body]}


The code for the above is:

<div>
  <script>
  function MyController($scope, $firebase) {
    var ref = new Firebase('https://doctorbud.firebaseIO.com/simplechat1');
    $scope.messages = $firebase(ref);
    $scope.addMessage = function(e) {
      if (e.keyCode != 13) return;
      if ($scope.msg === "#clear")
      {
        $scope.messages.$remove();
      }
      else
      {
        $scope.messages.$add({from: $scope.name, body: $scope.msg});
      $scope.msg = "";
      }
    };
  }
  window.MyController = MyController;
  </script>
  <div ng-controller="MyController" style="padding:20px;max-width:500px;">
    <div scroll-glue class="overflowable" id="messagesDiv" style="color:#00AA00;background-color:#000000;">
      <div ng-repeat="msg in messages">
        <em>{{msg.from}}</em>: {{msg.body}}
      </div>
    </div>
    <br/>
    <input type="text" ng-model="name" placeholder="Name" size="10">
    <input type="text" ng-model="msg" ng-keydown="addMessage($event)" placeholder="Message..." size="50">
  </div>
</div>

Christmas Presence

We’ve established that we can implement liveness in a web page with the above demos, but an important aspect of collaboration is the idea of presence. In other words, is the person I’m remotely dialoguing with still paying attention or online?

Let’s add an Online Users display, and then evolve it towards a reflection of how Active the users are. I want it to be immediately clear that a user is online or offline. What is unclear until I get it working is whether each Firebase client in a single page counts as one or whether there is one client for the whole page, no matter how many Firebase refs there are.

While we’re building a Users display, let’s see how much useful info we can capture.

{[totalViewers]} viewers are viewing

  • Geo: {[viewer.city]}, {[viewer.region]}, {[viewer.country]} -- {[viewer.loc]}
    Net: {[viewer.ip]} {[viewer.hostname]} {[viewer.org]}

The code for the above is:

<script>
angular.module('BlogApp')
  .controller('MainCtrl', ['$scope', 'PresenceService',
    function($scope, PresenceService) {
      $scope.totalViewers = 0;
      $scope.allViewers = [];

      $scope.$on('onOnlineUser', function() {
        $scope.$apply(function() {
          $scope.totalViewers = PresenceService.getOnlineUserCount();
          $scope.allViewers = PresenceService.getOnlineUserList();
        });
      });
    }
  ])
  .factory('PresenceService', ['$rootScope', '$http',
    function($rootScope, $http) {
      var onlineUsers = [];
      var numOnlineUsers = 0;
      var myUserInfo = {};

      // Create our references
      var listRef = new Firebase('https://doctorbud.firebaseIO.com/simplepresence1');
      var userRef = listRef.push(); // This creates a unique reference for each user
      var presenceRef = new Firebase('https://doctorbud.firebaseIO.com/.info/connected');

      var jsonp = $http.jsonp('https://ipinfo.io/');

      jsonp.then(
        function(response) {
          console.log('response: ', response);
          myUserInfo.city = response.city;
          myUserInfo.country = response.country;
          myUserInfo.hostname = response.hostname;
          myUserInfo.ip = response.ip;
          myUserInfo.loc = response.loc;
          myUserInfo.org = response.org;
          myUserInfo.region = response.region;
        });

      // Add ourselves to presence list when online.
      presenceRef.on('value', function(snap) {
        if (snap.val()) {
          userRef.set(myUserInfo);
          // Remove ourselves when we disconnect.
          userRef.onDisconnect().remove();
        }
      });

      // Get the user count and notify the application
      listRef.on('value', function(snap) {
        numOnlineUsers = snap.numChildren();
        onlineUsers = [];
        snap.forEach(function(childSnapshot) {
          // This code will be called twice.
          var name = childSnapshot.name();
          var childData = childSnapshot.val();
          onlineUsers.push(childData);
        });

        $rootScope.$broadcast('onOnlineUser');
      });

      var getOnlineUserCount = function() {
        return numOnlineUsers;
      }

      var getOnlineUserList = function() {
        return onlineUsers;
      }

      return {
        getOnlineUserCount: getOnlineUserCount,
        getOnlineUserList: getOnlineUserList
      }
    }
  ]);
</script>

<div ng-controller="MainCtrl" style="padding:20px;max-width:600px;">
  <h1>{{totalViewers}} viewers are viewing</h1>

  <div scroll-glue class="overflowable" style="color:#1111AA;background-color:#DDDDDD;">
    <ul>
      <li ng-repeat='viewer in allViewers'>
        Geo: {{viewer.city}}, {{viewer.region}}, {{viewer.country}} -- {{viewer.loc}}
        <br/>
        Net: {{viewer.ip}} {{viewer.hostname}} {{viewer.org}}
      </li>
    </ul>
  </div>
</div>

A ChatBot Example

Continuing with the Chat Window example above, let’s use a <mlmmachine> to talk to Firebase, and see that it shows up in the Chat Window at the top of the page.

Machine Time: {[ MA.getMachineTime() ]}
Machine State: {[ MA.getMachineState() ]}
Machine Running: {[ MA.isRunning() ]}
Message Number: {[ messageNumber ]}

The code for the above is:

<script>
function ControllerB($scope, $firebase) {
  var ref = new Firebase('https://doctorbud.firebaseIO.com/simplechat1');
  $scope.messages = $firebase(ref);
  $scope.addBotMessage = function(msg) {
    $scope.messages.$add({from: "ChatBot", body: msg});
  };
}
</script>

<div style="background-color: aliceblue;" ng-controller="ControllerB">

<h6>Machine Time: {{ MA.getMachineTime() }}</h6>
<h6>Machine State: {{ MA.getMachineState() }}</h6>
<h6>Machine Running: {{ MA.isRunning() }}</h6>

<h6>Message Number: {{ messageNumber }}</h6>

<mlmmachine
  ptr="MA"
  ng-init="messageNumber=0;"
  reset-fn="messageNumber=1;"
  step-fn=
    "messageNumber = messageNumber + 1;addBotMessage('message#' + messageNumber);"
  class="mlm background">

<mlmpanel></mlmpanel>

</mlmmachine>
</div>