This may not be the answer you are looking for, but it’s somewhat on topic as it involves handling Friendships/Requests in another way.
If you create a Requests, Friends and User_Relations table. you can store multiple states between two users in one location for each user and keep the Requests and Friends as simple joins.
As long as you run the transactions through Multi, you should be able to keep the User_Relations in sync with the actual Request/Friend records.
User_Relations table.
defmodule Phxie.Schema.User_Relation do
use Ecto.Schema
import Ecto.Changeset
alias Phxie.Schema.{Board, User}
schema "user_relations" do
belongs_to :user, User
belongs_to :current_user, User, foreign_key: :current_user_id
field :blocking, :boolean
field :blocked, :boolean
field :following, :boolean
field :friends, :boolean
field :sent_request, :boolean
field :rec_request, :boolean
timestamps()
end
@id [:current_user_id, :user_id]
@state [:blocking, :blocked, :following, :friends, :sent_request, :rec_request]
@doc false
def changeset(follow, attrs) do
follow
|> cast(attrs, @id ++ @state)
end
end
Button example that will display different text based on whether users are friends, blocking one another, sent a request or received one. If no relation exists the button displays add friend.
<.friend_btn
{@menu_btn}
type={:user}
mods="br-0"
active_class="btn-s"
active={@post_relation.friends}
name={@post["user_info"]["username"]}
disabled_if={@post_relation.blocked}
text={friend_relation_text(@post_relation)}
/>
def friend_relation_text(user_relations) do
cond do
user_relations.blocked -> "Blocked"
user_relations.blocking -> "Blocked"
user_relations.sent_request -> "Cancel Request"
user_relations.rec_request -> "Decline Request"
user_relations.friends -> "Remove Friend"
true -> "Add Friend"
end
end
Requests example
defmodule Phxie.Requests do
import Ecto.Query, warn: false
import Phxie.{EctoHelpers, GetHelpers}
import PhxieWeb.AuthHelpers
alias Ecto.Multi
alias Phxie.{Repo, Blocks, Friends, User_Relations}
alias Phxie.Schema.{Block, Friend, Notification, Notification_Setting, Request, User, User_Setting, User_Relation}
@friend_request "Friend Request"
##########################
#### Friend Request ####
##########################
#######################################
#### Friend Request : Auth Check ####
#######################################
def handle_friend_request(params, socket) do
user = get_user!(params["name"])
current_user = socket.assigns.current_user
with :ok <- check_authentication("user", current_user, user) do
action_select(current_user, user)
else
error -> error
end
end
##########################################
#### Friend Request : Action Select ####
##########################################
defp action_select(current_user, user) do
user_relations = User_Relations.get_user_relations("user", current_user, user)
case user_relations do
nil -> create_friend_request_and_relations(current_user.id, user.id)
%User_Relation{blocked: true} -> {:error, :blocked_by_user, ["You are blocked by #{user.username}."]}
%User_Relation{blocking: true} -> {:error, :blocking_user, ["You are blocking #{user.username}."]}
%User_Relation{sent_request: true} -> delete_friend_request(current_user.id, user.id)
%User_Relation{rec_request: true} -> delete_friend_request(user.id, current_user.id)
%User_Relation{friends: true} -> nil
_ -> create_friend_request(current_user.id, user.id)
end
end
################################################
#### Friend Request : Create and Relation ####
################################################
defp create_friend_request_and_relations(current_user_id, user_id) do
settings = ecto_get(Notification_Setting, user_id, [:user])
if settings.friend_request do
Multi.new()
|> Multi.insert(:request, %Request{current_user_id: current_user_id, user_id: user_id, type: @friend_request})
|> Multi.insert(:user_relations_1, %User_Relation{current_user_id: current_user_id, user_id: user_id, sent_request: true})
|> Multi.insert(:user_relations_2, %User_Relation{current_user_id: user_id, user_id: current_user_id, rec_request: true})
|> Repo.transaction()
{:ok, :request_created}
else
{:error, :requests_are_disabled, ["#{settings.user.username} has friend requests disabled."]}
end
end
###################################
#### Friend Request : Create ####
###################################
defp create_friend_request(current_user_id, user_id) do
settings = ecto_get(Notification_Setting, user_id, [:user])
if settings.friend_request do
Multi.new()
|> Multi.insert(:request, %Request{current_user_id: current_user_id, user_id: user_id, type: @friend_request})
|> Multi.update_all(:user_relations_1, from(r in User_Relation, where: r.current_user_id == ^current_user_id and r.user_id == ^user_id), set: [sent_request: true])
|> Multi.update_all(:user_relations_2, from(r in User_Relation, where: r.current_user_id == ^user_id and r.user_id == ^current_user_id), set: [rec_request: true])
|> Repo.transaction()
{:ok, :request_created}
else
{:error, :requests_are_disabled, ["#{settings.user.username} has friend requests disabled."]}
end
end
###################################
#### Friend Request : Delete ####
###################################
def delete_friend_request(current_user_id, user_id) do
request = ecto_get_by(Request, %{current_user_id: current_user_id, user_id: user_id, type: @friend_request})
try do
Multi.new()
|> Multi.delete(:delete, request)
|> Multi.update_all(:user_relations_1, from(r in User_Relation, where: r.current_user_id == ^current_user_id and r.user_id == ^user_id), set: [sent_request: false])
|> Multi.update_all(:user_relations_2, from(r in User_Relation, where: r.current_user_id == ^user_id and r.user_id == ^current_user_id), set: [rec_request: false])
|> Repo.transaction()
{:ok, :request_deleted}
rescue
Ecto.StaleEntryError ->
{:error, :stale_entry, ["Could not delete request, the request may have already been accepted or deleted."]}
end
end
end