Getting LiveComponent children of a Phoenix LiveView to update

Getting LiveComponent children of a Phoenix LiveView to update

04 February, 2024 3 min read
software, programming, Elixir, learning, Phoenix LiveView

Making progress with Changelogrex , the “Linux kernel Changelog king”! Today I figured out how to get LiveComponents within a LiveView to update by using PubSub, the send_update function within a LiveComponent, and the update function within a LiveView.

When the one2 function is invoked with a Linux kernel version string as a parameter (that is properly validated using a regex), the corresponding ChangeLog is fetched from pub.kernel.org and gets split into commits using the functions of the Changelogr.Parser module. After that, the commits’ text bodies are parsed asynchronously into separate fields across all CPU threads, and the resulting fields are persisted as a %Commit{} struct in an SQLite database with Ecto.

After every write to the database a PubSub message is sent to the topic “crud”, to which the Dashboard LiveView is also subscribed. Note that LiveComponents cannot handle_info, and therefore it makes no sense to subscribe a LiveComponent to a PubSub topic.

The LiveView contains two LiveComponents:

  1. ChangelogrWeb.DashboardLive.CommitTileComponent and
  2. ChangelogrWeb.DashboardLive.ChangelogTileComponent.

They both use the same tile design, with a different title and a different function that pulls the data shown with an Ecto query; in this case, a count of all commits, and the most recent kernel ChangeLog that’s in the database, respectively.

After trying to figure it out by chatting with ChatGPT 3.5, I realized that its knowledge of Phoenix LiveView is hopelessly outdated. Finally, I took a look at the documentation :

While a component may always be updated from the parent by updating some parent assigns which will re-render the child, thus invoking update/2 on the child component, send_update/3 is useful for updating a component that entirely manages its own state, as well as messaging between components mounted in the same LiveView.

Here’s a video that shows the end result in action:

How it works

The trick to get these LiveComponents to update when the data changes is:

  1. Broadcast a PubSub message to the “crud” topic using ChangelogrWeb.Endpoint.broadcast.
  2. Create a handle_info callback in the parent LiveView that receives PubSub messages in the “crud” topic.
  3. Within handle_info of the LiveView, use send_update to each of the child LiveComponents.
  4. What happens next is that send_update triggers the LiveComponent’s update function, in which you assign new values to the socket struct.

As shown on the video, this updates even across different users looking at the same page (the Dashboard).

And yes, it is overkill to show the number increasing, plus it makes the whole thing much slower. For the practical purpose of not having to reload the page to check if a new ChangeLog and commits have been added, the PubSub message doesn’t really need to be sent with every single commit being persisted; it could be sent with every n-th commit, or simply sent once after all the commits in the ChangeLog have been processed.

Outlook

The reason why this needs to be updated by back-end functions without user intervention is that I plan to poll pub.kernel.org every hour to check for new ChangeLogs; and since pub.kernel.org doesn’t offer an API for accessing the ChangeLogs, I am considering making a GET endpoint available with Phoenix that registered users could consume.

Finally, the most important features are missing:

  1. the ability to star specific topics or contributors and receive notifications about what’s been going on, and
  2. the ability to search across all commits.

I have also “baked in” the use of ollama through ollamex in order to enable users to ask standard questions about a commit (ELI5, etc.). However, CPU-based LLM inference is pretty slow, and I don’t know if it would be better than simply copy-pasting the commit body into ChatGPT 3.5 after a prompt. Or, maybe, I could allow users to add their OpenAI API token to the settings, so that those who already pay for this service can use it on this web app. TBD.