Programming Phoenix LiveView B3.0: incorrect pattern match and other minor bugs (page 247)

I think that the second clause will never match because Presence.get_by_key/2 returns a presences() map that is defined as

    presences() :: %{required(String.t()) => %{metas: [map()]}}

so, in the second clause, instead of

  %{users: active_users_for_product} ->

we should have

  %{metas: [%{users: active_users_for_product}]} ->

this is the complete code

def track_user(pid, product, token) do
  user = Accounts.get_user_by_session_token(token)
  case Presence.get_by_key(@user_activity_topic, product.name) do 
    [] ->
      Presence.track(
        pid,
       @user_activity_topic, product.name,
       %{users: [%{email: user.email}]}
       )

  # %{users: active_users_for_product} ->
  %{metas: [%{users: active_users_for_product}]} ->
      Presence.update(pid, @user_activity_topic, product.name, %{
users: [active_users_for_product | %{email: user.email}] })
end end

I also noticed another little bug with list concatenation: using the | operator to append the new user to the previous, the list of previous users should be prepended by the map with the new user, not followed by. Instead of

  Presence.update(pid, "user_activity", product.name, %{
    users: [active_users_for_product | %{email: user.email}] })

should be

  Presence.update(pid, "user_activity", product.name, %{
    users: [%{email: user.email} | active_users_for_product] })

But now we have the last problem: Presence.update/4 will return {:error, :nopresence} when trying to add a new user when she/he is viewing the same product page. This because only the socket/PID tracking the presence object can update/modify it, as noticed here. A workaround could be registering the process with a name, as explained in this article.

Besides these errata, your book is fantastic, it explain very well not only LiveView but many other aspect of the E/OTP ecosystem, and it is superbly written, a real joy to read!