Skip to content

Commit

Permalink
feat(gloabl): role management in confs
Browse files Browse the repository at this point in the history
  • Loading branch information
Irere123 committed Aug 21, 2024
1 parent 42900cd commit b7ef38a
Show file tree
Hide file tree
Showing 14 changed files with 460 additions and 57 deletions.
41 changes: 41 additions & 0 deletions api/lib/breeze/routes/v1/confs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,45 @@ defmodule Breeze.Routes.V1.Confs do
conn
|> send_resp(200, Jason.encode!(conf_id))
end

post "/add-speaker" do
%Plug.Conn{params: %{"userId" => user_id_to_make_speaker}} = conn

user_id = conn.assigns.user_id

Spek.Conf.set_role(user_id_to_make_speaker, :speaker, by: user_id)

conn
|> send_resp(200, Jason.encode!(%{success: true}))
end

post "/ask-to-speak" do
user_id = conn.assigns.user_id

Spek.Conf.set_role(user_id, :hand_raised, by: user_id)

conn
|> send_resp(200, Jason.encode!(%{success: true}))
end

post "/set-listener" do
%Plug.Conn{params: %{"userId" => user_id_make_listener}} = conn

user_id = conn.assigns.user_id

Spek.Conf.set_role(user_id_make_listener, :listener, by: user_id)

conn
|> send_resp(200, Jason.encode!("Hello world"))
end

post "/:id/change-conf-creator" do
conn
|> send_resp(200, Jason.encode!("hello world"))
end

post "/:id/change-mod-status" do
conn
|> send_resp(200, Jason.encode!("Hello world"))
end
end
143 changes: 111 additions & 32 deletions api/lib/spek/conf.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,57 +11,125 @@ defmodule Spek.Conf do
end
end

defp internal_set_listener(user_id_to_make_listener, conf_id) do
ConfPermissions.make_listener(user_id_to_make_listener, conf_id)
ConfSession.remove_speaker(conf_id, user_id_to_make_listener)
####################################################################
## ROLE

@doc """
sets the role of the user in the conf that they're in. Authorization
to do so is pulled from the options `:by` keyword.
"""
def set_role(user_id, role, opts) do
conf_id = Users.get_current_conf_id(user_id)

case role do
_ when is_nil(conf_id) ->
:noop

:listener ->
set_listener(conf_id, user_id, opts[:by])

:speaker ->
set_speaker(conf_id, user_id, opts[:by])

:hand_raised ->
set_raised_hand(conf_id, user_id, opts[:by])
end
end

####################################################################
## listener

# you are always allowed to set yourself as listener
defp set_listener(conf_id, user_id, user_id) do
internal_set_listener(user_id, conf_id)
end

defp set_listener(conf_id, user_id, setter_id) do
# TODO: refactor this to be simpler. The list of
# creators and mods should be in the preloads of the conf.
with {auth, _} <- Confs.get_conf_status(setter_id),
{role, _} <- Confs.get_conf_status(user_id) do
if auth == :creator or (auth == :mod and role not in [:creator, :mod]) do
internal_set_listener(user_id, conf_id)
end
end
end

defp internal_set_listener(user_id, conf_id) do
ConfPermissions.make_listener(user_id, conf_id)
Pulse.ConfSession.remove_speaker(conf_id, user_id)
end

def set_listener(user_id, user_id_to_set_listener) do
if user_id == user_id_to_set_listener do
internal_set_listener(
user_id_to_set_listener,
Users.get_current_conf_id(user_id_to_set_listener)
)
####################################################################
## speaker

defp set_speaker(nil, _, _), do: :noop

defp set_speaker(conf_id, user_id, setter_id) do
if not ConfPermissions.asked_to_speak?(user_id, conf_id) do
:noop
else
{status, conf} = Confs.get_conf_status(user_id_to_set_listener)
case Confs.get_conf_status(setter_id) do
{_, nil} ->
:noop

is_creator = user_id_to_set_listener == not is_nil(conf)
{:mod, _} ->
internal_set_speaker(user_id, conf_id)

