Outline

Now that we have working channels with messaging, adding direct messages will be easier since we can reuse a lot of the existing functionality we have.

In our Messages service, add a Firebase reference to the userMessages node.
    var userMessagesRef = firebase.database().ref('userMessages')

{x: add messages forusers} Add a forUsers function to our Messages service to retrieve direct messages between two users, given their uids.

return {
  forChannel: function(channelId){
    return $firebaseArray(channelMessagesRef.child(channelId));
  },
  forUsers: function(uid1, uid2){
    var path = uid1 < uid2 ? uid1+'/'+uid2 : uid2+'/'+uid1;

    return $firebaseArray(userMessagesRef.child(path));
  }
};

We'll be storing our direct messages in Firebase like so:

{
  "userMessages": {
    "userid1": {
      "userid2": {
        "messageId1": {
          "uid": "userid1",
          "body": "Hello!",
          "timestamp": firebase.database.ServerValue.TIMESTAMP
        },
        "messageId2": {
          "uid": "userid2",
          "body": "Hey!",
          "timestamp": firebase.database.ServerValue.TIMESTAMP
        }
      }
    }
  }
}

Since we always want to reference the same path in our Firebase regardless of which id was passed first, we'll need to sort our ids before referencing the direct messages.

Create a new state channels.direct with the following code:
.state('channels.direct', {
  url: '/{uid}/messages/direct',
  templateUrl: 'channels/messages.html',
  controller: 'MessagesCtrl as messagesCtrl',
  resolve: {
    messages: function($stateParams, Messages, profile){
      return Messages.forUsers($stateParams.uid, profile.$id).$loaded();
    },
    channelName: function($stateParams, Users){
      return Users.all.$loaded().then(function(){
        return '@'+Users.getDisplayName($stateParams.uid);
      });
    }
  }
});

This state is almost identical to channels.messages, using the same templateUrl and controller. We're using a different url, and the messages dependency is using the Messages.forUsers function that we just created. The channelName dependency also looks up the other user's display name, and prefixes it with @.

Inject the Users service into ChannelsCtrl
Set users on channelsCtrl to Users.all
channelsCtrl.users = Users.all;
Update the sidebar to have to list all users and link to their conversations.
<div class="channel create">
  <a ui-sref="channels.create">+ create channel</a>
</div>

<div class="list-head">Direct Messages</div>
<div class="channel" ng-repeat="user in channelsCtrl.users">
  <a ng-if="user.$id !== channelsCtrl.profile.$id" ui-sref="channels.direct({uid: user.$id})" ui-sref-active="selected">
    {{ user.displayName }}
  </a>
</div>

We're now able to chat directly to other users in our application!

Check your work

You can view the completed & working code for this tutorial here:

 

I finished! On to the next chapter