Skip to content

Commit

Permalink
fix: Editing timers while running works properly. #195
Browse files Browse the repository at this point in the history
The timer can be updated while it is running. Validation now works properly.
  • Loading branch information
LuchoTurtle committed Nov 11, 2022
1 parent d2c2b91 commit ac53751
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 17 deletions.
23 changes: 22 additions & 1 deletion lib/app/timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ defmodule App.Timer do
end

@doc """
Lists all the timer from a given item.id.
Lists all the timers from a given item.id.
## Examples
Expand All @@ -93,6 +93,27 @@ defmodule App.Timer do
|> Repo.all()
end

@doc """
Lists all the timers changesets from a given item.id.
This is useful for form validation, as it returns the timers in a changeset form, in which you can add errors.
## Examples
iex> list_timers(1)
[ #Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #App.Timer<>, valid?: true> ]
"""
def list_timers_changesets(item_id) do
from(v in Timer, where: [item_id: ^item_id], order_by: [asc: :id])
|> Repo.all()
|> Enum.map(fn t ->
Timer.changeset(t, %{
id: t.id,
start: t.start,
stop: t.stop,
item_id: t.item_id
}) end)
end

@doc """
`stop_timer_for_item_id/1` stops a timer for the given item_id if there is one.
Fails silently if there is no timer for the given item_id.
Expand Down
102 changes: 87 additions & 15 deletions lib/app_web/live/app_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule AppWeb.AppLive do
start: NaiveDateTime.utc_now()
})

AppWeb.Endpoint.broadcast(@topic, "update", :start)
AppWeb.Endpoint.broadcast(@topic, "update", {:start, item.id})
{:noreply, socket}
end

Expand All @@ -86,25 +86,15 @@ defmodule AppWeb.AppLive do
timer_id = Map.get(data, "timerid")
{:ok, _timer} = Timer.stop(%{id: timer_id})

AppWeb.Endpoint.broadcast(@topic, "update", :stop)
AppWeb.Endpoint.broadcast(@topic, "update", {:stop, Map.get(data, "id")})
{:noreply, socket}
end

@impl true
def handle_event("edit-item", data, socket) do
item_id = String.to_integer(data["id"])

timers_list = Timer.list_timers(item_id)

timers_list_changeset =
Enum.map(timers_list, fn t ->
Timer.changeset(t, %{
id: t.id,
start: t.start,
stop: t.stop,
item_id: t.item_id
})
end)
timers_list_changeset = Timer.list_timers_changesets(item_id)

{:noreply,
assign(socket, editing: item_id, editing_timers: timers_list_changeset)}
Expand All @@ -130,6 +120,66 @@ defmodule AppWeb.AppLive do
end

@impl true
def handle_event(
"update-item-timer",
%{
"timer_id" => id,
"index" => index,
"timer_start" => timer_start,
"timer_stop" => timer_stop
},
socket
) when timer_stop == "" do

timer_changeset_list = socket.assigns.editing_timers
index = String.to_integer(index)
changeset_obj = Enum.at(timer_changeset_list, index)

try do
start =
App.DateTimeParser.parse!(timer_start, "%Y-%m-%dT%H:%M:%S")
|> DateTime.to_naive()

other_timers_list =
List.delete_at(socket.assigns.editing_timers, index)

max_end = other_timers_list |> Enum.map(fn chs -> chs.data.stop end) |> Enum.max()

case NaiveDateTime.compare(start, max_end) do
:gt ->
Timer.update_timer(%{id: id, start: start, stop: nil})
{:noreply, assign(socket, editing: nil, editing_timers: [])}
_ ->
updated_changeset_timers_list =
error_timer_changeset(
timer_changeset_list,
changeset_obj,
index,
:id,
"When editing an ongoing timer, make sure it's after all the others."
)

{:noreply,
assign(socket, editing_timers: updated_changeset_timers_list)}

end

rescue
_e ->
updated_changeset_timers_list =
error_timer_changeset(
timer_changeset_list,
changeset_obj,
index,
:id,
"Date format invalid on either start or stop."
)

{:noreply,
assign(socket, editing_timers: updated_changeset_timers_list)}
end
end

def handle_event(
"update-item-timer",
%{
Expand Down Expand Up @@ -270,11 +320,33 @@ defmodule AppWeb.AppLive do
end

@impl true
def handle_info(%Broadcast{event: "update", payload: _message}, socket) do
def handle_info(%Broadcast{event: "update", payload: payload}, socket) do

person_id = get_person_id(socket.assigns)
items = Item.items_with_timers(person_id)

{:noreply, assign(socket, items: items)}
isEditingItem = socket.assigns.editing

# If the item is being edited, we update the timer list of the item being edited.
if isEditingItem do
case payload do
{:start, item_id} ->
timers_list_changeset = Timer.list_timers_changesets(item_id)
{:noreply, assign(socket, items: items, editing: item_id, editing_timers: timers_list_changeset)}


{:stop, item_id} ->
timers_list_changeset = Timer.list_timers_changesets(item_id)
{:noreply, assign(socket, items: items, editing: item_id, editing_timers: timers_list_changeset)}

_ ->
{:noreply, assign(socket, items: items)}
end

# If not, just update the item list.
else
{:noreply, assign(socket, items: items)}
end
end

# only show certain UI elements (buttons) if there are items:
Expand Down
1 change: 0 additions & 1 deletion lib/app_web/live/app_live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@
<input
type="text"
name="timer_stop"
required="required"
id={"#{changeset.data.id}_stop"}
value={changeset.data.stop}
/>
Expand Down

0 comments on commit ac53751

Please sign in to comment.