Giter Site home page Giter Site logo

dwyl / phoenix-chat-example Goto Github PK

View Code? Open in Web Editor NEW
732.0 114.0 91.0 860 KB

πŸ’¬ The Step-by-Step Beginners Tutorial for Building, Testing & Deploying a Chat app in Phoenix 1.7 [Latest] πŸš€

Home Page: https://phoenix-chat.fly.dev

JavaScript 9.45% CSS 0.16% Elixir 77.79% HTML 9.56% Dockerfile 2.77% Shell 0.17% Batchfile 0.10%
phoenix elixir elixir-lang beginner tutorial step-by-step testing deployment heroku phoenix-chat

phoenix-chat-example's Introduction

Phoenix Chat Example

phoenix-chat-logo

GitHub Workflow Status codecov.io contributions welcome HitCount Hex pm

Try it: phoenix-chat.fly.dev

A step-by-step tutorial for building, testing and deploying a Chat app in Phoenix!

Why?

Chat apps are the "Hello World" of real time examples.

Sadly, most example apps show a few basics and then ignore the rest ... πŸ€·β€β™€οΈ
So beginners are often left lost or confused as to what they should do or learn next!
Very few tutorials consider Testing, Deployment, Documentation or other "Enhancements" which are all part of the "Real World" of building and running apps; so those are topics we will cover to "fill in the gaps".

We wrote this tutorial to be easiest way to learn Phoenix, Ecto and Channels with a practical example anyone can follow.

This is the example/tutorial we wished we had when we were learning Elixir, Phoenix ... If you find it useful, please ⭐ πŸ™ Thanks!

What?

A simple step-by-step tutorial showing you how to:

  • Create a Phoenix App from scratch (using the mix phx.new chat "generator" command)
  • Add a "Channel" so your app can communicate over WebSockets.
  • Implement a basic front-end in plain JavaScript (ES5 without any libraries) to interact with Phoenix (send/receive messages via WebSockets)
  • Add a simple "Ecto" schema to define the Database Table (to store messages)
  • Write the functions ("CRUD") to save message/sender data to a database table.
  • Test that everything is working as expected.
  • Deploy to Fly.io so you can show people your creation!

Initially, we deliberately skip over configuration files and "Phoenix Internals" because you (beginners) don't need to know about them to get started. But don't worry, we will return to them when needed. We favour "just-in-time" (when you need it) learning as it's immediately obvious and practical why we are learning something.

Who?

This example is for complete beginners as a "My First Phoenix" App.

We try to assume as little as possible, but if you think we "skipped a step" or you feel "stuck" for any reason, or have any questions (related to this example), please open an issue on GitHub!
Both the @dwyl and Phoenix communities are super beginner-friendly, so don't be afraid/shy.
Also, by asking questions, you are helping everyone that is or might be stuck with the same thing!

How?

These instructions show you how to create the Chat app from scratch.

0. Pre-requisites (Before you Start)

  1. Elixir Installed on your local machine.
    see: dwyl/learn-elixir#installation
    e.g:
brew install elixir

Note: if you already have Elixir installed on your Mac, and just want to upgrade to the latest version, run: brew upgrade elixir

  1. Phoenix framework installed. see: hexdocs.pm/phoenix/installation.html
    e.g:
mix archive.install hex phx_new
  1. PostgreSQL (Database Server) installed (to save chat messages)
    see: dwyl/learn-postgresql#installation
  1. Basic Elixir Syntax knowledge will help,
    please see: dwyl/learn-elixir

  2. Basic JavaScript knowledge is advantageous (but not essential as the "front-end" code is quite basic and well-commented). see: dwyl/Javascript-the-Good-Parts-notes

Check You Have Everything Before Starting

Check you have the latest version of Elixir (run the following command in your terminal):

elixir -v

You should see something like:

Erlang/OTP 25 [erts-13.1.1] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]

Elixir 1.14.1 (compiled with Erlang/OTP 25)

Check you have the latest version of Phoenix:

mix phx.new -v

You should see:

Phoenix installer v1.7.0-rc.2

Note: if your Phoenix version is newer, Please feel free to update this doc! πŸ“ We try our best to keep it updated ... but your contributions are always welcome!

In this tutorial, we are using Phoenix 1.7-rc2, the second release candidate for Phoenix 1.7. At the time of writing, if you install Phoenix, the latest stable version is not v1.7. To use this version, follow the official guide (don't worry, it's just running one command!) -> https://www.phoenixframework.org/blog/phoenix-1.7-released

However, if you are reading this after its release, v1.7 will be installed for you, and you should see Phoenix installer v1.7.0 in your terminal.

Confirm PostgreSQL is running (so the App can store chat messages) run the following command:

lsof -i :5432

You should see output similar to the following:

COMMAND  PID  USER   FD  TYPE DEVICE                  SIZE/OFF NODE NAME
postgres 529 Nelson  5u  IPv6 0xbc5d729e529f062b      0t0  TCP localhost:postgresql (LISTEN)
postgres 529 Nelson  6u  IPv4 0xbc5d729e55a89a13      0t0  TCP localhost:postgresql (LISTEN)

This tells us that PostgreSQL is "listening" on TCP Port 5432 (the default port)

If the lsof command does not yield any result in your terminal, run:

pg_isready

It should print the following:

/tmp:5432 - accepting connections

With all those "pre-flight checks" performed, let's fly! πŸš€


First Run the Finished App

Before you attempt to build the Chat App from scratch, clone and run the finished working version to get an idea of what to expect.

Clone the Project:

In your terminal run the following command to clone the repo:

git clone [email protected]:dwyl/phoenix-chat-example.git

Install the Dependencies

Change into the phoenix-chat-example directory and install both the Elixir and Node.js dependencies with this command:

cd phoenix-chat-example
mix setup

Run the App

Run the Phoenix app with the command:

mix phx.server

If you open the app localhost:4000 in two more web browsers, you can see the chat messages displayed in all of them as soon as you hit the Enter key:

phoenix-chat-example-tailwind-ui-with-auth


Now that you have confirmed that the finished phoenix chat app works on your machine, it's time to build it from scratch!

Change directory:

cd ..

And start building!


1. Create The App

In your terminal program on your localhost, type the following command to create the app:

mix phx.new chat --no-mailer --no-dashboard --no-gettext

That will create the directory structure and project files.

We are running the mix phx.new command with the --no-mailer --no-dashboard --no-gettext arguments because we don't want our project to generate mailer files, to include a Phoenix.LiveDashboard and generate gettext files (for i18n).

When asked to "Fetch and install dependencies? [Yn]",
Type Y in your terminal, followed by the Enter (Return) key.

You should see:
fetch-and-install-dependencies

Change directory into the chat directory by running the suggested command:

cd chat

Now run the following command:

mix setup

Note: at this point there is already an "App" it just does not do anything (yet) ...
you can run mix phx.server in your terminal - don't worry if you're seeing error
messages, this is because we haven't created our database yet.
We will take care of that in step 6!
For now, open http://localhost:4000 in your browser
and you will see the default "Welcome to Phoenix" homepage:

welcome-to-phoenix

Shut down the Phoenix server in your terminal with the ctrl+C command.

Run the Tests

In your terminal window, run the following command:

mix test

You should see output similar to the following:

Generated chat app
.....
Finished in 0.02 seconds (0.02s async, 0.00s sync)
5 tests, 0 failures

Randomized with seed 84184

Now that we have confirmed that everything is working (all tests pass), let's continue to the interesting part!


2. Create the (WebSocket) "Channel"

Generate the (WebSocket) channel to be used in the chat app:

mix phx.gen.channel Room

If you are prompted to confirm installation of a new socket handler type y and hit the [Enter] key.

This will create three files:

* creating lib/chat_web/channels/room_channel.ex
* creating test/chat_web/channels/room_channel_test.exs
* creating test/support/channel_case.ex

in addition to creating two more files:

* creating lib/chat_web/channels/user_socket.ex
* creating assets/js/user_socket.js

The room_channel.ex file handles receiving/sending messages and the room_channel_test.exs tests basic interaction with the channel. We'll focus on the socket files created afterwards. (Don't worry about this yet, we will look at the test file in step 14 below!)

We are informed that we need to update a piece of code in our app:

Add the socket handler to your `lib/chat_web/endpoint.ex`, for example:

    socket "/socket", ChatWeb.UserSocket,
      websocket: true,
      longpoll: false

For the front-end integration, you need to import the `user_socket.js`
in your `assets/js/app.js` file:

    import "./user_socket.js"

The generator asks us to import the client code in the frontend. Let's do that later. For now, open the lib/chat_web/endpoint.ex file and follow the instructions.

After this, open the file called /lib/chat_web/channels/user_socket.ex
and change the line:

channel "room:*", ChatWeb.RoomChannel

to:

channel "room:lobby", ChatWeb.RoomChannel

Check the change here.

This will ensure that whatever messages that are sent to "room:lobby" are routed to our RoomChannel.

The previous "room.* meant that any subtopic within "room" were routed. But for now, let's narrow down to just one subtopic πŸ˜„.

For more detail on Phoenix Channels, (we highly recommend you) read: https://hexdocs.pm/phoenix/channels.html


3. Update the Template File (UI)

Open the the /lib/chat_web/controllers/page_html/home.html.heex file
and copy-paste (or type) the following code:

<!-- The list of messages will appear here: -->
<div class="mt-[4rem]">
  <ul id="msg-list" phx-update="append" class="pa-1"></ul>
</div>

<footer class="bg-slate-800 p-2 h-[3rem] bottom-0 w-full flex justify-center sticky mt-[auto]">
  <div class="w-full flex flex-row items-center text-gray-700 focus:outline-none font-normal">
    <input type="text" id="name" placeholder="Name" required
        class="grow-0 w-1/6 px-1.5 py-1.5"/>

    <input type="text" id="msg" placeholder="Your message" required
      class="grow w-2/3 mx-1 px-2 py-1.5"/>

    <button id="send" class="text-white bold rounded px-3 py-1.5 w-fit
        transition-colors duration-150 bg-sky-500 hover:bg-sky-600">
      Send
    </button>
  </div>
</footer>

This is the basic form we will use to input Chat messages.
The classes e.g. w-full and items-center are TailwindCSS classes to style the form.
Phoenix includes Tailwind by default so you can get up-and-running with your App/Idea/"MVP"!

If you're new to Tailwind, please see: dwyl/learn-tailwind

If you have questions about any of the Tailwind classes used, please spend 2 mins Googling or searching the official (superb!) docs: tailwindcss.com/docs and then if you're still stuck, please open an issue.

Your home.html.heex template file should look like this: /lib/chat_web/controllers/page_html/home.html.heex

3.1 Update Layout Template

Open the lib/chat_web/components/layouts/root.html.heex file and locate the <body> tag. Replace the contents of the <body> with the following code:

  <body class="bg-white antialiased min-h-screen flex flex-col">
    <header class="bg-slate-800 w-full h-[4rem] top-0 fixed flex flex-col justify-center z-10">
      <div class="flex flex-row justify-center items-center">
        <h1 class="w-4/5 md:text-3xl text-center font-mono text-white">
          Phoenix Chat Example
        </h1>
      </div>
    </header>
    <%= @inner_content %>
  </body>

Your root.html.heex template file should look like this: /lib/chat_web/components/layouts/root.html.heex

At the end of this step, if you run the Phoenix Server mix phx.server, and view the App in your browser it will look like this:

phoenix-chat-blank

So it's already starting to look like a basic Chat App. Sadly, since we changed the copy of the home.html.heex our page_controller_test.exs now fails:

Run the command:

mix test
1) test GET / (ChatWeb.PageControllerTest)
     test/chat_web/controllers/page_controller_test.exs:4
     Assertion with =~ failed
     code:  assert html_response(conn, 200) =~ "Peace of mind from prototype to production"

Thankfully this is easy to fix.

3.2 Update the page_controller_test.exs

Open the test/chat_web/controllers/page_controller_test.exs file and replace the line:

    assert html_response(conn, 200) =~ "Peace of mind from prototype to production"

With:

    assert html_response(conn, 200) =~ "Phoenix Chat Example"

Now if you run the tests again, they will pass:

mix test

Sample output:

........
Finished in 0.1 seconds (0.09s async, 0.06s sync)
8 tests, 0 failures

Randomized with seed 275786

4. Update the "Client" code in App.js

Open assets/js/app.js, uncomment and change the line:

import socket from "./user_socket.js"

With the line uncommented, our app will import the socket.js file which will give us WebSocket functionality.

Then add the following JavaScript ("Client") code to the bottom of the file:

/* Message list code */
const ul = document.getElementById('msg-list');    // list of messages.
const name = document.getElementById('name');      // name of message sender
const msg = document.getElementById('msg');        // message input field
const send = document.getElementById('send');      // send button

const channel = socket.channel('room:lobby', {});  // connect to chat "room"
channel.join(); // join the channel.

// Listening to 'shout' events
channel.on('shout', function (payload) {
  render_message(payload)
});


// Send the message to the server on "shout" channel
function sendMessage() {

  channel.push('shout', {        
    name: name.value || "guest", // get value of "name" of person sending the message. Set guest as default
    message: msg.value,          // get message text (value) from msg input field.
    inserted_at: new Date()      // date + time of when the message was sent
  });

  msg.value = '';                // reset the message input field for next message.
  window.scrollTo(0, document.documentElement.scrollHeight) // scroll to the end of the page on send
}

