Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Going further: set up a private chat between users #83

Open
ndrean opened this issue Nov 27, 2022 · 2 comments
Open

Going further: set up a private chat between users #83

ndrean opened this issue Nov 27, 2022 · 2 comments

Comments

@ndrean
Copy link

ndrean commented Nov 27, 2022

image

Question: How do you create a private chat between two users?

I did not find a way to get all subscribers.

When you look at the code for Phoenix.PubSub.subscribe:

# Phoenix.PubSub.subscribe
def subscribe(pubsub, topic, opts \\ [])
      when is_atom(pubsub) and is_binary(topic) and is_list(opts) do
   case Registry.register(pubsub, topic, opts[:metadata]) do
  {:ok, _} -> :ok

# Phoenix.PubSub.unsubscribe
def unsubscribe(pubsub, topic) when is_atom(pubsub) and is_binary(topic) do
  Registry.unregister(pubsub, topic)

Dwyl/I use Phoenix.Endpoint instead of Phoenix.PubSub and the PubSub is started with:

{Phoenix.PubSub, name: MyApp.PubSub},

and the standard configuration is:

config :live_map, MyAppWeb.Endpoint,
  pubsub_server: MyApp.PubSub,

We don't use the "atom" form" . Does this start a registry?

I don't see anything:

iex> Registry.count(MyAppPubSub)
# 12 ????
iex> Registry.keys(MyApp.PubSub, self())
# []  ?????
@ndrean
Copy link
Author

ndrean commented Nov 27, 2022

Sidenote (not from me)

PubSub: allow processes to communicate with each other between a node or multiple nodes. This is important in a distributed world, where server A has a message for a user that’s connected to server B. The message is dispatched over PubSub and the process on server B picks it up for delivery to the user.

Tracker: Tracker is a CRDT-based library to keep a list of information in-sync across a cluster. This information could really be anything, although it’s commonly something related to connected users or other ephemeral types of information. When you write an implementation for Tracker, you have to provide some callbacks like handle_diff 14.

Presence: Implementation of Tracker that works out of the box with Phoenix Channels. You don’t really have to do anything for this other than tie your channels to a Presence module—it’s all built for you already. There is also some JS provided by Phoenix framework for handling Presence on the frontend as well.

Presence is really well suited when you want the list of presence information to be sent to your clients. The quintessential example of this is “who’s online in this chat room” type of information. If you don’t want to broadcast the presence information to clients, then Tracker may be better because you will avoid doing excess work (costs CPU) that you don’t need to do.

PubSub is really separate here. It enables communication, but it doesn’t do anything regarding keeping stateful information distributed in your cluster. It’s not as easy as “broadcast a message that someone joined the channel” because distributed systems introduce a whole host of problems (what happens if a server disconnects, there is a networking issue, etc). Tracker (and therefore Presence) is designed to work in these real-world conditions.

@ndrean
Copy link
Author

ndrean commented Nov 29, 2022

A drawing is easier to follow I believe: set up N(N+1)/2 one-to-one channels (subscriptions) between users. You need to monitor subscriptions, meaning unsubscribe on deconnection, otherwise you have multiple rendering.

Untitled-2022-11-28-1733(2)

The idea is when a user mounts, he "pre"-build N-1 channels with every connected user, and the topic follows the convention #{user_id}-#{another_user_id}".
This way, we have N-1 one-to-one connections. A user can now push a notification to another user and send a private message.
Tested with 3 users (thanks to Dwyl's excellent login tools)

However, I wanted to be able to check the existing subscriptions.

Edit 1: you can indeed get all the topics a user subscribed to with:

Registry.keys(MyApp.PubSub, self())

(the error was MyAppWeb.Pubsub instead of MyApp.PubSub .... ^^)

Edit2: I learnt that you can't pattern match on an empty map. Use guard clause
Example: to get get "leaves" events, do:

def handle_info(%{event: "presence", payload: %{joins: joins}, socket) when joins == %{}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant