Retrieving from database

Note: we are going to be adding all these code into lib/slackir_web/channels/random_channel.ex

Everytime when you refresh your page, you lose the message history and that is not ideal if you want to know what has been discussed. We have all these stored messages in our database, and we want to retrieve it when someone logs in. Therefore, we will need to retrieve these messages and display them in the chat dialog.

Perform a callback after join

Go to the join function, and add the line just below if authorized?(payload) do

We are going to send a message contaning our self PID(Process Identifier) as the first parameter. The second parameter is a message to run a function called after_join. This function is identified by an atom type.

  def join("random:lobby", payload, socket) do
    if authorized?(payload) do
      send self(), :after_join #<-- Add this line
      {:ok, socket}
    else
      {:error, %{reason: "unauthorized"}}
    end
  end

What does send do?

send sends a message, :after_join, with it's PID generated by the function ,self(). We want to be able to run the after join task asynchronously. Lets look at how we should receive the :after_join message

Receiving a message in a callback

We now know that a message identified by the atom :after_join is being sent. Phoenix provides us with a function called handle_info to be able to receive that message and do something with it. Insert the following code after the join function

  def handle_info(:after_join, socket) do
    messages =
      Slackir.Conversations.list_messages()
      |> Enum.map(fn(m) -> %{message: m.message, name: m.name} end)

    push socket, "messages_history", %{messages: messages}
    {:noreply, socket}
  end

Retrieving all messages from our database

Phew, that looked like a lot is going on in the handle_info function. Lets breakdown the code to understand what is going on.

As we have seen previously, context Slackir.Conversations provides us with many useful functions to communicate with the database. Thanks to list_messages() function we can retrieve all the messages from our database.

We then, pipe the retrieved messages into a map() function provided by the Enum module. map applies this anonymous function &(%{message: &1.message, name: &1.name}) onto every element in messages

Note: Please ask your mentor to explain how Enum.map works if you are unsure about it :)

There is of course the & operator that is used as a shorthand to convert the expression into a function. We are trying to format the messages that we have retrieved into a proper structure.

Passing the messages back to the browser

Finally, we want to push our list of messages to the socket via the push function.

We are using push instead of broadcast, because we only want to display it to the current user that has just joined the channel

Render the messages in the browser

Add the below code to the assets/js/socket.js file

    channel.on('messages_history', messages => {
      let messages_list = messages["messages"];

      messages_list.forEach( function(msg) {
        list.append(`<b>${msg["name"] || 'Anonymous'}:</b> ${msg["message"]}<br>`);
        list.prop({scrollTop: list.prop("scrollHeight")});
      });
    });

This is the final step, really. We need display the messages in our browser by iterating through our list You should have the history of messages displayed to you when you join the channel. Try opening multiple tabs connecting to http://127.0.0.1/4000

You will be able to see that all the messages gets rendered everytime you join