How to fire turbo_stream by browser visibilitychange

I want to update part of view with turbo_stream when the browser visilitychange.
Is it possible using stimulus?

There is no built-in function for it, but stimulus-use is a popular library that has utility functions such as useVisibility for this exact use case!

Or do you mean you want something to happen when the DOM element becomes visible on screen?

If that is the case you can simply use lazy loading on that frame!

Thanks for your reply.
Real-time updating of the page that pushes turbo stream using websocket stops working if the browser is inactive, thus I would like to update the page to current state when the user reopens the browser and becomes active. so present implementation, the entire screen is reloaded with visibilitychange as a trigger as shown below.

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    document.addEventListener("visibilitychange", () => {
      if (!document.hidden) {
        location.reload();
      }
    })
  }
}

However it’s stressful to reload the entire screen, so I’d like to change it so that only part of the screen is updated.
I think it would be good to be able to do it with Hotwire, but would it be difficult?
I would appreciate it if you could tell me if you have any ideas.

I see!

I can think of two ways:

  1. Update turbo_stream.src attributes using stimulus.
    a. You can use the stimulus controller you already have but wrap it around the entire page.
    b. Then, you need to also wrap the specific parts of the page you want to reload with a <turbo-frame>.
    c. Then on the stimulus controller, when the visibility changes, update each turbo-stream’s src attribute to trigger a new request to the backend. This works because Turbo automatically triggers a new fetch when src attribute changes! (Read more)
    Your stimulus controller would end up looking something like this:
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "turboFrame" ]

  connect() {
    document.addEventListener("visibilitychange", () => {
      if (!document.hidden) {
        this.turboFrameTargets.forEach((turboFrame) => {
          turboFrame.src = turboFrame.src
        })
      }
    })
  }
}
  1. Turbo.visit with turbo stream response.
    I’m not totally sure how to do this but if wrapping the entire page with the stimulusJS and targeting all turbo frames is too difficult to manage, maybe you could call Turbo.visit() inside your stimulusJS controller to a backend route that would render turbo stream responses that replace all the parts of the webpage you want to update.
    The only issue with this is that you have to make sure the Turbo.visit request accepts a MIME type of text/vnd.turbo-stream.html, and I’m not sure if it does by default!

Thank you for your reply and sorry for my delay response.
I tried the first of the two methods you mentioned, but unfortunately it did not work.
My stimulus controller as “reloadable” is followings.

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["reloadFrame"]

  connect() {
    document.addEventListener("visibilitychange", () => {
      if (!document.hidden) {
        this.reloadFrameTargets.forEach((reloadFrame) => {
          reloadFrame.src = reloadFrame.src
        })
      }
    })
  }
}

Applicable view part as below.

<div data-controller="reloadable">
  <div data-reloadable-target="reloadFrame">
    <%= turbo_frame_tag dom_id(@dogrun_place, :num_of_playing_dogs) do %>
       <%= render partial: "shared/num_of_playing_dogs", locals: ... %>
    <% end %>
    <%= turbo_frame_tag dom_id(@dogrun_place, :among_them_non_public_dogs) do %>
      <%= render partial: "shared/among_them_non_public_dogs", locals: ...  %>
    <% end %>
    <%= render partial: 'shared/during_pre_entry_dogs_content', locals: ... %>
    <%= turbo_frame_tag dom_id(@dogrun_place, :login_top_content) do %>
      <%= render partial: 'shared/login_top_content', locals: ... %>
    <% end %>
    <%= render partial: 'shared/during_entry_dogs_content', locals: ... %>
  </div>
</div>

The difference could not be confirmed in the operation regardless of the presence or absence of <div data-reloadable-target="reloadFrame">.
About the second of you mentioned, I didn’t know what to do.
I would be happy if there was a mechanism to fire Turbo Stream by switching from inactive to active browser to active, but I understand that it is not possible. Unfortunately.

Hey @Y.Ono!

I created a simple app to showcase my solution:

Hope this helps! :slight_smile:

1 Like

I’m late in thanking you @marco-beduschi , but I was able to implement the functionality I wanted with reference to the showcase you presented.
Thank you very much for your kind and polite answer.

1 Like

I’m glad it worked! :smile: