Phoenix live view: update causes element hidden with push_event to become visible

In my live view app, I have a form where user clicks a button that shows a modal dialog previously hidden. From the dialog, user can select a file for upload. When upload is done, dialog is hidden automatically by sending even to the browser via Phoenix.LiveView.push_event/3. if user types into a form element after this, the dialog will be come visible again.

However, if dialog is hidden via Phoenix.LiveView.JS.hide/2 I never get this behavior.

Here is the code that powers the component:

defmodule MyAppWeb.UploadComponent do
  use MyAppWeb, :live_component

  alias Phoenix.LiveView.JS
  alias Ecto.UUID
  alias Ecto.Multi
  alias MyApp.Repo
  alias MyAppWeb.Endpoint
  alias MyApp.Uploads.Upload

  @impl true
  def update(assigns, socket) do
    changeset = Upload.changeset()

    {
      :ok,
      socket
      |> allow_upload(
        :file,
        max_file_size: Endpoint.max_file_size(),
        # accept: assigns.accept,
        accept: :any,
        max_entries: 1
        # external: &presign_entry/2
      )
      |> assign(assigns)
      |> assign(:changeset, changeset)
      |> assign(:show_submit, false)
    }
  end

  @impl true
  def handle_event("validate", _params, socket) do
    {
      :noreply,
      socket
      |> assign(:show_submit, true)
    }
  end

  @impl true
  def handle_event("cancel-upload", _, socket) do
    socket =
      case socket.assigns.uploads.file.entries do
        [] ->
          socket

        [entry] ->
          # Call to cancel_upload/3 while upload is in flight will cause
          # handle_event("save", _, socket) to be called with
          # socket.assigns.file.entries = [] when cancel_upload/3 settles. It
          # is in handle_event/3 that we will dismiss the dialog.

          socket
          |> cancel_upload(:file, entry.ref)
      end

    {
      :noreply,
      socket
      |> assign(:show_submit, false)
    }
  end

  def handle_event("save", _params, socket) do
    uploaded_files =
      consume_uploaded_entries(socket, :file, fn %{path: path}, entry ->
        file_id = UUID.generate()

        file_map = %{
          filename: entry.client_name,
          path: path
        }

        multi =
          Multi.new()
          |> Multi.insert(
            file_id,
            Upload.changeset(%Upload{id: file_id}, %{file: file_map})
          )

        {:ok, multi}
      end)
      |> Enum.reduce(Multi.new(), &Multi.merge(&2, fn _ -> &1 end))
      |> Repo.transaction()
      |> case do
        {:ok, entries} ->
          Map.values(entries)

        {:error, _operation_name, errors} ->
          dbg(errors)
          []
      end

    case uploaded_files do
      [] ->
        :ok

      _ ->
        {parent_module, parent_id} = socket.assigns.send_reesult_to

        send_update(
          parent_module,
          id: parent_id,
          uploaded_files: uploaded_files
        )
    end

    {
      :noreply,
      socket
      |> assign(:show_submit, false)
      |> push_event(
        "dismiss-add-file-dialog",
        %{
          id: "add-file-dialog",

          # When cancel_upload/3 completes, we get a re-render which has the
          # potential to show the dialog. We must therefore delay hiding dialog
          # until cancel_upload/3 has settled. `debounce` flag tells client
          # browser to hide dialog after a timeout.

          debounce: true
        }
      )
    }
  end

  # defp error_to_string(:too_large) do
  #   "Too large"
  # end
  #
  # defp error_to_string(:too_many_files) do
  #   "You have selected too many files"
  # end
  #
  # defp error_to_string(:not_accepted) do
  #   "You have selected an unacceptable file type"
  # end

  defp hide_self(js \\ %JS{}) do
    js
    |> JS.push("cancel-upload")
    |> JS.hide(
      to: "#add-file-dialog",
      transition: "fade-out"
    )
  end

  @impl true
  def render(assigns) do
    ~H"""
    <form
      class={[
        "modal",
        "fade-in"
      ]}
      tabindex="-1"
      id="add-file-dialog"
      phx-submit="save"
      phx-change="validate"
      phx-target={@myself}
      style={~s( display:none )}
    >
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h1 class="modal-title fs-5" id="addFileLabel">
              Select File
            </h1>

            <button
              type="button"
              class="btn-close"
              aria-label="Close"
              phx-click={hide_self()}
              phx-target={@myself}
            >
            </button>
          </div>

          <div class="modal-body">
            <%= for err <- upload_errors(@uploads.file) do %>
              <p class="notification is-danger max-w-xs ">
                <%= Phoenix.Naming.humanize(err) %>
              </p>
            <% end %>
            <%= for entry <- @uploads.file.entries do %>
              <div class={[
                "progress",
                "mb-1",
                !entry.preflighted? && "d-none"
              ]}>
                <div
                  class={[
                    "progress-bar",
                    "progress-bar-striped",
                    "progress-bar-animated",
                    "bg-secondary"
                  ]}
                  role="progressbar"
                  aria-label="Progressbar for file upload"
                  aria-valuenow={entry.progress}
                  aria-valuemin="0"
                  aria-valuemax="100"
                  style={~s(width: #{entry.progress}%)}
                >
                </div>
              </div>
            <% end %>

            <.live_file_input
              class={[
                "form-control"
              ]}
              upload={@uploads.file}
            />
          </div>

          <div class={[
            "modal-footer"
          ]}>
            <button
              type="button"
              class={[
                "btn",
                "btn-secondary"
              ]}
              phx-target={@myself}
              phx-click={hide_self()}
            >
              Close
            </button>

            <button
              class={[
                "btn",
                "btn-info"
              ]}
              style={
                ~s(display: #{if @show_submit, do: "inline-block", else: "none"})
              }
              id="add-file-dialog-save"
              type="submit"
              phx-disable-with="Uploading, please wait..."
              phx-target={@myself}
            >
              Save changes
            </button>
          </div>
        </div>
      </div>
    </form>
    """
  end
end

And the javascript code that responds to Phoenix.LiveView.push_event/3:

window.addEventListener("phx:dismiss-add-file-dialog", (evt) => {
  const {
    detail: { id, debounce },
  } = evt;

  const el = document.getElementById(id);

  if (el) {
    setTimeout(
      () => {
        el.style.display = "none";
      },
      debounce ? LATENCY_MILI_SEC + 5 : 0
    );
  }
});