// Render the message with Tailwind styles
function render_message(payload) {

  const li = document.createElement("li"); // create new list item DOM element

  // Message HTML with Tailwind CSS Classes for layout/style:
  li.innerHTML = `
  <div class="flex flex-row w-[95%] mx-2 border-b-[1px] border-slate-300 py-2">
    <div class="text-left w-1/5 font-semibold text-slate-800 break-words">
      ${payload.name}
      <div class="text-xs mr-1">
        <span class="font-thin">${formatDate(payload.inserted_at)}</span> 
        <span>${formatTime(payload.inserted_at)}</span>
      </div>
    </div>
    <div class="flex w-3/5 mx-1 grow">
      ${payload.message}
    </div>
  </div>
  `
  // Append to list
  ul.appendChild(li);
}

// Listen for the [Enter] keypress event to send a message:
msg.addEventListener('keypress', function (event) {
  if (event.keyCode == 13 && msg.value.length > 0) { // don't sent empty msg.
    sendMessage()
  }
});

// On "Send" button press
send.addEventListener('click', function (event) {
  if (msg.value.length > 0) { // don't sent empty msg.
    sendMessage()
  }
});

// Date formatting
function formatDate(datetime) {
  const m = new Date(datetime);
  return m.getUTCFullYear() + "/" 
    + ("0" + (m.getUTCMonth()+1)).slice(-2) + "/" 
    + ("0" + m.getUTCDate()).slice(-2);
}

// Time formatting
function formatTime(datetime) {
  const m = new Date(datetime);
  return ("0" + m.getUTCHours()).slice(-2) + ":"
    + ("0" + m.getUTCMinutes()).slice(-2) + ":"
    + ("0" + m.getUTCSeconds()).slice(-2);
}

Take a moment to read the JavaScript code and confirm your understanding of what it's doing.
Hopefully the in-line comments are self-explanatory, but if anything is unclear, please ask!

At this point your app.js file should look like this: /assets/js/app.js

4.1 Comment Out Lines in user_socket.js

By default the phoenix channel (client) will subscribe to the generic room: "topic:subtopic". Since we aren't going to be using this, we can avoid seeing any "unable to join: unmatched topic" errors in our browser/console by simply commenting out a few lines in the user_socket.js file. Open the file in your editor and locate the following lines:

let channel = socket.channel("room:42", {})
channel.join()
  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) })

Comment out the lines so they will not be executed:

//let channel = socket.channel("room:42", {})
//channel.join()
//  .receive("ok", resp => { console.log("Joined successfully", resp) })
//  .receive("error", resp => { console.log("Unable to join", resp) })

Your user_socket.js should now look like this: /assets/js/user_socket.js

If you later decide to tidy up your chat app, you can delete these commented lines from the file.
We are just keeping them for reference of how to join channels and receive messages.

If you are running the app, try to fill the name and message fields and click Enter (or press Send).

The message should appear on different windows!

ephemeral_chat

With this done, we can proceed.


Storing Chat Message Data/History

If we didn't want to save the chat history, we could just deploy this App immediately and we'd be done!

In fact, it could be a "use-case" / "feature" to have "ephemeral" chat without any history ... see: http://www.psstchat.com/. psst-chat

But we are assuming that most chat apps save history so that new people joining the "channel" can see the history and people who are briefly "absent" can "catch up" on the history.


5. Generate Database Schema to Store Chat History

Run the following command in your terminal:

mix phx.gen.schema Message messages name:string message:string

You should see the following output:

* creating lib/chat/message.ex
* creating priv/repo/migrations/20230203114114_create_messages.exs

Remember to update your repository by running migrations:

    $ mix ecto.migrate

Let's break down that command for clarity:

  • mix phx.gen.schema - the mix command to create a new schema (database table)
  • Message - the singular name for record in our messages "collection"
  • messages - the name of the collection (or database table)
  • name:string - the name of the person sending a message, stored as a string.
  • message:string - the message sent by the person, also stored as a string.

The line creating lib/chat/message.ex creates the "schema" for our Message database table.

Additionally a migration file is created, e.g: creating priv/repo/migrations/20230203114114_create_messages.exs The "migration" actually creates the database table in our database.


6. Run the Ecto Migration (Create The Database Table)

In your terminal run the following command to create the messages table:

mix ecto.migrate

You should see the following in your terminal:

11:42:10.130 [info] == Running 20230203114114 Chat.Repo.Migrations.CreateMessages.change/0 forward

11:42:10.137 [info] create table messages

11:42:10.144 [info] == Migrated 20230203114114 in 0.0s

6.1 Review the Messages Table Schema

If you open your PostgreSQL GUI (e.g: pgadmin) you will see that the messages table has been created in the chat_dev database:

pgadmin-messages-table

You can view the table schema by "right-clicking" (ctrl + click on Mac) on the messages table and selecting "properties":

pgadmin-messages-schema-columns-view


7. Insert Messages into Database

Open the lib/chat_web/channels/room_channel.ex file and inside the function def handle_in("shout", payload, socket) do add the following line:

Chat.Message.changeset(%Chat.Message{}, payload) |> Chat.Repo.insert  

So that your function ends up looking like this:

def handle_in("shout", payload, socket) do
  Chat.Message.changeset(%Chat.Message{}, payload) |> Chat.Repo.insert  
  broadcast socket, "shout", payload
  {:noreply, socket}
end

8. Load Existing Messages (When Someone Joins the Chat)

Open the lib/chat/message.ex file and import Ecto.Query:

defmodule Chat.Message do
  use Ecto.Schema
  import Ecto.Changeset
  import Ecto.Query # add Ecto.Query

Then add a new function to it:

def get_messages(limit \\ 20) do
  Chat.Message
  |> limit(^limit)
  |> order_by(desc: :inserted_at)
  |> Chat.Repo.all()
end

This function accepts a single parameter limit to only return a fixed/maximum number of records. It uses Ecto's all function to fetch all records from the database. Message is the name of the schema/table we want to get records for, and limit is the maximum number of records to fetch.


9. Send Existing Messages to the Client when they Join

In the /lib/chat_web/channels/room_channel.ex file create a new function:

@impl true
def handle_info(:after_join, socket) do
  Chat.Message.get_messages()
  |> Enum.reverse() # revers to display the latest message at the bottom of the page
  |> Enum.each(fn msg -> push(socket, "shout", %{
      name: msg.name,
      message: msg.message,
      inserted_at: msg.inserted_at,
    }) end)
  {:noreply, socket} # :noreply
end

and at the top of the file update the join function to the following:

def join("room:lobby", payload, socket) do
  if authorized?(payload) do
    send(self(), :after_join)
    {:ok, socket}
  else
    {:error, %{reason: "unauthorized"}}
  end
end

10. Checkpoint: Our Chat App Saves Messages!! (Try it!)

Start the Phoenix server (if it is not already running):

mix phx.server

Note: it will take a few seconds to compile.

In your terminal, you should see:

[info] Running ChatWeb.Endpoint with cowboy 2.8.0 at 0.0.0.0:4000 (http)
[info] Access ChatWeb.Endpoint at http://localhost:4000

webpack is watching the files…

