Baxter

Joined

1,850 Experience
18 Lessons Completed
0 Questions Solved

Activity

Hi! I'm building a Rails app where users can check if a given username is available on multiple platforms. I have a form where users can enter a username, and then the app makes concurrent requests to check if the username is available on Github, Gitlab, and Instagram.

The problem I'm experiencing is that the app currently renders all of the availability results at once, rather than showing each result as it becomes available. I'd like to display a loading animation for each platform while the request is in progress, and then show the result (either "available" or "unavailable") when it's ready.

Here is the code I have so far:

# app/controllers/usernames_controller.rb
class UsernamesController < ApplicationController
  def index
    @availability = {}
  end

  def create
    @availability = UsernameService.check_availability(params[:username])
    render partial: 'username_results'
  end
end

# app/services/username_service.rb
class UsernameService
  def self.check_availability(username)
    username = Username.new(username)

    # Use a thread pool to make the requests concurrently
    thread_pool = Concurrent::FixedThreadPool.new(3)

    # Create futures for each request
    github_future = Concurrent::Future.execute(executor: thread_pool) { username.github_available? }
    gitlab_future = Concurrent::Future.execute(executor: thread_pool) {  username.gitlab_available? }
    instagram_future = Concurrent::Future.execute(executor: thread_pool) { username.instagram_available? }

    # Wait for the futures to complete and retrieve the results
    github_result = github_future.value
    gitlab_result = gitlab_future.value
    instagram_result = instagram_future.value

    {
      github: github_result,
      gitlab: gitlab_result,
      instagram: instagram_result
    }
  end
end

Here is the form:

<div class="flex flex-col mt-5">
  <div class="mx-auto">
    <%= form_with(url: usernames_path, method: :post, data: { turbo_frame: "username_results"}) do |f| %>
      <div class="grid grid-cols-3 gap-6 mt-5">
        <div class="col-span-3 sm:col-span-2">
          <%= f.label :username, class: "block | text-sm text-gray-700" %>
          <%= f.text_field :username, class: "block w-full | rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" %>
        </div>
      </div>

      <div>
        <%= f.button "Check Availability", type: "submit" %>
      </div>
    <% end %>
  </div>

  <div id="loading-animation" style="display: none">
    <p>Loading Animation...</p>
  </div>

  <%= render  partial: 'username_results', locals: { availability: @availability } %>

  <script>
    // When the frame is loading
    document.addEventListener("turbo:before-fetch-request", function() {
      document.getElementById("loading-animation").style.display = "block";
    });

    // When the frame has loaded
    document.addEventListener("turbo:frame-load", function() {
      document.getElementById("loading-animation").style.display = "none";
    });
  </script>
</div>

And here's the partial for username_results:

<%= turbo_frame_tag "username_results", class: "mt-10" do %>
  <div class="flex flex-wrap justify-center">
    <% @availability.each do |platform, availability| %>
      <div class="w-32 h-32 bg-gray-200 rounded-lg shadow-lg m-4 p-4">
        <% if availability %>
          <div class="text-green-500"><%= platform %></div>
        <% else %>
          <div class="text-red-500"><%= platform %></div>
        <% end %>
      </div>
    <% end %>
  </div>
<% end %>

Is this a scenario where I should consider using Server-Sent Events (SSEs) to stream the results to the client as they become available. Or is Turbo Streams a suitable option for updating the results as they become available in my application? I am not familiar with Turbo Streams, so any guidance on this would be greatly appreciated.