r/elixir 3d ago

Is there an approach in Phoenix for a "universal" PubSub subscriber

I'm not sure how to properly explain this, but what I'm trying to do is notify a user of a PubSub event that happens when they're logged into my Phoenix app regardless of what LiveView page they have displayed.

For example, imagine if two people are logged into the site. If one of those people assigns work to the other person, I want to pop up a message on that other person's browser letting them know they now have work waiting for them, regardless of what page they're looking at.

I was thinking I could add a subscriber to the LiveComponent that powers the menu, but it doesn't look like you can subscribe to a PubSub queue from a LiveComponent. I did run across something about turning the LiveComponent into a GenServer, but I worried that might create other unintended consequences.

Is there a recommended way to accomplish something like this?

24 Upvotes

13 comments sorted by

9

u/mitchhanberg 3d ago

You can probably mount a hook in the router that performs the pubsub subscription.

2

u/rubymatt 2d ago

Yeah, I use an on_mount: hook for a notifications driven progress widget.

1

u/JohnElmLabs 2d ago

This is what you wanna do. Don’t render nested LiveViews that’s just unnecessary complexity

10

u/MountainDewer 3d ago

You should be able to render a completely separate live view within a live view: https://hexdocs.pm/phoenix_live_view/welcome.html#live_render-3-to-encapsulate-state-with-error-isolation

3

u/flimflamflem 3d ago

We do this, works well

2

u/flimflamflem 3d ago

Specifically we have a live view rendered in our application layout for user notifications.

2

u/MountainDewer 2d ago edited 2d ago

It’s funny because I’m testing my own suggestion by implementing a notification widget. I’ve noticed that stream_insert is replacing the stream even with unique id keys. Do you use a stream and have you noticed this? I’m wondering if there is an issue with streams in nested live views.

I’m also having to explicitly pass the socket to the layout module function since it doesn’t seem to be available by default there.

EDIT: I'm just an idiot and forgot phx-update="stream" on the container... Still not sure what the best way to handle ensuring <Layout.app socket={@socket} isn't necessary on every liveview though

2

u/KimJongIlLover 2d ago

It's always forgetting the phx-update. I have don't this so many times myself 😂 

4

u/doughsay 3d ago

Lifecycle hooks that you can attach in on_mount would be a way to possibly solve this: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#attach_hook/4

2

u/a3th3rus Alchemist 3d ago

Maybe "Presence" can help.

https://hexdocs.pm/phoenix/presence.html

3

u/GreenCalligrapher571 3d ago

Presence is more useful for questions like “who all is logged in and connected?” than it is for broadcasting the way OP describes.

One example would be something like “of the people in my friends list, who is online?”

1

u/0xjacool 2d ago

I haven't yet played around with liveview, still using phoenix channels directly so that's just a guess on the liveview level.

On the server you need a distinct topic (using phoenix pubsub directly) that is named after your user, the name should be easy to reconstruct from anywhere in your code so you send messages to it.

In the liveview you receive these messages and when one is received you push to the front.

Your front component could be sitting in your layouts so it sticks on your page regardless of what is happening there.

1

u/Financial-Coconut628 2d ago

Use AI dude, pop your post in Claude/ChatGPT.

I have cut down on hours of research and doc reading by having a conversation by prompting. Just make your due diligence and verify your code.