10.10: Marking Messages as Read

Our last thing to add for our messages is the functionality to mark messages as read.

This would be a good challenge for you to try to implement on your own. It uses most of the skills we learned in the course!

Adding Service Method

First, we'll add a method signature to our IMessageService.cs class:

Task<bool> MarkMessageAsRead(Message message);

Next, we'll implement it in MessageService.cs:

public async Task<bool> MarkMessageAsRead(Message message)
{
    message.IsRead = true;
    message.DateRead = DateTime.Now;

    return await SaveAll();
}

Much more manageable than some of our previous service methods!

Add Controller Method

[HttpPost("{id}/read")]
public async Task<IActionResult> MarkMessageAsRead(int userId, int id)
{
    if (userId != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
        return Unauthorized();

    var message = await _messageService.GetMessage(id);

    if (message.RecipientId != userId)
        return BadRequest("Failed to mark message as read.");

    if (await _messageService.MarkMessageAsRead(message))
        return NoContent();

    throw new Exception("Error deleting the message");
}

Add Service Method in Angular

In user.service.ts - add a new method called markAsRead()

markAsRead(userId: number, messageId: number) {
    return this._http.post(this.baseUrl + 'users/' + userId + '/messages/' + messageId + '/read', {}).subscribe();
}

We can make the method here self-subscribing, which means we don't have to subscribe to it in our component and write a separate method - we can just use it in loadMessages() in our member-messages.component.ts.

First, let's import the do operator from rxjs. This will allow us to 'do' something with the messages array before we subscribe to it. In our case, we want to check if the messages are marked as read.

We'll also bring in underscore again.

import 'rxjs/add/operator/do';
import * as _ from 'underscore';
loadMessages() {
    const currentUserId = +this._authService.decodedToken.nameid;                                   //  1.
    this._userService.getMessageThread(currentUserId, this.userId)                                  //  2.
      .do(messages => {                                                                             //  3.
        _.each(messages, (message: Message) => {                                                    //  4.
          if (message.isRead === false && message.recipientId === currentUserId) {                  //  5.
            this._userService.markAsRead(currentUserId, message.id);                                //  6.
          }
        });
      })
      .subscribe(messages => {
        this.messages = messages;
      }, error => {
        this._alertify.error(error);
      });
}

This is a bit trickier in the JavaScript/TypeScript than what we've done so far. But, think back to how we're manipulating data in some of our service methods on the backend with LINQ and it's very relatable.

  1. Since we need it again, we'll store this in a variable this time to make it reusable.

  2. We're calling our service method as usual - but, changing the first parameter to the variable we just created.

  3. We're calling the rxjs do operator that let's us 'do' something with the data before we subscribe to it.

  4. This is what we're 'doing': we're using the underscore each() function to loop through an array and apply a callback function to each element.

  5. In the callback function, we're checking if the message isn't marked as read and if the user that is loading the messages is the recipient.

  6. If so, we call the markAsRead() function we just wrote.

Test It!

Open up the browser, log in as one user in a message conversation - navigate to the MemberMessage component, view the messages, and then log in as the other user.

See if the IsRead property has updated:

Last updated