We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Okay so now I wanted to work on the lobby for the Rooms this is where a use can see all the active rooms and then be able to pick on or create their own if they are logged in. I like to keep everything I can separated so that you can keep context and domains to what they should be doing only.
First let’s set up the component.ex that will house the different things that we want to have in the lobby.ex. This will be the instructions for the rooms. The table for all the live games that are being played. As well as a final block for creating your own game. Later we can work on making sure that there can only be one room created person. But for right now we have all that we need.
defmodule GameSiteWeb.MultiPokerLive.Component do
use GameSiteWeb, :live_view
use Phoenix.Component
def instructions(assigns) do
~H"""
<h2 class="text-xl font-semibold mb-2">Poker Game Overview</h2>
<ul class="list-disc list-inside mt-2 space-y-1 text-gray-700">
<li>Here you can create or join a room to play some poker</li>
<li>You must be logged in to create a room.</li>
<li>If you want to keep track of your chip count you need to create an account.</li>
<li>There will be a max of 6 players per room.</li>
<li>You will start with 1000 chips if you are a new player.</li>
</ul>
"""
end
attr(:rooms, :map, required: true)
def live_games(assigns) do
~H"""
<.table id="rooms" rows={@rooms}>
<:col :let={room} label="Room ID">
<span class="font-mono text-sm">{room.display_id}</span>
</:col>
<:col :let={room} label="Players">
{room.player_count}
</:col>
<:col :let={room} label="Status">
<span class={[
"inline-flex rounded-full px-2 py-1 text-xs font-semibold",
room.room_status == :waiting && "bg-yellow-100 text-yellow-800",
room.room_status == :in_progress && "bg-green-100 text-green-800"
]}>
{room.room_status}
</span>
</:col>
<:action :let={room}>
<.link
navigate={~p"/multi-poker/#{room.room_id}"}
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500"
>
Join
</.link>
</:action>
</.table>
"""
end
attr(:current_user, :map, required: false)
def new_game(assigns) do
~H"""
<div class="rounded-xl border border-zinc-200 bg-white p-6 shadow-sm">
<h2 class="mb-2 text-xl font-semibold text-zinc-800">Can't find a game you like?</h2>
<%= if @current_user == nil do %>
<p class="mb-4 text-sm text-zinc-600">
Log in or create an account to make your own room.
</p>
<div class="flex gap-3">
<.link
navigate={~p"/users/log_in"}
class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-500"
>
Log in
</.link>
<.link
navigate={~p"/users/register"}
class="rounded-md border border-zinc-300 px-4 py-2 text-sm font-semibold text-zinc-700 hover:bg-zinc-50"
>
Register
</.link>
</div>
<% else %>
<p class="mb-4 text-sm text-zinc-600">
Start a new room and invite other players to join.
</p>
<button
type="button"
phx-click="create_room"
class="rounded-md bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-500"
>
Create Room
</button>
<% end %>
</div>
"""
end
end
Next we can set up the actual page. This will leverage the component.ex that we created earlier to keep the render very slim. The mount will only populate the Rooms if it is connected and then we have the event to create a room and the direct them to the created room.
I wanted to also take a min to go over the list_rooms and then the list_room_summaries. The first one uses Registry to pull all the pids and their ids from the Supervisor, the normal output for the return value is a list that looks like
[
{key, pid, value}
...
]
# so looking at the code for it we can see that we are using this syntax for the select()
{
{:"$1", :"$2", :"$3"}, # match
[], # guards
[{{:"$1", :"$2"}}] # return
}
defmodule GameSiteWeb.MultiPokerLive.Lobby do
use GameSiteWeb, :live_view
alias GameSite.MultiPoker.Room
alias GameSite.MultiPoker
alias GameSiteWeb.MultiPokerLive.Component
@registry GameSite.MultiPoker.RoomRegistry
@impl true
def render(assigns) do
~H"""
<Component.instructions />
<Component.live_games rooms={@rooms} />
<Component.new_game current_user={@current_user} />
"""
end
@impl true
def mount(_params, _session, socket) do
rooms =
if connected?(socket) do
list_room_summaries()
else
[]
end
{:ok, assign(socket, :rooms, rooms)}
end
@impl true
def handle_event("create_room", _params, socket) do
current_user = socket.assigns.current_user
{_, room_id} = MultiPoker.create_room(current_user.id)
socket =
assign(socket, :rooms, list_room_summaries())
{:noreply, redirect(socket, to: "/multi-poker/#{room_id}")}
end
defp list_rooms do
Registry.select(@registry, [
{
{:"$1", :"$2", :"$3"},
[],
[{{:"$1", :"$2"}}]
}
])
end
defp list_room_summaries do
list_rooms()
|> Enum.with_index(1)
|> Enum.map(fn {{room_id, pid}, display_id} ->
state = Room.get_state(pid)
%{
room_id: room_id,
player_count: map_size(state.players),
room_status: state.room_status,
display_id: display_id
}
end)
end
end
No we can set up a holder for the room where all the games will be played. We will fill this in later but I wanted you to have a page to render while we worked on the code.
defmodule GameSiteWeb.MultiPokerLive do
use GameSiteWeb, :live_view
def render(assigns) do
~H"""
The Games will go here.
"""
end
def mount(_params, _session, socket) do
{:ok, assign(socket, :room, nil)}
end
end
lastly we can set up the routes for the Lobby and Room This is pretty simple as it just makes the routes for the different modules to render and mount.
scope "/", GameSiteWeb do
pipe_through :browser
live_session :games,
on_mount: [{GameSiteWeb.UserAuth, :mount_current_user}] do
...
live "/multi-poker", MultiPokerLive.Lobby
live "/multi-poker/:room", MultiPokerLive
...
end
end