Friday, June 20, 2014

Friend Request in JavaScript using Parse.com - Part 3

Now for actions, assuming you added some nice little buttons to those lists.

These are very simplistic, just to show the concept. You would probably want to have some Cloud Code that will verify things in a before-save function. Also all my examples haven't included any error handling but you should add some.

Approve a request

// assuming you saved the FriendRequest.id as maybe an attribute
// data-friend-request-id="xxxx" on the Approve button this code is
// linked to
var friendRequest = new FriendRequest();
friendRequest.id = $(this).data('friendRequestId');
friendRequest.set('status', RequestStatus.approved);
friendRequest.save();

Reject a request

As per Approve, but use
friendRequest.set('status', RequestStatus.rejected);

Un-friend

As per Approve, but use
friendRequest.set('status', RequestStatus.rejected);

Friend Request in JavaScript using Parse.com - Part 2

Continued from Part 1, our next requirement is:

List requests from others waiting for my approve/reject action

// remember our common code, FriendRequest, currentUser
// and RequestStatus are already defined
var pendingRequestQuery = new Parse.Query(FriendRequest);
pendingRequestQuery.equalTo('toUser', currentUser);
pendingRequestQuery.equalTo('status', RequestStatus.requested);

requests.find().then(function (requests) {
  // render them somewhere with Approve/Reject buttons
});
I'll leave it as an exercise for the reader to render the list.

List my friends

var myFriendsQuery1 = new Parse.Query(FriendRequest);
myFriendsQuery1.equalTo('fromUser', currentUser);
myFriendsQuery1.equalTo('status', RequestStatus.approved);

var myFriendsQuery2 = new Parse.Query(FriendRequest);
myFriendsQuery2.equalTo('toUser', currentUser);
myFriendsQuery2.equalTo('status', RequestStatus.approved);

var myFriendsQuery = Parse.Query.or(myFriendsQuery1, myFriendsQuery2);
myFriendsQuery.find().then(function (approvedFriendRequests) {
  // render them
});

List my requests that are pending

var pendingRequestQuery = new Parse.Query(FriendRequest);
pendingRequestQuery.equalTo('fromUser', currentUser);
pendingRequestQuery.equalTo('status', RequestStatus.requested);

pendingRequestQuery.find().then(function (pendingRequests) {
  // render them
});

List my rejections

var rejectedMeQuery = new Parse.Query(FriendRequest);
rejectedMeQuery.equalTo('fromUser', currentUser);
rejectedMeQuery.equalTo('status', RequestStatus.rejected);

rejectedMeQuery.find().then(function (rejectedMeRequests) {
  // render them
});

List requests I have rejected

var rejectedByMeQuery = new Parse.Query(FriendRequest);
rejectedByMeQuery.equalTo('toUser', currentUser);
rejectedByMeQuery.equalTo('status', RequestStatus.rejected);

rejectedByMeQuery.find().then(function (rejectedByMeRequests) {
  // render them
});
In the next part I'll talk about Actions.

Friend Request in JavaScript using Parse.com - Part 1

This is a follow-on from my last post, almost a year ago. Yes, I know, I should post more often, anyway...

First off we need to define some things that are common to all the code blocks:
// fake an enum, allows auto-complete for some editors
var RequestStatus = {
  requested: 'requested', 
  rejected: 'rejected',
  approved: 'approved'
};
// define our Parse Class
var FriendRequest = Parse.Object.extend('FriendRequest');
var currentUser = Parse.User.current();
I will also assume you have jQuery available.

Now lets go through our requirements.

Find a person (highlight existing status)

// for simplicity lets assume we're doing a search for people
// in the same "area" as us, lets also assume you're aware of
// case-sensitive issues and created a "search_Area" property
// forced to lower-case (using a before-save Cloud Function)

var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('search_Area', currentUser.get('search_Area'));

// shared array we'll store details in
var userList = [];
// dictionary we'll use for easier updating
var userDict = [];
userQuery.find().then(function(users) {
  // lets just extract the "id" and "username" first
  for (var i in users) {
    var userInfo = {
      id: users[i].id, 
      name: users[i].get('username')
    });

    userList.push(userInfo);
    userDict[userInfo.id] = userInfo;
  }

  // now we need to attach a status to each one
  var myRequests = new Parse.Query(FriendRequest);
  myRequests.equalTo('fromUser', currentUser);
  myRequests.equalTo('toUser', users);

  var requestsToMe = new Parse.Query(FriendRequest);
  requestsToMe.equalTo('fromUser', users);
  requestsToMe.equalTo('toUser', currentUser);

  var combinedRequests = Parse.Query.or(myRequests, requeststoMe);

  return combinedRequest.find();
}).then(function (requests) {
  for (var i in requests) {
    var request = requests[i];
    var status = request.get('status');

    if (request.get('fromUser').id == currentUser.id) {
      // set status from my perspective
      userDict[request.get('toUser').id].statusMessage = 
        status == RequestStatus.approved ? 'Already my friend' :
        status == RequestStatus.rejected ? 'I rejected them' :
        status == RequestStatus.requested ? 'Waiting for them to reply' :
        'Unknown status';
    } else {
      // set status from the other person's perspective
      userDict[request.get('fromUser').id].statusMessage = 
        status == RequestStatus.approved ? 'Already my friend' :
        status == RequestStatus.rejected ? 'They rejected me' :
        status == RequestStatus.requested ? 'Waiting for me to reply' :
        'Unknown status';
    }
  }

  // at this point you can render the userList
  for (var i in userList) {
    var userInfo = userList[i];
    // assuming you have a container DIV with id="possibleFriends"
    $('#possibleFriends').append(
      '<div data-user-id="' +  userInfo.id + '"'
      + ' class="possibleFriend">'
      + userInfo.userName
      // not all will have a status, so handle that
      + (userInfo.status ? ' - ' + userInfo.status : '')
      + '</div>');
  }
});
With the above you can extend it however you like to allow sorting etc, it is just the bare-bones implementation.