This tells us that our code compiled (as expected) and the Chat App is running on TCP Port 4000!

Open the Chat web app in two separate browser windows: http://localhost:4000
(if your machine only has one browser try using one "incognito" tab)

You should be able to send messages between the two browser windows:
phoenix-chat-example-basic-cropped

Congratulations! You have a working (basic) Chat App written in Phoenix!

The chat (message) history is saved!

This means you can refresh the browser or join in a different browser and you will still see the history!


Testing our App (Automated Testing)

Automated testing is one of the best ways to ensure reliability in your web applications.

Note: If you are completely new to Automated Testing or "Test Driven Development" ("TDD"), we recommend reading/following the "basic" tutorial: github.com/dwyl/learn-tdd

Testing in Phoenix is fast (tests run in parallel!) and easy to get started! The ExUnit testing framework is built-in so there aren't an "decisions/debates" about which framework or style to use.

If you have never seen or written a test with ExUnit, don't fear, the syntax should be familiar if you have written any sort of automated test in the past.


11. Run the Default/Generated Tests

Whenever you create a new Phoenix app or add a new feature (like a channel), Phoenix generates a new test for you.

We run the tests using the mix test command:

........
Finished in 0.1 seconds (0.05s async, 0.06s sync)
8 tests, 0 failures

Randomized with seed 157426

In this case none of these tests fails. (8 tests, 0 failure)

12. Understanding The Channel Tests

It's worth taking a moment (or as long as you need!) to understand what is going on in the /room_channel_test.exs file. Open it if you have not already, read the test descriptions & code.

For a bit of context we recommend reading: https://hexdocs.pm/phoenix/testing_channels.html

12.1 Analyse a Test

Let's take a look at the first test in /test/chat_web/channels/room_channel_test.exs:

test "ping replies with status ok", %{socket: socket} do
  ref = push socket, "ping", %{"hello" => "there"}
  assert_reply ref, :ok, %{"hello" => "there"}
end

The test gets the socket from the setup function (on line 6 of the file) and assigns the result of calling the push function to a variable ref push merely pushes a message (the map %{"hello" => "there"}) on the socket to the "ping" topic.

The handle_in function clause which handles the "ping" topic:

def handle_in("ping", payload, socket) do
  {:reply, {:ok, payload}, socket}
end

Simply replies with the payload you send it, therefore in our test we can use the assert_reply Macro to assert that the ref is equal to :ok, %{"hello" => "there"}

Note: if you have questions or need any help understanding the other tests, please open an issue on GitHub we are happy to expand this further!
(we are just trying to keep this tutorial reasonably "brief" so beginners are not "overwhelmed" by anything...)


13. What is Not Tested?

Often we can learn a lot about an application (or API) from reading the tests and seeing where the "gaps" in testing are.

Thankfully we can achieve this with only a couple of steps:


13.1 Add excoveralls as a (Development) Dependency to mix.exs

Open your mix.exs file and find the "deps" function:

defp deps do

Add a comma to the end of the last line, then add the following line to the end of the List:

{:excoveralls, "~> 0.15.2", only: [:test, :dev]} # tracking test coverage

Additionally, find the def project do section (towards the top of mix.exs) and add the following lines to the List:

test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
  coveralls: :test,
  "coveralls.detail": :test,
  "coveralls.post": :test,
  "coveralls.html": :test
]

Then, install the dependency on excoveralls we just added to mix.exs:

mix deps.get

You should see:

Resolving Hex dependencies...
Dependency resolution completed:
* Getting excoveralls (Hex package)
... etc.

13.2 Create a New File Called coveralls.json

In the "root" (base directory) of the Chat project, create a new file called coveralls.json and copy-paste the following:

{
  "coverage_options": {
    "minimum_coverage": 100
  },
  "skip_files": [
    "test/",
    "lib/chat/application.ex",
    "lib/chat_web.ex",
    "lib/chat_web/telemetry.ex",
    "lib/chat_web/components/core_components.ex",
    "lib/chat_web/channels/user_socket.ex"
  ]
}

This file is quite basic, it instructs the coveralls app to require a minimum_coverage of 100% (i.e. everything is tested1) and to ignore the files in the test/ directory for coverage checking. We also ignore files such as application.ex, telemetry.ex, core_components.ex and user_socket.ex because they are not relevant for the functionality of our project.

1We believe that investing a little time up-front to write tests for all our code is worth it to have fewer bugs later.
Bugs are expensive, tests are cheap and confidence/reliability is priceless
.

13.3 Run the Tests with Coverage Checking

To run the tests with coverage, copy-paste the following command into your terminal:

MIX_ENV=test mix do coveralls.json

You should see:

Randomized with seed 527109
----------------
COV    FILE                                        LINES RELEVANT   MISSED
100.0% lib/chat.ex                                     9        0        0
100.0% lib/chat/message.ex                            26        4        0
100.0% lib/chat/repo.ex                                5        0        0
 70.0% lib/chat_web/channels/room_channel.ex          46       10        3
100.0% lib/chat_web/components/layouts.ex              5        0        0
100.0% lib/chat_web/controllers/error_html.ex         19        1        0
100.0% lib/chat_web/controllers/error_json.ex         15        1        0
100.0% lib/chat_web/controllers/page_controller        9        1        0
100.0% lib/chat_web/controllers/page_html.ex           5        0        0
100.0% lib/chat_web/endpoint.ex                       49        0        0
 66.7% lib/chat_web/router.ex                         27        3        1
[TOTAL]  80.0%
----------------

As we can se here, only 80% of lines of code in /lib are being "covered" by the tests we have written.

To view the coverage in a web browser run the following:

MIX_ENV=test mix coveralls.html ; open cover/excoveralls.html

This will open the Coverage Report (HTML) in your default Web Browser:

coverage-80-percent

13.4 Write a Test for the Untested Function

Open the test/chat_web/channels/room_channel_test.exs file and add the following test:

test ":after_join sends all existing messages", %{socket: socket} do
  # insert a new message to send in the :after_join
  payload = %{name: "Alex", message: "test"}
  Chat.Message.changeset(%Chat.Message{}, payload) |> Chat.Repo.insert()

  {:ok, _, socket2} = ChatWeb.UserSocket
    |> socket("person_id", %{some: :assign})
    |> subscribe_and_join(ChatWeb.RoomChannel, "room:lobby")

  assert socket2.join_ref != socket.join_ref
end

Finally, inside lib/chat_web/router.ex, comment the following piece of code.

  pipeline :api do
    plug :accepts, ["json"]
  end

Since we are not using this :api in this project, there is no need to test it.

Now when you run MIX_ENV=test mix do coveralls.json you should see:

