Hey guys, when inputting a verification code with a count down timer it’s wiping out the form.
Below is reproducable liveview code
defmodule ClimateCollectiveWeb.SmsVerificationLive do
use ClimateCollectiveWeb, :live_view
# alias ClimateCollective.Accounts.SMSTokens
# alias ClimateCollectiveWeb.UserAuth
# alias ClimateCollective.Accounts.Users
def render(assigns) do
~H"""
<div class="container">
<div class="row">
<div class="col-12">
<h1>Verify your phone number: <%= @phone %></h1>
<p>
If your phone is registered, we sent a verification code to your phone. Please click on the in the sms to continue.
</p>
<.simple_form
id="sms_verification_form"
phx-change="code_changed"
for={@form}
action={~p"/sms_log_in"}
phx-trigger-action={@trigger_submit}
>
<div class="flex flex-row space-x-3 form-group">
<.label for="code">Verification code</.label>
<%= @verification_token %>
<input type="text" name="phone" hidden="true" value={@phone} />
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_0"
name="code[0]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_0"
value=""
phx-value-input-id="0"
phx-keydown="input_down"
/>
</div>
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_1"
name="code[1]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_1"
value=""
phx-value-input-id="1"
phx-keydown="input_down"
/>
</div>
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_2"
name="code[2]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_2"
value=""
phx-value-input-id="2"
phx-keydown="input_down"
/>
</div>
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_3"
name="code[3]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_3"
value=""
phx-value-input-id="3"
phx-keydown="input_down"
/>
</div>
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_4"
name="code[4]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_4"
value=""
phx-value-input-id="4"
phx-keydown="input_down"
/>
</div>
<div class="w-12">
<input
type="tel"
class="form-control code-input"
id="code_5"
name="code[5]"
inputmode="numeric"
maxlength="1"
pattern="[0-9]*"
field="code_5"
value=""
phx-value-input-id="5"
phx-keydown="input_down"
/>
</div>
</div>
<%= if @invalid do %>
<p class="text-red-500">Invalid code</p>
<% end %>
<:actions>
<div>
<.button
type="button"
class={"btn btn-secondary #{if @resend_in > 0, do: "disabled:opacity-50"}"}
phx-click="resend_code"
disabled={@resend_in > 0}
>
Resend code <%= if @resend_in > 0, do: @resend_in %>
</.button>
</div>
<.button type="submit" class="btn btn-primary ">
Submit
</.button>
</:actions>
</.simple_form>
</div>
</div>
</div>
"""
end
def mount(_params, session, socket) do
form = to_form(%{}, as: "sms_verification")
phone = "+15550123"
socket =
socket
|> assign(:code, "")
|> assign(:disable_submit, true)
|> assign(:invalid, false)
|> assign(:trigger_submit, false)
|> assign(:resend_in, 30)
|> assign(:phone, phone)
|> assign(:verification_token, 654_321)
|> assign(:form, form)
if connected?(socket), do: Process.send_after(self(), :update_resend_timer, 1_000)
{:ok, socket}
end
def handle_info(:update_resend_timer, socket) do
Process.send_after(self(), :update_resend_timer, 1_000)
if socket.assigns.resend_in >= 1 do
{:noreply, update(socket, :resend_in, fn resend_in -> resend_in - 1 end)}
else
{:noreply, socket}
end
end
def handle_event("input_down", %{"input-id" => id, "key" => "Backspace"}, socket) do
id = String.to_integer(id)
cond do
id > 0 ->
{:noreply,
socket
|> push_event("focus", %{id: "code_#{id - 1}"})
|> push_event("clear", %{id: "code_#{id - 1}"})}
true ->
{:noreply, socket}
end
end
def handle_event("input_down", %{"input-id" => id, "key" => key}, socket) do
id = String.to_integer(id)
cond do
key == "Meta" ->
{:noreply, socket}
id < 5 ->
{:noreply, move_input_field(socket, true, id)}
id == 5 ->
{:noreply, assign(socket, trigger_submit: true)}
true ->
{:noreply, socket}
end
end
def handle_event("code_changed", %{"code" => code}, socket) do
code_string = Map.values(code) |> Enum.join()
if String.length(code_string) == 6 do
{:noreply, socket |> assign(:code, code) |> assign(trigger_submit: true)}
else
{
:noreply,
socket
|> assign(:code, code)
|> assign(:disable_submit, String.length(code_string) != 6)
}
end
end
def handle_event("input_pasted", %{"code" => code}, socket) do
if String.length(code) == 6 do
{:noreply, socket |> assign(trigger_submit: true)}
else
IO.puts("no")
{:noreply, socket}
end
end
def handle_event("resend_code", _params, socket) do
{:noreply, socket |> assign(:resend_in, 30) |> assign(:verification_token, "123")}
end
defp move_input_field(socket, is_present?, input_id) do
cond do
input_id == 5 ->
socket
input_id == 0 && !is_present? ->
socket
is_present? && input_id < 5 ->
push_event(socket, "focus", %{id: "code_#{input_id + 1}"})
!is_present? && input_id > 0 ->
push_event(socket, "focus", %{id: "code_#{input_id - 1}"})
end
end
end
Here’s what happens:
- Land on page
- Input code
- Once the button refreshes form data dissappears
Would love some help! I think I’m missing some sort of concept
Thank you in advance!