Skip to content

Commit

Permalink
redesign offering cards (#546)
Browse files Browse the repository at this point in the history
Fixes: #535

Co-authored-by: rjt-rockx <[email protected]>
  • Loading branch information
zkat and rjt-rockx authored Aug 2, 2023
1 parent 5837eae commit 5e1428e
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 132 deletions.
3 changes: 2 additions & 1 deletion lib/banchan/offerings/offering.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ defmodule Banchan.Offerings.Offering do
field :currency, Ecto.Enum, values: Studios.Common.supported_currencies()
field :deleted_at, :naive_datetime

field :option_prices, {:array, Money.Ecto.Composite.Type}, virtual: true
field :base_price, Money.Ecto.Composite.Type, virtual: true
field :has_addons, :boolean, virtual: true
field :used_slots, :integer, virtual: true
field :user_subscribed?, :boolean, virtual: true
field :gallery_uploads, {:array, Upload}, virtual: true
Expand Down
49 changes: 29 additions & 20 deletions lib/banchan/offerings/offerings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,18 @@ defmodule Banchan.Offerings do
Calculates the offering's base price. Assumes the offering has been loaded
through `list_offerings/1`, which populates the relevant virtual field.
"""
def offering_base_price(%Offering{} = offering) do
if Enum.empty?(offering.option_prices) do
nil
else
offering.option_prices
|> Enum.reduce(&Money.add/2)
end
def offering_base_price(%Offering{base_price: base_price}) do
base_price
end

@doc """
Does this offering have any optional addons?
"""
def offering_has_addons?(%Offering{} = offering) do
!is_nil(offering.options) &&
!Enum.empty?(offering.options) &&
offering.options
|> Enum.any?(&(!&1.default))
end

@doc """
Expand Down Expand Up @@ -571,19 +576,26 @@ defmodule Banchan.Offerings do
as: :used_slots,
on: true,
left_lateral_join:
default_prices in subquery(
price_info in subquery(
from oo in OfferingOption,
where: oo.offering_id == parent_as(:offering).id and oo.default,
where: oo.offering_id == parent_as(:offering).id,
group_by: [oo.offering_id],
select: %{
prices:
type(fragment("array_agg(?)", oo.price), {:array, Money.Ecto.Composite.Type}),
# NB(zkat): This is hacky to the point of uselessness if we end up
# having a ton of different currencies listed, but it's serviceable for now.
sum: fragment("sum((?).amount)", oo.price)
has_addons:
fragment("max(case when ? = true then 0 else 1 end)::boolean", oo.default),
base_price:
type(
fragment(
"(sum(case when ? then (?).amount else 0 end), min((?).currency))",
oo.default,
oo.price,
oo.price
),
Money.Ecto.Composite.Type
)
}
),
as: :default_prices,
as: :price_info,
on: true,
left_lateral_join:
gallery_uploads in subquery(
Expand All @@ -607,11 +619,8 @@ defmodule Banchan.Offerings do
),
{:array, Upload}
),
option_prices:
type(
coalesce(default_prices.prices, fragment("ARRAY[]::money_with_currency[]")),
{:array, Money.Ecto.Composite.Type}
)
has_addons: price_info.has_addons,
base_price: type(price_info.base_price, Money.Ecto.Composite.Type)
})
)
|> filter_query(opts)
Expand Down
20 changes: 14 additions & 6 deletions lib/banchan/payments/payments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1637,15 +1637,23 @@ defmodule Banchan.Payments do
For example, differentiating between different kinds of dollars that would
usually just use `$` as a symbol.
"""
def print_money(%Money{} = money) do
case Money.Currency.symbol(money) do
"$" -> currency_symbol(money.currency) <> Money.to_string(money, symbol: false)
"" -> currency_symbol(money.currency) <> " " <> Money.to_string(money, symbol: false)
" " -> currency_symbol(money.currency) <> " " <> Money.to_string(money, symbol: false)
_ -> Money.to_string(money, symbol: true)
def print_money(%Money{} = money, symbol \\ true) do
if symbol do
case Money.Currency.symbol(money) do
"$" -> currency_symbol(money.currency) <> Money.to_string(money, symbol: false)
"" -> currency_symbol(money.currency) <> " " <> Money.to_string(money, symbol: false)
" " -> currency_symbol(money.currency) <> " " <> Money.to_string(money, symbol: false)
_ -> Money.to_string(money, symbol: true)
end
else
Money.to_string(money, symbol: false)
end
end

def currency_symbol(%Money{currency: currency}) do
currency_symbol(currency)
end

def currency_symbol(currency) when is_atom(currency) do
case Money.Currency.symbol(currency) do
"$" -> dollar_prefix(currency) <> "$"
Expand Down
96 changes: 49 additions & 47 deletions lib/banchan_web/components/offering_card.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,74 @@ defmodule BanchanWeb.Components.OfferingCard do

alias Banchan.Payments

alias BanchanWeb.Components.{Card, OfferingCardImg}
alias BanchanWeb.Components.OfferingCardImg

prop current_user, :any, from_context: :current_user
prop name, :string
prop image, :any
prop base_price, :struct
prop show_pills?, :boolean, default: true
prop has_addons?, :boolean, default: false
prop show_base_price?, :boolean, default: true
prop archived?, :boolean, default: false
prop mature?, :boolean, default: false
prop uncensored?, :boolean, default: false
prop open?, :boolean, default: false
prop hidden?, :boolean, default: false
prop total_slots, :integer
prop available_slots, :integer
prop hover_grow?, :boolean, default: true

def render(assigns) do
~F"""
<Card
class={
"h-full transition-all relative",
"opacity-50": @archived?,
"sm:hover:scale-105 sm:hover:z-10": @hover_grow?
}
image_class="overflow-hidden"
>
<:header>
<div class="text-sm sm:text-lg font-bold">{@name}</div>
</:header>
<:image>
<OfferingCardImg mature?={@mature?} image={@image} />
</:image>
<div
:if={@show_pills?}
class="absolute top-4 right-4 flex flex-col flex-wrap gap-2 items-end z-10"
>
{#if @open? && !is_nil(@total_slots) && !is_nil(@available_slots)}
<div class="cursor-default whitespace-nowrap badge badge-primary shadow-md shadow-black">{@available_slots}/{@total_slots} Slots</div>
{#elseif !@open? && !is_nil(@total_slots)}
<div class="badge badge-error shadow-md shadow-black cursor-default">0/{@total_slots} Slots</div>
{#elseif @open?}
<div class="badge badge-primary shadow-md shadow-black cursor-default">Open</div>
{#else}
<div class="badge badge-error shadow-md shadow-black cursor-default">Closed</div>
{/if}
{#if @mature?}
<div class="badge badge-error shadow-md shadow-black cursor-default">Mature</div>
{/if}
{#if @hidden?}
<div class="badge badge-error shadow-md shadow-black cursor-default">Hidden</div>
{/if}
</div>
<div :if={@show_base_price?} class="flex flex-col gap-2 grow justify-end">
<div class="flex flex-col z-20">
<p class="flex flex-row items-end">
<span class="font-bold grow cursor-default">Base Price:</span>
<offering-card class={
"h-full transition-all relative flex flex-col",
"opacity-50": @archived?
}>
<figure class="overflow-hidden border rounded-lg border-base-content border-opacity-10 bg-base-300/25">
<OfferingCardImg blur?={@mature? && !@uncensored?} image={@image} />
</figure>
<div class="flex flex-row pt-2 align-items-center">
<div class="flex flex-col grow">
<div class="flex flex-row">
<span class="font-bold name text-md">{@name}</span>
{#if @mature?}
<span class="bg-error text-error-content">M</span>
{/if}
{#if @hidden?}
<span class="bg-warning text-warning-content">Hidden</span>
{/if}
</div>
<div class="text-xs font-semibold opacity-75 availability-status whitespace-nowrap">
{#if @open? && !is_nil(@total_slots) && !is_nil(@available_slots)}
{@available_slots}/{@total_slots} Slots
{#elseif !@open? && !is_nil(@total_slots)}
0/{@total_slots} Slots
{#elseif @open?}
Open
{#else}
Closed
{/if}
</div>
</div>
<div
:if={@show_base_price?}
class="flex flex-col justify-center text-lg font-bold whitespace-nowrap"
>
<span class="flex items-center gap-2">
{#if is_nil(@base_price)}
<span class="font-semibold cursor-default">Inquire</span>
Inquire
{#else}
<span class="font-semibold cursor-default">{Payments.print_money(@base_price)}</span>
{#if @has_addons?}
<span class="text-sm font-semibold opacity-80">From
</span>
{/if}
<span class="flex gap-0">
<span class="opacity-80">{Payments.currency_symbol(@base_price)}</span>
{Payments.print_money(@base_price, false)}
</span>
{/if}
</p>
</span>
</div>
</div>
</Card>
</offering-card>
"""
end
end
61 changes: 33 additions & 28 deletions lib/banchan_web/components/offering_card_img.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,55 @@ defmodule BanchanWeb.Components.OfferingCardImg do

alias Phoenix.LiveView.UploadEntry

prop current_user, :any, from_context: :current_user
prop image, :any
prop mature?, :boolean, default: false
prop blur?, :boolean, default: false

def render(assigns) do
~F"""
{#case @image}
{#match %UploadEntry{}}
<div class="absolute overflow-hidden z-10">
<div class="relative">
{#case @image}
{#match %UploadEntry{}}
<div class="absolute z-10 w-full h-full overflow-hidden">
<.live_img_preview
class={
"object-contain aspect-video w-full h-full",
"blur-lg": @blur?
}
draggable="false"
entry={@image}
/>
</div>
<.live_img_preview
class={
"object-contain aspect-video w-full h-full",
"blur-lg": @mature? && !@current_user.uncensored_mature
}
class="object-contain w-full h-full aspect-video blur-2xl"
draggable="false"
entry={@image}
/>
</div>
<.live_img_preview class="aspect-video w-full h-full blur-lg" draggable="false" entry={@image} />
{#match _}
<div class="absolute overflow-hidden z-10">
{#match _}
<div class="absolute z-10 w-full h-full overflow-hidden">
<img
class={
"object-contain aspect-video w-full h-full",
"blur-lg": @blur?
}
draggable="false"
src={if @image do
~p"/images/offering_card_img/#{@image}"
else
~p"/images/640x360.png"
end}
/>
</div>
<img
class={
"object-contain aspect-video w-full h-full",
"blur-lg": @mature? && !@current_user.uncensored_mature
}
class="object-contain w-full h-full aspect-video blur-2xl"
draggable="false"
src={if @image do
~p"/images/offering_card_img/#{@image}"
else
~p"/images/640x360.png"
end}
/>
</div>
<img
class="aspect-video w-full h-full blur-lg"
draggable="false"
src={if @image do
~p"/images/offering_card_img/#{@image}"
else
~p"/images/640x360.png"
end}
/>
{/case}
{/case}
</div>
"""
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ defmodule BanchanWeb.CommissionLive.Components.OfferingBox do
<OfferingCard
image={@offering.card_img_id}
name={"#{@offering.name} by #{@offering.studio.name}"}
show_pills?={false}
show_base_price?={false}
/>
{#else}
Expand Down
2 changes: 1 addition & 1 deletion lib/banchan_web/live/discover_live/components/offerings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ defmodule BanchanWeb.DiscoverLive.Components.Offerings do
<LivePatch class="link" to={Routes.discover_index_path(Endpoint, :index, "studios", params)}>Search Studios instead.</LivePatch>
{/if}
{#else}
<div class="offering-list grid grid-cols-2 sm:gap-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 auto-rows-fr">
<div class="offering-list grid grid-cols-1 sm:gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 auto-rows-fr">
{#for {offering, idx} <- Enum.with_index(@offerings)}
<OfferingCard id={"offering-#{idx}"} current_user={@current_user} offering={offering} />
{/for}
Expand Down
2 changes: 1 addition & 1 deletion lib/banchan_web/live/home_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ defmodule BanchanWeb.HomeLive do
</div>
<div class="divider" />
<div class="sm:px-2 flex flex-col gap-4">
<div class="grid grid-cols-2 sm:gap-2 md:grid-cols-4 auto-rows-fr">
<div class="grid grid-cols-1 sm:gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 auto-rows-fr">
{#for {offering, idx} <- Enum.with_index(@offerings.entries)}
<OfferingCard id={"offering-#{idx}"} current_user={@current_user} offering={offering} />
{/for}
Expand Down
21 changes: 3 additions & 18 deletions lib/banchan_web/live/offering_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ defmodule BanchanWeb.OfferingLive.Show do
Lightbox,
Markdown,
MasonryGallery,
OfferingCardImg,
ReportModal,
Tag
}
Expand Down Expand Up @@ -197,15 +198,7 @@ defmodule BanchanWeb.OfferingLive.Show do
>
{#if @offering.card_img && [email protected]_img.pending}
<Lightbox.Item>
<img
class="absolute w-full h-full object-contain aspect-video overflow-hidden z-10"
src={Routes.public_image_path(Endpoint, :image, :offering_card_img, @offering.card_img_id)}
/>
<img
class="aspect-video w-full h-full blur-lg"
draggable="false"
src={Routes.public_image_path(Endpoint, :image, :offering_card_img, @offering.card_img_id)}
/>
<OfferingCardImg image={@offering.card_img} />
</Lightbox.Item>
{#else}
<div class="w-full h-full aspect-video bg-base-300" />
Expand Down Expand Up @@ -307,15 +300,7 @@ defmodule BanchanWeb.OfferingLive.Show do
>
{#if @offering.card_img && [email protected]_img.pending}
<Lightbox.Item>
<img
class="absolute w-full h-full object-contain aspect-video z-10"
src={Routes.public_image_path(Endpoint, :image, :offering_card_img, @offering.card_img_id)}
/>
<img
class="aspect-video w-full h-full blur-lg"
draggable="false"
src={Routes.public_image_path(Endpoint, :image, :offering_card_img, @offering.card_img_id)}
/>
<OfferingCardImg image={@offering.card_img} />
</Lightbox.Item>
{#else}
<div class="w-full h-full aspect-video bg-base-300" />
Expand Down
Loading

0 comments on commit 5e1428e

Please sign in to comment.