jonathan-s / django-sockpuppet

Build reactive applications with the django tooling you already know and love.
https://github.com/jonathan-s/django-sockpuppet
MIT License
450 stars 22 forks source link

Can't dispatch event back to the user using Channel #81

Closed Ryner01 closed 3 years ago

Ryner01 commented 3 years ago

Hey so first of all thanks for the awesome library. I am trying it our right now in a small production project and so far I am very happy with it. :)

I am not even sure if this is actually a bug report or I do just not understand how to use the event system but here it goes.

Describe the bug

Following the example, I wanted to dispatch_event in a reflex that would then in turn call some javascript (let's say change the query string using the history API). Here is the code example:

channel = Channel(self.consumer.scope['session'].session_key)
channel.dispatch_event({
    'name': "test",
    'detail': {
        'test': 'test'
    },
})
channel.broadcast()

The problem is that while the event is received (in the chrome WS network tab) nothing actually happens in the browser. The event is just not fired.

But if I create a new subscription and then dispatch an event on that channel then it actually works. But this seems to be designed more for a 'group' kind of interaction (e.g chat room). It would probably work if I just stored some kind of user_id somewhere in the template and then get it in JS. Is that the correct approach for client <- server communication (one-to-one)?

consumer.subscriptions.create('ChatChannel', {
  received (data) {
    if (data.cableReady) CableReady.perform(data.operations)
  }
})

Thanks just wanted to verify that I am doing thing correctly and not missing anything.

jonathan-s commented 3 years ago

hi @Ryner01, happy to hear that it is being used!

Could you please try to change how the Channel class is initialized?

channel = Channel(self.consumer.scope['session'].session_key, identifier='StimulusReflex::Channel')

StimulusReflex::Channel is the default identifier which the frontend automatically is subscribed to. Ie it would be the equivalent to ChatChannel.

If that doesn't work, I'll have a deeper look, so let me know.

Ryner01 commented 3 years ago

Hello thanks for the help! You guided me on the right path. To get it to work I had to initialize the channel like this:

channel = Channel(self.consumer.scope['session'].session_key, identifier='{"channel":"StimulusReflex::Channel"}')

Since I noticed that morph events all have identifiers as a dict with the channel name. Maybe it would be good to have some shorthand property in base reflex class. Let's say self.channel which you could use to send the message directly. I can submit a PR once I get more comfortable with the lib.

Also just to understand what is the difference between the identifier and channel name (session key)? I presume that the session key is in this case used by Django channels to actually find the relevant socket(s) from some channel layer (like redis) and the identifier is then used once the message is actually received. Where based on the identifier stimulus reflex JS will do some magic and call the methods connected to the identifier right?

Thats what this is for I think:

  received (data) {
    if (data.cableReady) CableReady.perform(data.operations)
  }
jonathan-s commented 3 years ago

Also just to understand what is the difference between the identifier and channel name (session key)?

The identifier is used to find the 'subscription' in the frontend. When you create the subscription like this.

consumer.subscriptions.create('ChatChannel', {
  received (data) {
    if (data.cableReady) CableReady.perform(data.operations)
  }
})

When you create a subscription above the identifier is codified as a stringified object as you noticed. When you create a Channel in django with only the name the identifier gets codified the same. (https://github.com/jonathan-s/django-sockpuppet/blob/master/sockpuppet/channel.py#L22). When you use the session key there is only ever a single person that will be subscribed to that channel group. Whereas if you create a subscription with a name several people will be able to get updates from that channel.

By default the stimulus reflex javascript sets up a frontend subscription with the identifier you mentioned here -> https://github.com/hopsoft/stimulus_reflex/blob/68f2b55edefe50922e1504e016b79e361add9d19/javascript/stimulus_reflex.js#L346-L349

Does that make sense?

I hope that in the future I will be able re-use parts of the library so it's less tied to the conventions of the rails backend. I created an issue about that here -> https://github.com/hopsoft/stimulus_reflex/issues/333

Happy to receive any PRs. A first draft of the code to see if it makes sense is always helpful.

jonathan-s commented 3 years ago

Btw, if you've got a better name for Channel, I'm happy to recieve any suggestions. It's kind of easy to confuse with django-channels when you try to explain things.

Ryner01 commented 3 years ago

Thanks, it's clear for me now.

Unfortunately, I don't have a better name right now. I want to spend some time with the library to really understand it before recommending any changes. But personally, for me, it was clear right away that Channel is not the same thing as a Django channel.

But I understand that without context it's hard to know what channels are being talked about. Especially since you need to setup some actual Django channel stuff like layers etc in settings.

In StimusReflex they call their version CableReady but I guess that this is not a good name for django. Since not all CableReady features are implemented and technically it's an implementation detail.

jonathan-s commented 3 years ago

Great, I'll close this issue as it's been resolved then :).