Skip to content

Commit

Permalink
feat: option to terminate a boost
Browse files Browse the repository at this point in the history
  • Loading branch information
ruilopesm committed Sep 2, 2023
1 parent 934709e commit 5ec7b3b
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 40 deletions.
30 changes: 30 additions & 0 deletions lib/parzival/store.ex
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,41 @@ defmodule Parzival.Store do
Item.changeset(item, attrs)
end

@doc """
Returns true if the user has a skip task boost in their inventory, false otherwise.
## Examples
iex> has_skip_task?(user_id)
true
iex> has_skip_task?(user_id)
false
"""
def has_skip_task?(user_id) do
Item
|> where([i], i.user_id == ^user_id)
|> join(:inner, [i], b in Boost, on: i.boost_id == b.id)
|> where([i, b], b.type == :skip_task)
|> Repo.exists?()
end

@doc """
Terminates a boost, by setting its `expires_at` to `nil`. It also deletes the item from the user inventory.
"""
def terminate_boost(item_id) do
item = get_item!(item_id)

Ecto.Multi.new()
|> Ecto.Multi.update(:update_item, Item.changeset(item, %{expires_at: nil}))
|> Ecto.Multi.delete(:delete_item, item)
|> Repo.transaction()
|> case do
{:ok, transaction} ->
{:ok, transaction.update_item}

{:error, _transaction, changeset, _} ->
{:error, changeset}
end
end
end
6 changes: 2 additions & 4 deletions lib/parzival/store/item.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Parzival.Store.Item do
@moduledoc """
A item with a boost that belongs to a user.
An item with a boost that belongs to a user.
"""
use Parzival.Schema

Expand All @@ -9,9 +9,7 @@ defmodule Parzival.Store.Item do

@required_fields ~w(user_id boost_id)a

@optional_fields [
:expires_at
]
@optional_fields ~w(expires_at)a

schema "items" do
field :expires_at, :naive_datetime
Expand Down
12 changes: 6 additions & 6 deletions lib/parzival_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ defmodule ParzivalWeb do
layout: {ParzivalWeb.LayoutView, "live.html"}

unquote(view_helpers())
unquote(error_handling_helpers())
unquote(handle_flash_helper())
end
end

Expand All @@ -58,7 +58,7 @@ defmodule ParzivalWeb do
layout: unquote(layout)

unquote(view_helpers())
unquote(error_handling_helpers())
unquote(handle_flash_helper())
end
end

Expand Down Expand Up @@ -116,13 +116,13 @@ defmodule ParzivalWeb do
end
end

# Injects helpers for error handling in live views
defp error_handling_helpers do
# Injects helpers for handling flash messages sent from live components
defp handle_flash_helper do
quote do
def handle_info({:error, reason}, socket) do
def handle_info({action, reason}, socket) when is_atom(action) do
{:noreply,
socket
|> put_flash(:error, reason)}
|> put_flash(action, reason)}
end
end
end
Expand Down
17 changes: 9 additions & 8 deletions lib/parzival_web/components/boost.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ defmodule ParzivalWeb.Components.Boost do
def render(assigns) do
~H"""
<div class="w-12 h-12">
<%= if !assigns.item.expires_at do %>
<%= if assigns.item.boost.type in [:exp, :tokens] do %>
<button phx-click="activate-boost" phx-value-item_id={assigns.item.id} phx-target={@myself} data-confirm={"Are you sure you wanna activate #{assigns.item.boost.name}? #{assigns.item.boost.description}."}>
<img src={Uploaders.BoostImage.url({assigns.item.boost.image, assigns.item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full hover:opacity-70" />
<%= if ! @item.expires_at do %>
<%= if @item.boost.type in [:exp, :tokens] do %>
<button phx-click="activate-boost" phx-value-item_id={@item.id} phx-target={@myself} data-confirm={"Are you sure you wanna activate #{assigns.item.boost.name}? #{assigns.item.boost.description}."}>
<img src={Uploaders.BoostImage.url({@item.boost.image, @item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full hover:opacity-70" />
</button>
<% else %>
<img src={Uploaders.BoostImage.url({assigns.item.boost.image, assigns.item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full" />
<img src={Uploaders.BoostImage.url({@item.boost.image, @item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full" />
<% end %>
<% else %>
<div class="relative">
<span class="absolute w-full h-full bg-red-500 opacity-75 animate-ping" />
<img src={Uploaders.BoostImage.url({assigns.item.boost.image, assigns.item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full" />
<img src={Uploaders.BoostImage.url({@item.boost.image, @item.boost}, :original)} class="object-cover object-center w-full h-full lg:w-full lg:h-full" />
</div>
<% end %>
</div>
Expand All @@ -38,12 +38,13 @@ defmodule ParzivalWeb.Components.Boost do
item = Store.get_item!(item_id)
Store.update_item(item, %{expires_at: Timex.shift(NaiveDateTime.utc_now(), minutes: 60)})

send(self(), {:info, "Boost activated!"})

{:noreply,
socket
|> assign(
inventory: Store.list_inventory(where: [user_id: item.user_id], preloads: [:boost])
)
|> put_flash(:info, "Boost activated!")}
)}
end
end
end
69 changes: 69 additions & 0 deletions lib/parzival_web/components/inventory.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
defmodule ParzivalWeb.Components.Inventory do
@moduledoc false
use ParzivalWeb, :live_component

alias Parzival.Store

@impl true
def render(assigns) do
~H"""
<span>
<p class="text-gray-500">
<%= "Inventory (#{length(@inventory)}/5)" %>
</p>
<div class="grid grid-cols-5 gap-2">
<%= for i <- 1..5 do %>
<%= if i > length(@inventory) do %>
<div class="w-12 h-12 border border-gray-200" />
<% else %>
<.live_component module={ParzivalWeb.Components.Boost} id={Enum.at(@inventory, i - 1).id} item={Enum.at(@inventory, i - 1)} flash={@flash} />
<% end %>
<% end %>
</div>
<%= for item <- @inventory do %>
<%= if item.expires_at do %>
<div class="flex justify-between items-center mt-2 text-gray-900">
<span><%= item.boost.name %></span>
<div class="flex gap-x-1 items-center">
<span><%= relative_datetime_in_digital_clock_format(item.expires_at) %></span>
<span phx-target={@myself} phx-click="terminate-boost" phx-value-item_id={item.id} class="cursor-pointer" data-confirm="Are you sure you want to terminate this boost?">
<svg class="w-5 h-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
/>
</svg>
</span>
</div>
</div>
<% end %>
<% end %>
</span>
"""
end

@impl true
def handle_event("terminate-boost", %{"item_id" => item_id}, socket) do
case Store.terminate_boost(item_id) do
{:ok, item} ->
send(self(), {:info, "Boost terminated!"})

{:noreply,
socket
|> assign(
inventory: Store.list_inventory(where: [user_id: item.user_id], preloads: [:boost])
)}

{:error, _reason} ->
send(
self(),
{:error,
"There was an error while trying to terminate the boost. Please try again later."}
)

{:noreply, socket}
end
end
end
23 changes: 1 addition & 22 deletions lib/parzival_web/templates/layout/live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -120,28 +120,7 @@
</span>
</div>
<% end %>
<!-- Inventory -->
<p class="text-gray-500">
<%= "Inventory (#{length(@inventory)}/5)" %>
</p>
<div class="grid grid-cols-5 gap-2">
<%= for i <- 1..5 do %>
<%= if i > length(@inventory) do %>
<div class="w-12 h-12 border border-gray-200" />
<% else %>
<.live_component module={ParzivalWeb.Components.Boost} id={Enum.at(@inventory, i - 1).id} item={Enum.at(@inventory, i - 1)} flash={@flash} />
<% end %>
<% end %>
</div>

<%= for item <- @inventory do %>
<%= if item.expires_at do %>
<div class="flex justify-between items-center mt-2 text-gray-900">
<span><%= item.boost.name %></span>
<span><%= relative_datetime_in_digital_clock_format(item.expires_at) %></span>
</div>
<% end %>
<% end %>
<.live_component module={ParzivalWeb.Components.Inventory} id="inventory" inventory={@inventory} />
<% end %>
</div>
<!-- Socials -->
Expand Down

0 comments on commit 5ec7b3b

Please sign in to comment.