srcrip / live_toast

A beautiful drop-in replacement for the Phoenix Flash system.
https://toast.src.rip
MIT License
193 stars 17 forks source link

Is there any better way to pass assigns to the :action option? #6

Open dfalling opened 6 months ago

dfalling commented 6 months ago

Hi, first off thank you so much for this lib! I was especially attracted to it for built-in support for notification actions, which I was putting off implementing.

Unless I'm missing something, it's currently a bit of a chore to get data into an action button:

  @impl true
  def handle_info(...) do
    url = ~p"/emails/#{email}"
    action = create_link_button(url)

    LiveToast.send_toast(
      :info,
      "Email \"#{email.subject}\" was processed successfully",
      action: action
    )

    {:noreply, socket}
  end

  # have to wrap the :actions function with another function to pass state in
  defp create_link_button(url) do
    fn assigns ->
      assigns = assign(assigns, :url, url)
      ~H"""
      <.button phx-click={JS.patch(@url)} type="button" style={:secondary}>View</.button>
      """
    end
  end

Is there a better way to do this? Thanks!

srcrip commented 5 months ago

Thank you, let me think of a good API change here to make this a little easier.

srcrip commented 5 months ago

I'm down for API changes if you have a suggestion, but does something like this pattern better solve it?

  opts = [
    action: fn assigns ->
      assigns
      |> Map.put(:id, "123")
      |> download_action()
    end
  ]

  LiveToast.send_toast(:info, "File parsed successfully.", opts)
  attr :id, :string, required: true

  def download_action(assigns) do
    ~H"""
    <button
      class="my-4 mr-4 text-sm font-medium bg-zinc-900 text-zinc-100 px-2 py-1 rounded-md hover:bg-zinc-800 hover:text-zinc-200"
      phx-click="download"
      phx-value-id={@id}
    >
      Download Item <%= @id %>
    </button>
    """
  end

If I had to resuse that more than once I'd probably also make a helper function like:

def send_download_toast(download_id) do
  opts = [
    action: fn assigns ->
      assigns
      |> Map.put(:id, "123")
      |> download_action()
    end
  ]

  LiveToast.send_toast(:info, "File parsed successfully.", opts)
end

and reuse it if it was needed.

That being said I am down to change the API for this customization if you have a suggestion. It's just kind of a hard problem. I think passing a function is the write move though because you can do stuff like this where you just edit the assigns.

srcrip commented 5 months ago

Also if this pattern is satisfying, we can put it in the docs.

dfalling commented 5 months ago

Yeah, I agree that those examples would be perfect- it took me a minute to grok my current solution and that looks cleaner.

One other idea- would it be too heavy or messy for send_toast's options to have an assigns that's merged in and passed to any action or custom notification component?

Eg.

def send_download_toast(download_id) do
  opts = [
    action: &link_button/1,
    assigns: [url: ~p"/files/avatar.png", icon: "hero-download"]
  ]

  LiveToast.send_toast(:info, "File parsed successfully.", opts)
end

My only issue with the current solutions is that wrapping the action / custom component in a lambda isn't as intuitive for me. But I could definitely be over-complicating this.

If this feels too complicated, I think the alternative is just for me to make my own helper in my codebase that takes assigns and action and returns a new action.

srcrip commented 4 months ago

I think passing the assigns would not be a great default, but I could be persuaded otherwise if there was something you couldn't accomplish by building the lambda. I understand it may be a little counterintuitive though.

dfalling commented 4 months ago

That's fair, the workarounds are fine. I can take a stab at a PR to add docs for them if you'd like.

srcrip commented 4 months ago

Sure, it would be appreciated!