Hi!
I am implementing a Yatzy-style game to dig my teeth into LiveView.
When one player clicks on a die during their turn the state (and thus the appearance) of the die changes. Selecting and unselecting dice in Yatzy is the main player interaction and therefore the UI should be instant.
I have tried to implement an optimistic UI using Phoenix.LiveView.JS
so that the appearance of a die is update immediately for the active player. It looks like this:
attr :hold, :boolean, required: true
attr :value, :integer, required: true
attr :index, :integer, required: true
attr :rolls_remaining, :integer, required: true
def die(assigns) do
~H"""
<button
disabled={@rolls_remaining == 0 or @rolls_remaining == 3}
phx-click={JS.push("toggle-die") |> optimistic_toggle(@hold, @index)}
phx-value-die={@index}
>
<div class={if @hold, do: "", else: "roll duration-150"}>
<div class={"face face-#{@index} p-1 rounded-lg border-4 w-16 h-16 #{if @hold, do: "bg-white/95 border-sky-800 translate-y-2", else: "bg-white border-transparent"}"}>
<span
:for={_n <- 1..@value}
class="pip w-3 h-3 rounded-full self-center justify-self-center bg-black/95"
/>
</div>
</div>
</button>
"""
end
def optimistic_toggle(js \\ %JS{}, hold, index) do
to = ".face-#{index}"
case hold do
true ->
js
|> JS.remove_class("bg-white border-transparent", to: to)
|> JS.add_class("bg-white/95 border-sky-800 translate-y-2", to: to)
false ->
js
|> JS.remove_class("bg-white/95 border-sky-800 translate-y-2", to: to)
|> JS.add_class("bg-white border-transparent", to: to)
end
end
I would expect the classes to be removed/added immediately, however this doesn’t to make a difference. I have tested this with liveSocket.enableLatencySim(100)
.
Anybody spot what I am doing wrong? Am I using Phoenix.LiveView.JS
incorrectly?
Much appreciated,
David