if not is_creator and (status == :creator or status == :mod) do
internal_set_listener(
user_id_to_set_listener,
Users.get_current_conf_id(user_id_to_set_listener)
)
{:creator, _} ->
internal_set_speaker(user_id, conf_id)

{_, _} ->
:noop
end
end
end

# only you can raise your own hand
defp set_raised_hand(conf_id, user_id, setter_id) do
if user_id == setter_id do
if Pulse.ConfSession.get(conf_id, :auto_speaker) do
internal_set_speaker(user_id, conf_id)
else
case ConfPermissions.ask_to_speak(user_id, conf_id) do
{:ok, %{is_speaker: true}} ->
internal_set_speaker(user_id, conf_id)

_ ->
Pulse.ConfSession.broadcast_ws(
conf_id,
%{
op: "hand_raised",
d: %{userId: user_id, confId: conf_id}
}
)
end
end
end
end

def internal_set_speaker(user_id_to_make_speaker, conf_id, true) do
case ConfPermissions.set_speaker(user_id_to_make_speaker, conf_id, true) do
@spec internal_set_speaker(any, any) :: nil | :ok | {:err, {:error, :not_found}}
defp internal_set_speaker(user_id, conf_id) do
case ConfPermissions.set_speaker(user_id, conf_id, true) do
{:ok, _} ->
ConfSession.add_speaker(
# kind of horrible to have to make a double genserver call
# here, we'll have to think about how this works (who owns muting)
Pulse.ConfSession.add_speaker(
conf_id,
user_id_to_make_speaker,
Pulse.UserSession.get(user_id_to_make_speaker, :muted),
Pulse.UserSession.get(user_id_to_make_speaker, :deafened)
user_id,
Pulse.UserSession.get(user_id, :muted),
Pulse.UserSession.get(user_id, :deafened)
)

error ->
{:err, error}
err ->
{:err, err}
end
catch
_, _ ->
{:error, "conf not found"}
end

@spec make_speaker(any(), any()) :: none() | no_return()
def make_speaker(user_id, user_id_to_make_speaker) do
with {status, conf} when status in [:creator, :mod] <- Confs.get_conf_status(user_id),
true <- ConfPermissions.asked_to_speak?(user_id_to_make_speaker, conf.id) do
internal_set_speaker(user_id_to_make_speaker, conf.id, true)
end
end

def join_voice_conf(user_id, conf, speaker? \\ nil) do
speaker? =
if is_nil(speaker?),
Expand Down Expand Up @@ -217,4 +285,15 @@ defmodule Spek.Conf do
catch
_, _ -> {:error, "that conf does not exist"}
end

def change_mod(user_id, user_id_to_change, value) do
if conf = Confs.get_conf_by_creator_id(user_id) do
ConfPermissions.set_is_mod(user_id_to_change, conf.id, value)

Pulse.ConfSession.broadcast_ws(conf.id, %{
op: "mod_changed",
d: %{confId: conf.id, userId: user_id_to_change}
})
end
end
end
26 changes: 12 additions & 14 deletions api/lib/telescope/access/confs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -106,25 +106,23 @@ defmodule Telescope.Access.Confs do
|> Repo.one()
end

@spec get_conf_status(any()) :: none() | no_return()
def get_conf_status(user_id) do
case Users.get_current_conf(user_id) do
nil ->
conf = Users.get_current_conf(user_id)

cond do
is_nil(conf) ->
{nil, nil}

%{} = conf when conf.creator_id == user_id ->
conf.creator_id == user_id ->
{:creator, conf}

%{} = conf ->
status =
case ConfPermissions.get_conf_perms(user_id, conf.id) do
%{is_mod: true} -> :mod
%{is_speaker: true} -> :speaker
%{asked_to_speak: true} -> :asked_to_speak
_ -> :listener
end

{status, conf}
true ->
{case ConfPermissions.get_conf_perms(user_id, conf.id) do
%{is_mod: true} -> :mod
%{is_speaker: true} -> :speaker
%{asked_to_speak: true} -> :asked_to_speak
_ -> :listener
end, conf}
end
end
end
2 changes: 1 addition & 1 deletion api/lib/telescope/access/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ defmodule Telescope.Access.Users do
end

def get_current_conf(user_id) do
conf_id = get_current_conf(user_id)
conf_id = get_current_conf_id(user_id)

case conf_id do
nil -> nil
Expand Down
7 changes: 5 additions & 2 deletions api/lib/telescope/conf_permissions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ defmodule Telescope.ConfPermissions do
end

def get_conf_perms(user_id, conf_id) do
from(cp in ConfPermission, where: cp.user_id == ^user_id and cp.conf_id == ^conf_id, limit: 1)
from(cp in ConfPermission,
where: cp.user_id == ^user_id and cp.conf_id == ^conf_id,
limit: 1
)
|> Repo.one()
end

Expand All @@ -61,7 +64,7 @@ defmodule Telescope.ConfPermissions do

def set_speaker(user_id, conf_id, speaker?, returning \\ false) do
upsert(
%{conf_id: conf_id, userId: user_id, is_speaker: speaker?},
%{conf_id: conf_id, user_id: user_id, is_speaker: speaker?},
[is_speaker: speaker?],
returning
)
Expand Down
1 change: 0 additions & 1 deletion api/lib/telescope/confs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ defmodule Telescope.Confs do
defdelegate get_a_user_for_conf(conf_id), to: Telescope.Access.Confs
defdelegate owner?(conf_id, user_id), to: Telescope.Access.Confs
defdelegate search_name(start_of_name), to: Telescope.Access.Confs
@spec get_conf_status(any()) :: none() | no_return()
defdelegate get_conf_status(user_id), to: Telescope.Access.Confs

# MUTATIONS functions
Expand Down
2 changes: 1 addition & 1 deletion api/lib/telescope/schemas/conf_permission.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule Telescope.Schemas.ConfPermission do

def insert_changeset(confPerm, attrs) do
confPerm
|> cast(attrs, [:user_id, :conf_id, :is_spek, :is_mod, :asked_to_speak])
|> cast(attrs, [:user_id, :conf_id, :is_speaker, :is_mod, :asked_to_speak])
|> validate_required([:user_id, :conf_id])
end
end
2 changes: 2 additions & 0 deletions apps/web/src/app/(authenticated)/conf/[id]/controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useCurrentConfInfo } from "@/hooks/useCurrentConfInfo";
import { useSetMute } from "@/hooks/useSetMute";
import { useDeafStore } from "@/stores/useDeafStore";
import { useSetDeafen } from "@/hooks/useSetDeafen";
import { RoomUserPreviewModal } from "@/components/room/RoomUserPreviewModal";