Randomized with seed 15920
----------------
COV    FILE                                        LINES RELEVANT   MISSED
100.0% lib/chat.ex                                     9        0        0
100.0% lib/chat/message.ex                            26        4        0
100.0% lib/chat/repo.ex                                5        0        0
100.0% lib/chat_web/channels/room_channel.ex          46       10        0
100.0% lib/chat_web/components/layouts.ex              5        0        0
100.0% lib/chat_web/controllers/error_html.ex         19        1        0
100.0% lib/chat_web/controllers/error_json.ex         15        1        0
100.0% lib/chat_web/controllers/page_controller        9        1        0
100.0% lib/chat_web/controllers/page_html.ex           5        0        0
100.0% lib/chat_web/endpoint.ex                       49        0        0
100.0% lib/chat_web/router.ex                         27        2        0
[TOTAL] 100.0%
----------------

This test just creates a message before the subscribe_and_join so there is a message in the database to send out to any clien that joins the chat.

That way the :after_join has at least one message and the Enum.each will be invoked at least once.

With that our app is fully tested!


Authentication

We can extend this project to support basic authentication. If you want to understand how Authentication is implemented the easy/fast way, see: auth.md


Adding Presence to track who's online

One of the great advantages of using Phoenix is that you can easily track processes and channels.

This paves the way to effortlessly showing who's online or not!

If you are interested in developing this feature, we have created a guide in presence.md just for you! πŸ˜€


Continuous Integration

Continuous integration lets you automate running the tests to check/confirm that your app is working as expected (before deploying). This prevents accidentally "breaking" your app.

Thankfully the steps are quite simple.

For an example ci.yml, see:

.github/workflows/ci.yml


Deployment!

Deployment to Fly.io takes a couple of minutes, we recommend following the official guide: fly.io/docs/elixir/getting-started

Once you have deployed you will will be able to view/use your app in any Web/Mobile Browser.

e.g: phoenix-chat.fly.dev/


thats-all-folks


What Next?

If you found this example useful, please ⭐️ the GitHub repository so we (and others) know you liked it!

If you want to learn more Phoenix and the magic of LiveView, consider reading our beginner's tutorial: github.com/dwyl/phoenix-liveview-counter-tutorial

For a version of a chat application using LiveView you can read the following repository: github.com/dwyl/phoenix-liveview-chat-example

Thank you for learning with us! β˜€οΈ



Inspiration

This repo is inspired by @chrismccord's Simple Chat Example: https://github.com/chrismccord/phoenix_chat_example ❀️

At the time of writing Chris' example was last updated on 20 Feb 2018 and uses Phoenix 1.3 see: issues/40.
There are quite a few differences (breaking changes) between Phoenix 1.3 and 1.6 (the latest version).

Our tutorial uses Phoenix 1.6.2 (latest as of October 2021). Our hope is that by writing (and maintaining) a step-by-step beginner focussed tutorial we contribute to the Elixir/Phoenix community without piling up PRs on Chris's repo.

Recommended Reading / Learning

phoenix-chat-example's People

Contributors

ajmeese7 avatar arhell avatar comerc avatar dependabot[bot] avatar hrithikwins avatar iteles avatar karwasze avatar kyleboe avatar laser-shark avatar llimllib avatar luchoturtle avatar nelsonic avatar sajidmisfit avatar seans84 avatar simonlab avatar smm13344331 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phoenix-chat-example's Issues

Feedback from a beginner

@nelsonic As promised, here is some feedback:

Getting started

I already had Phoenix and Elixir installed, and that's straightforward anyway.

Postgres, on the other hand πŸ˜–

I did have postgres installed from testing out the dwylbot docs, so I just started psql and ran lsof -i :5432. No output at all.

So I did various things like checking ports were open and restarting the services and a dozen things I found in SO answers or linux blogs etc. No change.

So I figured that it might be because there were multiple postgres instances or I'd set it up wrong in the first place - so I purged postgres entirely and started from scratch.

Now, this may be because I'm not amazing with linux or just generally quite mediocre at doing the coding things, but I still couldn't get any output from running the lsof command, unless I switched to the postgres user by sudo -i -u postgres.

