Skip to content

Commit

Permalink
Add phoenix channel/socket for desktop client
Browse files Browse the repository at this point in the history
  • Loading branch information
starcraft66 committed Sep 12, 2023
1 parent 6deb933 commit 4d4a5db
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 43 deletions.
64 changes: 64 additions & 0 deletions assets/js/desktop_client_socket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// NOTE: The contents of this file will only be executed if
// you uncomment its entry in "assets/js/app.js".

// Bring in Phoenix channels client library:
import {Socket} from "phoenix"

// And connect to the path in "lib/lanpartyseating_web/endpoint.ex". We pass the
// token for authentication. Read below how it should be used.
let socket = new Socket("/socket", {params: {token: window.userToken}})

// When you connect, you'll often need to authenticate the client.
// For example, imagine you have an authentication plug, `MyAuth`,
// which authenticates the session and assigns a `:current_user`.
// If the current user exists you can assign the user's token in
// the connection for use in the layout.
//
// In your "lib/lanpartyseating_web/router.ex":
//
// pipeline :browser do
// ...
// plug MyAuth
// plug :put_user_token
// end
//
// defp put_user_token(conn, _) do
// if current_user = conn.assigns[:current_user] do
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
// assign(conn, :user_token, token)
// else
// conn
// end
// end
//
// Now you need to pass this token to JavaScript. You can do so
// inside a script tag in "lib/lanpartyseating_web/templates/layout/app.html.heex":
//
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
//
// You will need to verify the user token in the "connect/3" function
// in "lib/lanpartyseating_web/channels/user_socket.ex":
//
// def connect(%{"token" => token}, socket, _connect_info) do
// # max_age: 1209600 is equivalent to two weeks in seconds
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1_209_600) do
// {:ok, user_id} ->
// {:ok, assign(socket, :user, user_id)}
//
// {:error, reason} ->
// :error
// end
// end
//
// Finally, connect to the socket:
socket.connect()

// Now that you are connected, you can join channels with a topic.
// Let's assume you have a channel with a topic named `room` and the
// subtopic is its id - in this case 42:
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) })

export default socket
29 changes: 26 additions & 3 deletions lib/lanpartyseating/logic/reservation_logic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Lanpartyseating.ReservationLogic do
alias Lanpartyseating.StationLogic, as: StationLogic
alias Lanpartyseating.BadgesLogic, as: BadgesLogic
alias Lanpartyseating.PubSub, as: PubSub
alias LanpartyseatingWeb.Endpoint, as: Endpoint

def create_reservation(station_number, duration, uid) do
if uid == "" do
Expand Down Expand Up @@ -48,6 +49,17 @@ defmodule Lanpartyseating.ReservationLogic do
{:stations, StationLogic.get_all_stations(now)}
)

Endpoint.broadcast!(
"desktop:all",
"new_reservation",
%{
station_number: station_number,
# reservation: updated
}
)

Logger.debug("Broadcasted station status change to occupied")

DynamicSupervisor.start_child(
Lanpartyseating.ExpirationTaskSupervisor,
{Lanpartyseating.Tasks.ExpireReservation, {end_time, updated.id}}
Expand All @@ -67,7 +79,9 @@ defmodule Lanpartyseating.ReservationLogic do
def cancel_reservation(id, reason) do
from(r in Reservation,
where: r.station_id == ^id,
where: is_nil(r.deleted_at)
where: is_nil(r.deleted_at),
join: s in assoc(r, :station),
preload: [station: s]
)
# There should, in theory, only be one non-deleted reservation for a station but let's clean up
# if that turns out not to be the case.
Expand All @@ -80,16 +94,25 @@ defmodule Lanpartyseating.ReservationLogic do
)

case Repo.update(reservation) do
{:ok, struct} ->
{:ok, reservation} ->
GenServer.cast(:"expire_reservation_#{res.id}", :terminate)

Endpoint.broadcast!(
"desktop:all",
"cancel_reservation",
%{
station_number: reservation.station.station_number,
# reservation: updated
}
)

Phoenix.PubSub.broadcast(
PubSub,
"station_update",
{:stations, StationLogic.get_all_stations()}
)

struct
reservation
# let it crash
# {:error, _} -> ...
end
Expand Down
23 changes: 22 additions & 1 deletion lib/lanpartyseating/tasks/start_tournament.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
defmodule Lanpartyseating.Tasks.StartTournament do
use GenServer, restart: :transient
import Ecto.Query
require Logger
alias Lanpartyseating.Repo
alias Lanpartyseating.TournamentReservation
alias LanpartyseatingWeb.Endpoint
alias Lanpartyseating.StationLogic
alias Lanpartyseating.PubSub, as: PubSub
alias Lanpartyseating.PubSub

def start_link(arg) do
{_, tournament_id} = arg
Expand Down Expand Up @@ -33,6 +37,23 @@ defmodule Lanpartyseating.Tasks.StartTournament do
{:stations, StationLogic.get_all_stations()}
)

from(r in TournamentReservation,
where: r.tournament_id == ^tournament_id,
join: s in assoc(r, :station),
preload: [station: s]
)
|> Repo.all()
|> Enum.map(fn res ->
Endpoint.broadcast(
"desktop:all",
"tournament_start",
%{
station_number: res.station.station_number,
}
)
end)


{:stop, :normal, tournament_id}
end
end
13 changes: 13 additions & 0 deletions lib/lanpartyseating_web/channels/desktop_channel.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule LanpartyseatingWeb.DesktopChannel do
use Phoenix.Channel
require Logger

def join("desktop:all", _message, socket) do
Logger.debug("Client joined desktop:all")
{:ok, socket}
end

def join("desktop:" <> _hostname, _params, _socket) do
{:error, %{reason: "unauthorized"}}
end
end
56 changes: 56 additions & 0 deletions lib/lanpartyseating_web/channels/desktop_client_socket.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
defmodule LanpartyseatingWeb.DesktopClientSocket do
use Phoenix.Socket
require Logger

# A Socket handler
#
# It's possible to control the websocket connection and
# assign values that can be accessed by your channel topics.

## Channels
# Uncomment the following line to define a "room:*" topic
# pointing to the `LanpartyseatingWeb.RoomChannel`:
#
channel "desktop:*", LanpartyseatingWeb.DesktopChannel
#
# To create a channel file, use the mix task:
#
# mix phx.gen.channel Room
#
# See the [`Channels guide`](https://hexdocs.pm/phoenix/channels.html)
# for further details.


# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error` or `{:error, term}`. To control the
# response the client receives in that case, [define an error handler in the
# websocket
# configuration](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#socket/3-websocket-configuration).
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
@impl true
def connect(_params, socket, _connect_info) do
Logger.debug("Client connected to desktop socket")
{:ok, socket}
end

# Socket id's are topics that allow you to identify all sockets for a given user:
#
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
#
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
# Elixir.LanpartyseatingWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
@impl true
def id(_socket), do: nil
end
37 changes: 0 additions & 37 deletions lib/lanpartyseating_web/channels/user_socket.ex

This file was deleted.

5 changes: 3 additions & 2 deletions lib/lanpartyseating_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ defmodule LanpartyseatingWeb.Endpoint do
signing_salt: "F6uh1uE5"
]

socket "/socket", LanpartyseatingWeb.UserSocket,
websocket: true # or list of options
socket "/desktop", LanpartyseatingWeb.DesktopClientSocket,
websocket: true,
longpoll: false

socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [session: @session_options]]
Expand Down

0 comments on commit 4d4a5db

Please sign in to comment.