interface Props {
id: string;
Expand Down Expand Up @@ -46,6 +47,7 @@ export const ConfController: React.FC<Props> = ({ id }) => {
with <span className="text-primary-100">{roomCreator?.username}</span>
</p>
</div>
<RoomUserPreviewModal {...data} />
<div className="flex flex-col flex-1">
<RoomUsersPanel {...data} />
</div>
Expand Down
11 changes: 7 additions & 4 deletions apps/web/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { queryClient } from "@/utils/queryClient";
import { WebSocketProvider } from "@/contexts/WebSocketContext";
import { MainWsHandlerProvider } from "@/hooks/useMainWsHandler";
import { WebRTC } from "@/webrtc/WebRTC";
import { UserPreviewContextProvider } from "@/contexts/UserPreviewContext";

interface Props {
children?: React.ReactNode;
Expand All @@ -24,10 +25,12 @@ export const Providers: React.FC<Props> = ({ children }) => {
<DataFetchingContextProvider>
<MainWsHandlerProvider>
<TooltipProvider>
{children}
<ConfirmModal />
<Toaster />
<WebRTC />
<UserPreviewContextProvider>
{children}
<ConfirmModal />
<Toaster />
<WebRTC />
</UserPreviewContextProvider>
</TooltipProvider>
</MainWsHandlerProvider>
</DataFetchingContextProvider>
Expand Down
Loading

0 comments on commit b7ef38a

Please sign in to comment.