Current section

Files

Jump to
wallet_passes lib wallet_passes preview components.ex
Raw

lib/wallet_passes/preview/components.ex

if Code.ensure_loaded?(Phoenix.LiveView) do
defmodule WalletPasses.Preview.Components do
@moduledoc """
LiveView function components for previewing wallet passes.
Optional — requires `phoenix_live_view` dependency.
## Usage
import WalletPasses.Preview.Components
<.apple_pass_preview pass_json={@apple_json} qr_svg={@qr_svg} />
<.google_pass_preview pass_object={@google_obj} qr_svg={@qr_svg} />
"""
use Phoenix.Component
attr(:pass_json, :map, required: true)
attr(:qr_svg, :string, required: true)
def apple_pass_preview(assigns) do
{pass_type, ticket} = detect_apple_pass_type(assigns.pass_json)
assigns =
assigns
|> assign(:pass_type, pass_type)
|> assign(:primary, List.first(ticket["primaryFields"] || []))
|> assign(:secondary, ticket["secondaryFields"] || [])
|> assign(:auxiliary, ticket["auxiliaryFields"] || [])
|> assign(:back_fields, ticket["backFields"] || [])
|> assign(:header_fields, ticket["headerFields"] || [])
|> assign(:transit_type, ticket["transitType"])
|> assign(:bg_color, assigns.pass_json["backgroundColor"] || "rgb(255, 255, 255)")
|> assign(:fg_color, assigns.pass_json["foregroundColor"] || "rgb(38, 38, 38)")
|> assign(:label_color, assigns.pass_json["labelColor"] || "rgb(0, 168, 80)")
~H"""
<div
class="rounded-2xl overflow-hidden shadow-xl w-full max-w-sm mx-auto border border-base-content/20"
style={"background: #{@bg_color}; color: #{@fg_color};"}
>
<div class="px-5 pt-5 pb-3">
<div class="flex justify-between items-center">
<div class="text-sm font-semibold">{@pass_json["logoText"]}</div>
<div class="text-xs opacity-50">{@pass_json["description"]}</div>
</div>
</div>
<div class="px-5 pt-3 pb-3">
<div class="text-2xl font-bold tracking-tight">{@primary && @primary["value"]}</div>
</div>
<div class="px-5 pb-3 flex gap-6">
<div :for={field <- @secondary} class="flex-1">
<div
class="text-[10px] uppercase tracking-wider font-semibold"
style={"color: #{@label_color};"}
>
{field["label"]}
</div>
<div class="text-sm font-medium">{field["value"]}</div>
</div>
</div>
<div class="px-5 pb-4 flex gap-6">
<div :for={field <- @auxiliary} class="flex-1">
<div
class="text-[10px] uppercase tracking-wider font-semibold"
style={"color: #{@label_color};"}
>
{field["label"]}
</div>
<div class="text-sm">{field["value"]}</div>
</div>
</div>
<div class="border-t border-current/10 mx-5" />
<div class="flex justify-center pt-4 pb-3">
<div class="[&>svg]:w-32 [&>svg]:h-32">
{Phoenix.HTML.raw(@qr_svg)}
</div>
</div>
<div class="text-center pb-5">
<div class="text-xs font-mono opacity-50">{@pass_json["barcode"]["message"]}</div>
</div>
</div>
<%= if @back_fields != [] do %>
<div class="rounded-xl border border-base-content/20 bg-base-100 p-4 max-w-sm mx-auto mt-3">
<div class="text-xs font-semibold text-base-content/40 mb-2 uppercase tracking-wider">
Back of pass
</div>
<div :for={field <- @back_fields} class="py-1.5">
<div class="text-xs text-base-content/50">{field["label"]}</div>
<div class="text-sm font-mono">{field["value"]}</div>
</div>
</div>
<% end %>
"""
end
defp detect_apple_pass_type(pass_json) do
cond do
pass_json["boardingPass"] -> {:boarding_pass, pass_json["boardingPass"]}
pass_json["storeCard"] -> {:store_card, pass_json["storeCard"]}
pass_json["coupon"] -> {:coupon, pass_json["coupon"]}
pass_json["generic"] -> {:generic, pass_json["generic"]}
true -> {:event_ticket, pass_json["eventTicket"] || %{}}
end
end
attr(:pass_object, :map, required: true)
attr(:qr_svg, :string, required: true)
def google_pass_preview(assigns) do
holder_name =
assigns.pass_object["ticketHolderName"] ||
assigns.pass_object["passengerName"] ||
assigns.pass_object["accountName"] ||
get_in(assigns.pass_object, ["header", "defaultValue", "value"])
type_label =
get_in(assigns.pass_object, ["ticketType", "defaultValue", "value"]) ||
detect_google_type_label(assigns.pass_object)
assigns =
assigns
|> assign(:header_value, get_in(assigns.pass_object, ["header", "defaultValue", "value"]))
|> assign(:type_label, type_label)
|> assign(:holder_name, holder_name)
|> assign(:text_modules, assigns.pass_object["textModulesData"] || [])
|> assign(:bg_color, assigns.pass_object["hexBackgroundColor"] || "#FFFFFF")
~H"""
<div
class="rounded-2xl overflow-hidden shadow-xl w-full max-w-sm mx-auto border border-base-content/20"
style={"background: #{@bg_color};"}
>
<div class="px-5 pt-5 pb-3">
<div>
<div class="text-sm font-semibold">{@header_value}</div>
<div class="text-xs opacity-50">{@type_label}</div>
</div>
</div>
<div class="border-t border-current/10 mx-5" />
<div class="px-5 pt-3 pb-3">
<div class="text-2xl font-bold tracking-tight">{@holder_name}</div>
</div>
<div class="px-5 py-3 space-y-3">
<div :for={mod <- @text_modules} class="flex justify-between items-baseline">
<div class="text-[10px] uppercase tracking-wider font-semibold opacity-60">
{mod["header"]}
</div>
<div class="text-sm font-medium">{mod["body"]}</div>
</div>
</div>
<div class="border-t border-current/10 mx-5" />
<div class="flex justify-center pt-4 pb-3">
<div class="[&>svg]:w-32 [&>svg]:h-32">
{Phoenix.HTML.raw(@qr_svg)}
</div>
</div>
<div class="text-center pb-5">
<div class="text-xs font-mono opacity-50">{@pass_object["barcode"]["value"]}</div>
</div>
</div>
"""
end
defp detect_google_type_label(pass_object) do
cond do
pass_object["ticketHolderName"] -> "Event Ticket"
pass_object["passengerName"] -> "Boarding Pass"
pass_object["accountName"] -> "Loyalty Card"
true -> "Pass"
end
end
end
end