(I had precisely followed https://github.com/dwyl/learn-postgresql all the way up to, but not including, installing PostGIS).

Sod it

By this point I'd spend far more than a single πŸ…, so I figured I'd just start the server without the database and just play around with that until it broke.

The server very kindly decided to run and let me access the page despite the c. 100 postgres-related errors in the terminal.

Because of the generosity of the whole thing not crashing despite multiple errors, I was then able to follow the tutorial quite easily and get the app working to an extent.

Until...

ecto.create

BOOM πŸ’₯ πŸ’₯

Once I ran that, all the postgres errors disappeared and lsof -i :5432 output the right thing and the whole app worked as it should and I was able to complete the tutorial (though I haven't had time to deploy to heroku).

Recommendations

Once I'd a) ignored the errors and then b) fixed the errors, the tutorial was great - easy to follow and with good, clear explanations.

Breakdowns like the one at https://github.com/dwyl/phoenix-chat-example#7-generate-database-schema-to-store-chat-history are very useful, for example.

So for me the key amendments needed are:

  • Getting postgres up and running. I'm happy to attribute my failure to operator error, but I really did try everything I could find online to get it started.
    • If you can guess what I did wrong, perhaps include a warning
    • If not, perhaps consider adding some guidance in case the "pre-flight checks" fail
    • Or maybe move the mix ecto.create command earlier (though I accept that doing this would mess with the flow of the tutorial)
  • Getting everything up and running generally - might be an idea to tell people which steps of the installation guides to follow - specifically maybe say "Follow the postgres tutorial but stop before installing postGIS"?
  • Small point, and probably because I only skimmed the basics of Phoenix/Elixir, but when we had to update the room_channel.ex and message.ex files, I added the new functions def outside the module def (i.e. at the very bottom of the file)
    • Now, because I'm not totally devoid of coding instincts, I noticed my error quite swiftly (because I figured defmodule probably defs a whole module duh) - but it might be worth adding a note to point out precisely where the function goes

Add 1 to 1 chat functionality

It would be super awesome to upgrade this great tutorial with a bit more advanced example. How would one pick up one person from (presence?) and open "private" 1-to-1 chat with it. On prior approval.

mix setup fails on node 16.3

The full build log of a failure is available here

I solved the issue by downgrading my version of node; I use asdf so for me it was as simple as asdf local nodejs ref:v14.17.0. I believe this is due to sass/node-sass#3077 or something similar (I've faced it on other projects).

Update to Phoenix 1.5.1

With the recent release of Phoenix 1.5 πŸŽ‰ https://elixirforum.com/t/phoenix-v1-5-0-released/30693
Now is a great time to go through this example/tutorial with a fresh pair of eyes and completely update it (including) links to code snapshots. We can also extend it to include auth_plug #25 πŸ” and presence #32 πŸš€.

Related to #19 (feedback on the version of the tutorial).

I expect this to take around 4 hours. ⏳
@SimonLab / @iteles if you want to remote-pair on this, LMK. πŸ‘
Testing auth_plug and Presence are both core to our App "roadmap". πŸ—ΊοΈ

design contribution proposal

Hi,

I am a graphic designer. I contribute to open source projects. If you need a logo design, I can do it. I also have a nice logo idea in my mind. I will be waiting for your feedback ...

Issue with "coveralls" with ElixirLS

Hey there,

Not sure if this is an ElixirLS issue or an issue with the documentation, but in relation to point 15 in the tutorial, I found the text placed in the dependencies folder throws a syntax error

image

I ended up removing the "" from the first elements and ElixirLS is happy with this. I'm totally new to this so I'm not sure whether or not this is expected.

Additionally, in my case, excoveralls 0.7.5 appears to have been included previously.

Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
  certifi 2.5.1
  connection 1.0.4
  cowboy 2.7.0
  cowlib 2.8.0
  db_connection 2.2.2
  decimal 1.8.1
  ecto 3.4.4
  ecto_sql 3.4.4
  excoveralls 0.7.5
  exjsx 4.0.0
  file_system 0.2.8
  gettext 0.18.0
  hackney 1.15.2
  idna 6.0.0
  jason 1.2.1
  jsx 2.8.3
  metrics 1.0.1
  mime 1.3.1
  mimerl 1.2.0
  parse_trans 3.3.0
  phoenix 1.5.3
  phoenix_ecto 4.1.0
  phoenix_html 2.14.2
  phoenix_live_dashboard 0.2.4
  phoenix_live_reload 1.2.2
  phoenix_live_view 0.13.0
  phoenix_pubsub 2.0.0
  plug 1.10.1
  plug_cowboy 2.2.1
  plug_crypto 1.1.2
  postgrex 0.15.4
  ranch 1.7.1
  ssl_verify_fun 1.1.5
  telemetry 0.4.1
  telemetry_metrics 0.5.0
  telemetry_poller 0.5.0
  unicode_util_compat 0.4.1
All dependencies are up to date

Guest messages are not saved in Postgres

When the name is left blank the message appears on the chat as guest:
image

However it is not saved into Postgres and on the next page reload the message won't appear anymore.
This is due to the changeset of the message which requires the name to not be blank:

|> validate_required([:name, :message])

So the insert won't work in this case:

Chat.Message.changeset(%Chat.Message{}, payload) |> Chat.Repo.insert()

Update to Phoenix v1.6

Given the recent Phoenix v1.6.0-rc.1 see: dwyl/learn-phoenix-framework#143

Todo

  • Go through this tutorial again and update to the latest Phoenix
    • Confirm if there is any added complexity or if v1.6 simplifies things.
  • Create Pull Request with changes
  • Assign PR for review

I expect this exercise to take around T2h because the tutorial is quite long and we need to test all the steps.
It might take less time, but this is the estimate. πŸ’­

[CHORE] The walkthrough appears to be outdated (v1.6.14)

As of the time of writing, Phoenix's latest version is 1.6.14 (with 1.7 apparently coming very soon 🀩 ).
This tutorial is great and works perfectly if I clone and run it.

However, when I try to build a new Phoenix app from scratch and follow this tutorial,
some information appears to be outdated.

Here are a few examples:

  • The boostrapped project with the newest version appears to have more files in some folders, namely lib\templates\layout.
  • Updating the layout template on app.html.eex does not make sense now, since the information is on root.html.eex.
  • Tests do not pass after "correcting" in page_controller_test.exs.

This is a snowball effect and for anyone that is new, they expect this tutorial to "Just WorkTM".
No doubt it does with v.1.6.11, though!

I'll try to update the tutorial to the latest version and the README file accordingly to the best of my abilities πŸ˜„ ...

Todo

  • clone the repo to localhost ⬇️
  • make a reference copy of the repo e.g. cp phoenix-chat-example phoenix-chat-example_OLD so that you can have one open while editing the other and not have to switch between branches. ✌️

Note: this is where having that second or third screen comes into play. πŸ’» + πŸ–₯️ + πŸ–₯️ πŸ’­

  • Create a new branch e.g. rewrite-phoenix-v1.6.14-issue#112 (snappy name, right? 😜)
    • git push the new branch to GitHub ASAP so that you can see if CI (GitHub Actions) continues to pass.
  • Re-create the app from scratch following the instructions in the README.md as much as possible. πŸ§‘β€πŸ’»
  • Update the sections that require it. πŸ†™
  • Preserve the Testing and any other sections that are still relevant. πŸ₯‡
  • Create a PR and assign it to me for review. πŸ™

Table of Contents?

Could this tutorial benefit from a Table of Contents...?
(any volunteers...?)

Unable to join Object { reason: "unmatched topic" }

Hello,
I followed whole tutorial, however I am not able to make it run well.

I am getting this error at chrome, console Unable to join Object { reason: "unmatched topic" }

also in the erlang console I get this error
[warn] Ignoring unmatched topic "topic:subtopic" in ChatWeb.UserSocket

I checked the room name in
lib/chat_web/channels/room_channel.ex
lib/chat_web/channels/user_socket.ex
assets/js/app.js

to ensure all three files have the same room:main (in my case)

Can you help me please?

Elm + Phoenix Quest

We love Elixir & Phoenix because they make building web apps easy and fun!
We are less enamoured with using JavaScript on the frontend because reliability is hard in JS.

At present there is not very much JS in this example because features are minimal, see: app.js
but the more JS we add the more likely it is to get messy and unmaintainable.

So I propose we extend this tutorial using Elm instead of JS
and give people a basic intro to Elm as the "advanced" section of this example.

Todo

Extend this tutorial to use Elm on the frontend for extensibility/reliability.

  • Add GitHub Auth to avoid impersonation and anonymous vandalism. see: #25
    |> dwyl/elixir-auth-github#32
  • Add Phoenix Presence (server side) to see who is online: #14
    • Use Elm to display the list of people who are online with GitHub Profile pictures
      • When profile pic hovered over or clicked display more profile details, e.g:
        image
  • Use Elm to post messages and avoid XSS by sanitising user input #25
  • Create a "Connected Status" Icon in the UI which shows when connected/disconnected.
    • Detect when disconnected from Phoenix server/channel.
      e.g: if I turn off Wifi on my device, the UI should reflect that I am no longer connected and change the "connected" icon to https://thenounproject.com/term/no-connection/369426/
    • disable the "post" message action, but should still allow messages to be composed.
    • When re-connected to Phoenix, display a brief and unobtrusive "connected" (snackbar) message and update the icon to show "Online" version.

Chore: Comprehensively Update this Tutorial to `Phoenix v1.7`

With the recent release of Phoenix v1.7.0-rc.2 (which looks reasonably stable),
Now is a good time to comprehensively update this tutorial to the latest and greatest.
This is a great opportunity for us to see the differences between 1.6.X and 1.7 in a more involved project.

Todo

  • git clone this project to your localhost and rename it to phx-chat-OLD
  • use this "old" version as the basis for creating the new one, but ...
  • git clone the project again and then DELETE all the code in the directories and mix.exs/mix.lock file:

phoenix-chat-example-delete

  • Create a new branch on your localhost e.g. phx-1.7-issue#X and
  • Follow the instructions in the REAMDE.md to create a new app from scratch.
  • Please remember to exclude all the noise that we don't need in a demo app like this e.g:
mix phx.new chat --no-mailer --no-dashboard --no-gettext

We really don't need all the noise from constant @dependabot updates for deps we aren't using in the example! ⏳

Note: this tutorial will NOT use LiveView.
We have a different one for that: https://github.com/dwyl/phoenix-liveview-chat-example
That is deliberate. We still use "MVC" (even though Phoenix has removed the "V" ... https://www.germanvelasco.com/blog/phoenix-1-7-is-view-less ... πŸ™„ )
So we want this tutorial to continue to showcase the MπŸ™ƒC workflow even if we use LiveView in some other places.
I have zero intention of using LiveView for the basic flow in Auth for example. It's just not appropriate.

  • Update the README.md as needed to match the updates in Phoenix.
  • Match all the features in the current example including the Tailwind UI/UX and auth
    • update auth.md as needed (shouldn't be too much)
  • Create a Pull Request with the update.
  • Review your own PR before assigning it for review.
  • Assign the PR to @SimonLab for review. πŸ™

I expect this update to take a few hours. (T4h? πŸ€·β€β™‚οΈ)
It pains me that we have to spend this time. I wish there weren't so many breaking changes! 😒
But the faster we can make this update the faster I (@nelsonic) can learn what I need to re-build auth dwyl/auth#207 🀞

Thanks! πŸ™

Deploy to Gigalixir?

This repo is amazing!! Very well done! What do you think about adding a guide for deploying with Gigalixir? Disclaimer: I'm the founder of Gigalixir.

Downgrade Version of Elixir in mix.exs

We are unable to push updates to our Phoenix App running on Heroku because the version of Elixir in the elixir_buildpack.config is set to 1.5.3 whereas our mix.exs file is requiring 1.7

elixir: "~> 1.7",

image

image

The latest version of Phoenix 1.4.9 requires Elixir 1.4
See: https://github.com/phoenixframework/phoenix/blob/7c3979869f1f53f9e846d7e46354fe67ab98c5da/mix.exs#L10

Therefore I propose downgrading the requirement in our mix.exs file to 1.5 to ensure that deployment works on Heroku.

Possible readme update

As a first time Pheonix user I followed the Step 1 in the readme and created the app, but when checking to see if I had done it properly I received the following error message numerous times, filling my terminal.

[error] Postgrex.Protocol (#PID<0.363.0>) failed to connect: ** (Postgrex.Error) FATAL 3D000 (invalid_catalog_name): database "chat_dev" does not exist

I understand that the site works correctly as shown in the demo and that this error occurred as I had not created my database yet. However, I wasn't expecting to see a terminal full of errors and I thought it may put some newer users off.

Perhaps you could consider moving Step 6 Create/Configure Database into Step 1 Or noting that when starting the server there will be errors and the reason for them at this time.

Could you add Phoenix Presence?

This tutorial is so fantastic. Thanks lots. Are you able to add phoenix presence to the user name parts, to maybe keep a list of everyone who's connected and show when they disconnect and stuff?

Thanks again for wonderful guide!

The database for Chat.Repo couldn't be created: killed

Hey all! I have been trying to get the full version of this running before building it out. Postgres installed flawlessly and I got it up and running without an issue. I followed the initial instructions/installations then git-cloned and ran mix install but keep running into this error:

2020-10-17 18:05:27.473 CDT [3405] FATAL: role "postgres" does not exist

18:05:27.473 [error] GenServer #PID<0.3651.0> terminating
** (Postgrex.Error) FATAL 28000 (invalid_authorization_specification) role "postgres" does not exist
(db_connection 2.3.0) lib/db_connection/connection.ex:99: DBConnection.Connection.connect/2
(connection 1.0.4) lib/connection.ex:622: Connection.enter_connect/5
(stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
** (Mix) The database for Chat.Repo couldn't be created: killed

I ran a mix.update -all since I noticed that postgrex wasn't the absolute latest version, but I'm still getting the same error. I'm very new to elixir so I'm a bit stumped.

Edit: I'm running Mac OSX Catalina 10.15.7

Have some problems on step 5

After printing "cd assets && npm install && cd .." (or anything else instead "..") it writes me:
mac:chat mac$ cd assets && npm install && cd .. //First try
-bash: npm: command not found
mac:assets mac$ cd assets && npm install && cd .. //Second try
-bash: cd: assets: No such file or directory

In case people can't determine if Postgresql is running with lsof -i :5432

Hi there,

This is my first ever open-source contribution post on GitHub. I'm super-new at this but also want to make sure others don't get stuck with frustrating issues when learning.

I noticed that when going through the tutorial, there was a recommendation to use lsof -i :5432 to check if Postgresql was running correctly. This didn't work for me (there was no output), but I do know that Postgres was running as I had just successfully created a new DB with Ecto.

Instead, I used pg_isready and got the desired confirmation:

image

For context, I am running Ubuntu 18.04 through WSL2 with Postgresql-12.

Cheers,
Leo.

Update version of Phoenix in "Before you Start" section

At present the version of Phoenix recommended in the README.md is 1.4.0 (so last year!)
image

The latest version according to https://github.com/phoenixframework/phoenix/releases is 1.4.4:
image

The recommended way of installing Phoenix is still the mix task:

mix archive.install hex phx_new 1.4.4

https://hexdocs.pm/phoenix/installation.html
image

Todo

  • Update version Phoenix in README.md installation instructions
  • Update version of Phoenix mix.exs including any dependencies.
    (I created a new chat app on my localhost to check if there are any other changes)
  defp deps do
    [
      {:phoenix, "~> 1.4.4"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"}
    ]
  end
  • Create Pull Request with Changes.

This article isn't really geared for Phoenix 1.4?

Just my personal feedback.

As it is stated it will be based on Phoenix 1.4, some parts are not so applicable

Examples

npm install in assets folder, believed this is no longer valid as phoneix 1.4 is using webpack and npm install will work when you setup the project for the 1st time

also phoenix 1.4 is using milligram, so the codes in index.html.eex will not display properly as the codes are based on bootstrap related.

Hope you can enhance it or have a change notes somewhere. It will help the beginners to phoenix 1.4.

Add `Tailwind CSS` ✨

It's occurred to me that the CSS/Styles in this example are "meh" ...

image

And with the addition of Tailwind CSS in Phoenix 1.7 dwyl/learn-phoenix-framework#152
We can significantly improve the UI!

Luckily, we've already done the "hard" part in: dwyl/phoenix-liveview-chat-example#15-tailwind-css-stylin

liveview-chat-with-tailwind

So I expect that we can borrow much of the code from that tutorial
and estimate that it shouldn't take us more than T1h to get it done.
(probably much less Time/Effort, but being conservative for any layout issues ...)

Todo

Did not get expected result at step 8.1 - Review the Messages Table Schema

I am following this tutorial step by step. At step 8.1 - Review the Messages Table Schema, I could not see any table created thru psql cli tool . If the table is created, I should be able to see it through either pgAdmin or CLI, is that right?

I am using Phoenix 1.4.11 instead of 1.4.4 if that might be the reason.

Could you shed some light on how to create the table ?

Thanks ! This is a fantastic tutorial for beginners !

Add more clarity on the Travis CI part?

Just a suggestion

I have to google a bit to get this worked

  1. Clone a copy of test.exs to config/travis.exs

  2. In the travis.exs, make sure the password is set to "" (no password) for Postgres as per Travis docs

  3. In travis.yml, my version is this

language: elixir
elixir:
  - 1.7.3
addons:
  postgresql: '9.5'
services:
  - postgresql
before_script:
  - cp config/travis.exs config/test.exs
  - mix do ecto.create, ecto.migrate
env:
  - MIX_ENV=test
script:
  - mix test

Hope it helps :)

Probably is good to mention that not all postgresql version can work with travis e.g. my local mac is using 10.5 but travis can't seem to support 10.5, have to use 9.5.

Getting error: Ecto.Queryable not implemented for Message

Hey there. I've followed the tutorial and adjusted my postgres databases, so everything works except querying the db to grab the last 20 saved messages. Here's what I get after I run mix phx.server:

Compiling 1 file (.ex)
[info] Running ChatWeb.Endpoint with Cowboy using http://0.0.0.0:4000
14:09:40 - info: compiled 6 files into 2 files, copied 3 in 1.6 sec
[warn] Ignoring unmatched topic "room:lobby" in ChatWeb.UserSocket
[warn] Ignoring unmatched topic "room:lobby" in ChatWeb.UserSocket
[info] JOIN "room:lobby" to ChatWeb.RoomChannel
Transport: Phoenix.Transports.WebSocket (2.0.0)
Serializer: Phoenix.Transports.V2.WebSocketSerializer
Parameters: %{}
[info] Replied room:lobby :ok
[error] GenServer #PID<0.389.0> terminating
** (Protocol.UndefinedError) protocol Ecto.Queryable not implemented for Message, the given module does not exist. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple
(ecto) lib/ecto/queryable.ex:37: Ecto.Queryable.Atom.to_query/1
(ecto) lib/ecto/repo/queryable.ex:34: Ecto.Repo.Queryable.all/4
(chat) lib/chat_web/channels/room_channel.ex:29: ChatWeb.RoomChannel.handle_info/2
(phoenix) lib/phoenix/channel/server.ex:258: Phoenix.Channel.Server.handle_info/2
(stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:686: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: :after_join
State: %Phoenix.Socket{assigns: %{}, channel: ChatWeb.RoomChannel, channel_pid: #PID<0.389.0>, endpoint: ChatWeb.Endpoint, handler: ChatWeb.UserSocket, id: nil, join_ref: "41", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: Chat.PubSub, ref: nil, serializer: Phoenix.Transports.V2.WebSocketSerializer, topic: "room:lobby", transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.382.0>, vsn: "2.0.0"}

I'm new to Elixir/Phoenix, and I've searched around, but I can't find a solution to this error. Do you know what's going wrong here?

Feat: Add `auth_plug` to Phoenix Chat Example so people can authenticate πŸ”

This tutorial is currently already quite popular. 🀩 πŸŽ‰
We can take it to the next level by adding auth via auth_plug using ONE Environment Variableβ„’! πŸš€

Story

As a person learning Elixir/Phoenix 🌱
I need a fully featured example including auth πŸ”
So that I can learn the Tech Stack as fast as possible and contribute in the team. πŸ™Œ

@LuchoTurtle hopefully you can identify with this story. 🀞

Todo

  • Create a new markdown file called auth.md and link to it from the main README.md
  • Document each step in the setup process for adding authentication to this Chat App.
  • Add auth_plug to this project, see: https://github.com/dwyl/auth_plug_example#how
    • Along the way, if you get stuck, please open issues! πŸ™

Our objective is to make all our docs "standalone"; i.e. anyone can pick up and understand exactly what is going on without hitting any hurdles. We acknowledge that we aren't there yet: dwyl/auth#153 (comment) so we need lots of help to get to the point where auth has superb documentation.

Very happy to pair on this if you want an idea of my workflow.

Hopefully this is a fun challenge that will help you to learn a bit of the Tech Stack 🀞
and make it a lot easier for the next people joining the team. πŸ˜‰

image

UI Bug: Message input field overlaps last message in thread

At present the final message in the thread is covered by the input:

chat-example-input-covers-final-message

This is the very definition of a "not fun" UI bug to fix ...
But equally researching and understanding how to fix it is a great test of Tailwind/CSS knowledge.

Todo

  • Add Tailwind class to ensure that the <input #msg> does not cover the message(s)
  • Check that it works on Mobile Device.
  • Create PR to update.
  • Assign PR.

Video showing the issue in more detail:
https://user-images.githubusercontent.com/194400/216541869-df66058b-e81a-49b4-98a9-6dea11bfc73f.mov

Again, if you're in any doubt Why we build such comprehensive tutorials/examples,
we do it because it's the "right thing" to do for other people who are learning our Tech Stack
And the practice makes us much better at our jobs.
Learning is 60% of knowledge work. The better you are at capturing what you learn the more valuable you are.
Some companies think it's "OK" for their people to hold all the knowledge in their heads. we take the opposite view.
we want as many people both in our team and the wider community to know how to build and fix things.

So thank you for looking at this bug. πŸ™

Text Output Strange

Hi - sorry, this is probably a silly question because this is my first time w/ Elixir but i followed this example and the output is weird in that each time a user enters an output it puts the output adjacent (rather than below with a line break), which makes the messages impossible to read... For example a typical output on my end looks like:

Bob hi there. Alice Hello How are you? Bob I'm good, and you? Alice I'm good and you.

Maybe this is intended but ideally I would like the output to read as so (I believe with

line break or something of the sort) -

Bob hi there.
Alice Hello How are you?
Bob I'm good, and you?
Alice I'm good and you.

Now I thought the code that controlled this was app.js. I'm no expert with JS, but I tried every possible iteration of adding in a line break into channel.on as well as msg.addEventListener ... and no matter what the line breaks don't appear to show up in the code (not sure if this is because they are getting wrapped inside "li.. /li" elements).

Could you please suggest how to adjust this so that every record from the DB is separated with a line break?

I thought something like the following addition of a "br" element would work, but it does not -

channel.on('shout', function (payload) {
var li = document.createElement("li");
var name = payload.name || 'guest';
li.innerHTML = '

' + name + ': ' + payload.message + '


';
ul.appendChild(li);
ul.appendChild(document.createElement("br"));
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.