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

Allow broadcasting without rendering and broadcasting with custom attributes #427

Conversation

davidalejandroaguilar
Copy link
Contributor

@davidalejandroaguilar davidalejandroaguilar commented Feb 4, 2023

Description

Key Enhancements

This PR improves the flexibility of custom Turbo Stream actions by allowing:

  • Broadcasting without rendering
    • Some custom actions don't need any rendering at all because they're custom actions.
  • Enabling passing custom attributes via the Broadcastable concern.
    • These custom actions might need them.

Motivation

Thanks to the work on hotwired/turbo#479 and #373, custom turbo_stream actions can now be created. However, some limitations still exist (See here for an example of both current limitations):

  • Limited flexibility for passing extra attributes
    • Currently, extra attributes can only be passed via turbo_stream_action_tag and turbo_stream.
  • Inability to use custom actions via models (which use the Broadcastable concern)
    • Broadcasting custom actions to any streamable/model is not possible. It can only be done via a turbo_stream response on the controller or inside a view response (.turbo_stream.erb) for a single user.

Example

A custom stream action that doesn't need a template, only attributes (e.g. a toast):

StreamActions.toast = function () {
  const text = this.getAttribute('text');
  const duration = Number(this.getAttribute('duration') || 3000);

  const toast = Toastify({
    text,
    duration,
  });

  toast.showToast();
};

Create a module with custom broadcast methods, just to encapsulate them and include them wherever we want:

module TurboBroadcastable
  def toast(text, duration: 3000)
    broadcast_action_later(
      :toast,
      render: false, # New in this PR
      attributes: {  # New in this PR
        text: 
        duration:
      }
    )
  end
end

Including it on ApplicationRecord so that we can toast to any model/streamable (as long as they're listening through turbo_stream_from.

class ApplicationRecord < ActiveRecord::Base
  include TurboBroadcastable
end

To be called anywhere, such as from a controller:

class WebhooksController < ApplicationController
  def create
    # ...
    Current.user.toast("Webhook received")
  end
end

Notes

This change only affects broadcast_action_ and related action_ methods, since this is the method you'd use for custom actions.

The default Javascript implementations for actions such as prepend, append, etc. are provided by Turbo on StreamAction and thus passing attributes here is unnecessary, since they won't be used.

@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from a291589 to 4bae00a Compare February 4, 2023 07:13
@davidalejandroaguilar davidalejandroaguilar marked this pull request as ready for review February 4, 2023 07:14
@davidalejandroaguilar davidalejandroaguilar changed the title Allow broadcasting actions with no rendering, and passing stream tag attributes Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Feb 4, 2023
@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from 4bae00a to f135fd4 Compare February 4, 2023 23:05
@davidalejandroaguilar davidalejandroaguilar changed the title Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Review required: Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Feb 6, 2023
@davidalejandroaguilar davidalejandroaguilar changed the title Review required: Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Review Required: Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Feb 6, 2023
@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from 8b8abf4 to b1f16f0 Compare April 6, 2023 04:45
@davidalejandroaguilar davidalejandroaguilar changed the title Review Required: Allow broadcasting actions with no rendering, and passing stream tag attributes from Broadcastable Allow broadcasting custom actions from anywhere Apr 7, 2023
@davidalejandroaguilar davidalejandroaguilar changed the title Allow broadcasting custom actions from anywhere Enhanced custom Turbo Stream actions and broadcasting flexibility Apr 7, 2023
@davidalejandroaguilar davidalejandroaguilar changed the title Enhanced custom Turbo Stream actions and broadcasting flexibility Allow broadcasting without rendering and broadcasting with custom attributes Jun 15, 2023
@davidalejandroaguilar
Copy link
Contributor Author

davidalejandroaguilar commented Jun 15, 2023

@seanpdoyle @dhh Hey there, it's been 4 months since I opened this PR. I know y'all are super busy, but I wonder if you could skim through this PR. It's a really simple change that creates a lot of flexibility and power. Basically it's allowing:

  • Passing attributes to action broadcasts methods.
  • Passing render: false to broadcast methods to avoid rendering any HTML and just sending the broadcast over the wire.

I'm unfamiliar with the repo's rules or usual wait times, so I'm unsure if this PR has been silently rejected or not. Appreciate your time and efforts spent bringing this project to life.

@dhh
Copy link
Member

dhh commented Jun 18, 2023

I'm fine with the changes, but the test suite need to pass. Can you have a look at that?

@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from b1f16f0 to f45749f Compare June 22, 2023 06:34
@davidalejandroaguilar
Copy link
Contributor Author

@dhh Done, tests are passing now! Thank you.

@davidalejandroaguilar
Copy link
Contributor Author

@dhh I think I missed you by a week 🤦🏼‍♂️ , so just a friendly ping on this after 2 weeks.

Wouldn't like to pester with notifications though, so LMK if I just need to be patient.

@davidalejandroaguilar
Copy link
Contributor Author

I'm fine with the changes, but the test suite need to pass. Can you have a look at that?

@dhh Another friendly ping, test are passing 😃

@dhh
Copy link
Member

dhh commented Jul 25, 2023

Need to settle the commented out tests. Can't merge with that is it is.

@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from f45749f to baed1ef Compare August 8, 2023 01:37
@davidalejandroaguilar
Copy link
Contributor Author

davidalejandroaguilar commented Aug 8, 2023

@dhh All good now!

(Had to call @message.save! first on the broadcast later tests, the discard_on config on the job was silently failing) 🤦🏼‍♂️

@davidalejandroaguilar
Copy link
Contributor Author

@dhh Another month has passed, so I'm feeling free to ping you on this 😃

All tests are passing and ready to merge!

@davidalejandroaguilar
Copy link
Contributor Author

@dhh Pinging you on this again, hoping y'all are out of a cycle and have time to merge? 😃

P.S. I'm pinging on a ~3-4 weeks interval so as to not annoy you. Let me know if this is too frequent.

…attributes

In order to allow custom Turbo actions that don't require a template and work only via attributes passed to the turbo_stream_tag.
@davidalejandroaguilar davidalejandroaguilar force-pushed the allow-broadcasting-actions-without-rendering branch from baed1ef to 8e28570 Compare October 12, 2023 05:27
@davidalejandroaguilar
Copy link
Contributor Author

@dhh Pinging once more 😶

Another cool usage thanks to this PR that I just added to my app. Printing from anywhere:

# From anywhere
Current.venue.print(data)

Thanks to:

module TurboBroadcastable
  def print(data:, to: self)
    broadcast_action_later_to(
      to,
      action: :print,
      render: false, # New in this PR
      attributes: {  # New in this PR
        data: data.to_json
      }
    )
  end
end
class ApplicationRecord < ActiveRecord::Base
  include TurboBroadcastable
end
StreamActions.print = async function () {
  const printerService = new PrinterService();
  await printerService.print(this.getAttribute("data"));
}

@dhh dhh merged commit 1684d35 into hotwired:main Oct 19, 2023
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants