Compare commits

..

7 Commits

Author SHA1 Message Date
0b27c8f80b add qr code for invite link
Some checks failed
continuous-integration/drone/push Build is failing
2023-01-26 00:46:25 -05:00
0ad1ee47de improve formatting 2023-01-26 00:40:48 -05:00
8ea2b06487 use list for classes 2023-01-26 00:39:53 -05:00
1e4accec9d fix padding at bottom of page in chrome 2023-01-25 22:14:04 -05:00
076d5eea18 add styles to background element 2023-01-25 22:07:33 -05:00
5b6bd00047 improve locale 2023-01-25 21:52:30 -05:00
8dd471afa8 use topbar through npm 2023-01-25 21:17:45 -05:00
32 changed files with 345 additions and 313 deletions

View File

@ -2,6 +2,7 @@
- Update dependencies
- Show topbar on form submit/page refresh
- Make loading/reconnection less intrusive
- Add QR code for invite link
# v0.8.0
- Add search to catalog, ammo, container, tag and range index pages

View File

@ -24,7 +24,7 @@ import 'phoenix_html'
// Establish Phoenix Socket and LiveView configuration.
import { Socket } from 'phoenix'
import { LiveSocket } from 'phoenix_live_view'
import topbar from '../vendor/topbar'
import topbar from 'topbar'
import MaintainAttrs from './maintain_attrs'
import ShotLogChart from './shot_log_chart'
import Alpine from 'alpinejs'

View File

@ -1,157 +0,0 @@
/**
* @license MIT
* topbar 1.0.0, 2021-01-06
* https://buunguyen.github.io/topbar
* Copyright (c) 2021 Buu Nguyen
*/
(function (window, document) {
"use strict";
// https://gist.github.com/paulirish/1579671
(function () {
var lastTime = 0;
var vendors = ["ms", "moz", "webkit", "o"];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame =
window[vendors[x] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[vendors[x] + "CancelAnimationFrame"] ||
window[vendors[x] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
})();
var canvas,
progressTimerId,
fadeTimerId,
currentProgress,
showing,
addEvent = function (elem, type, handler) {
if (elem.addEventListener) elem.addEventListener(type, handler, false);
else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
else elem["on" + type] = handler;
},
options = {
autoRun: true,
barThickness: 3,
barColors: {
0: "rgba(26, 188, 156, .9)",
".25": "rgba(52, 152, 219, .9)",
".50": "rgba(241, 196, 15, .9)",
".75": "rgba(230, 126, 34, .9)",
"1.0": "rgba(211, 84, 0, .9)",
},
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, .6)",
className: null,
},
repaint = function () {
canvas.width = window.innerWidth;
canvas.height = options.barThickness * 5; // need space for shadow
var ctx = canvas.getContext("2d");
ctx.shadowBlur = options.shadowBlur;
ctx.shadowColor = options.shadowColor;
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
for (var stop in options.barColors)
lineGradient.addColorStop(stop, options.barColors[stop]);
ctx.lineWidth = options.barThickness;
ctx.beginPath();
ctx.moveTo(0, options.barThickness / 2);
ctx.lineTo(
Math.ceil(currentProgress * canvas.width),
options.barThickness / 2
);
ctx.strokeStyle = lineGradient;
ctx.stroke();
},
createCanvas = function () {
canvas = document.createElement("canvas");
var style = canvas.style;
style.position = "fixed";
style.top = style.left = style.right = style.margin = style.padding = 0;
style.zIndex = 100001;
style.display = "none";
if (options.className) canvas.classList.add(options.className);
document.body.appendChild(canvas);
addEvent(window, "resize", repaint);
},
topbar = {
config: function (opts) {
for (var key in opts)
if (options.hasOwnProperty(key)) options[key] = opts[key];
},
show: function () {
if (showing) return;
showing = true;
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
if (!canvas) createCanvas();
canvas.style.opacity = 1;
canvas.style.display = "block";
topbar.progress(0);
if (options.autoRun) {
(function loop() {
progressTimerId = window.requestAnimationFrame(loop);
topbar.progress(
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
);
})();
}
},
progress: function (to) {
if (typeof to === "undefined") return currentProgress;
if (typeof to === "string") {
to =
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
? currentProgress
: 0) + parseFloat(to);
}
currentProgress = to > 1 ? 1 : to;
repaint();
return currentProgress;
},
hide: function () {
if (!showing) return;
showing = false;
if (progressTimerId != null) {
window.cancelAnimationFrame(progressTimerId);
progressTimerId = null;
}
(function loop() {
if (topbar.progress("+.1") >= 1) {
canvas.style.opacity -= 0.05;
if (canvas.style.opacity <= 0.05) {
canvas.style.display = "none";
fadeTimerId = null;
return;
}
}
fadeTimerId = window.requestAnimationFrame(loop);
})();
},
};
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = topbar;
} else if (typeof define === "function" && define.amd) {
define(function () {
return topbar;
});
} else {
this.topbar = topbar;
}
}.call(this, window, document));

View File

@ -15,17 +15,18 @@ end
config :cannery, CanneryWeb.ViewHelpers, shibao_mode: System.get_env("SHIBAO_MODE") == "true"
# Set default locale
config :gettext, :default_locale, System.get_env("LOCALE") || "en_US"
config :gettext, :default_locale, System.get_env("LOCALE", "en_US")
maybe_ipv6 = if System.get_env("ECTO_IPV6") == "true", do: [:inet6], else: []
database_url =
if config_env() == :test do
System.get_env("TEST_DATABASE_URL") ||
System.get_env(
"TEST_DATABASE_URL",
"ecto://postgres:postgres@localhost/cannery_test#{System.get_env("MIX_TEST_PARTITION")}"
)
else
System.get_env("DATABASE_URL") ||
"ecto://postgres:postgres@cannery-db/cannery"
System.get_env("DATABASE_URL", "ecto://postgres:postgres@cannery-db/cannery")
end
host =
@ -40,7 +41,7 @@ interface =
config :cannery, Cannery.Repo,
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
pool_size: String.to_integer(System.get_env("POOL_SIZE", "10")),
socket_options: maybe_ipv6
config :cannery, CanneryWeb.Endpoint,
@ -49,10 +50,10 @@ config :cannery, CanneryWeb.Endpoint,
# See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
# for details about using IPv6 vs IPv4 and loopback vs public addresses.
ip: interface,
port: String.to_integer(System.get_env("PORT") || "4000")
port: String.to_integer(System.get_env("PORT", "4000"))
],
server: true,
registration: System.get_env("REGISTRATION") || "invite"
registration: System.get_env("REGISTRATION", "invite")
if config_env() == :prod do
# The secret key base is used to sign/encrypt cookies and other secrets.
@ -76,12 +77,12 @@ if config_env() == :prod do
config :cannery, Cannery.Mailer,
adapter: Swoosh.Adapters.SMTP,
relay: System.get_env("SMTP_HOST") || raise("No SMTP_HOST set!"),
port: System.get_env("SMTP_PORT") || 587,
port: System.get_env("SMTP_PORT", 587),
username: System.get_env("SMTP_USERNAME") || raise("No SMTP_USERNAME set!"),
password: System.get_env("SMTP_PASSWORD") || raise("No SMTP_PASSWORD set!"),
ssl: System.get_env("SMTP_SSL") == "true",
email_from: System.get_env("EMAIL_FROM") || "no-reply@#{System.get_env("HOST")}",
email_name: System.get_env("EMAIL_NAME") || "Cannery"
email_from: System.get_env("EMAIL_FROM", "no-reply@#{System.get_env("HOST")}"),
email_name: System.get_env("EMAIL_NAME", "Cannery")
# ## Using releases
#

View File

@ -55,13 +55,13 @@ defmodule CanneryWeb.Components.AmmoGroupCard do
<span class="rounded-lg title text-lg">
<%= gettext("Purchased on:") %>
<%= @ammo_group.purchased_on |> display_date() %>
<.date date={@ammo_group.purchased_on} />
</span>
<%= if @ammo_group |> Ammo.get_last_used_shot_group() do %>
<span class="rounded-lg title text-lg">
<%= gettext("Last used on:") %>
<%= @ammo_group |> Ammo.get_last_used_shot_group() |> Map.get(:date) |> display_date() %>
<.date date={@ammo_group |> Ammo.get_last_used_shot_group() |> Map.get(:date)} />
</span>
<% end %>

View File

@ -157,7 +157,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
{purchased_on,
~H"""
<%= @purchased_on |> display_date() %>
<.date date={@purchased_on} />
"""}
end
@ -173,7 +173,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
{last_shot_group_date,
~H"""
<%= if @last_shot_group_date do %>
<%= @last_shot_group_date |> display_date() %>
<.date date={@last_shot_group_date} />
<% else %>
<%= gettext("Never used") %>
<% end %>

View File

@ -27,8 +27,14 @@ defmodule CanneryWeb.Components.InviteCard do
<%= if @invite.disabled_at |> is_nil() do %>
<h2 class="title text-md">
<%= gettext("Uses Left:") %>
<%= @invite.uses_left || "Unlimited" %>
<%= if @invite.uses_left do %>
<%= gettext(
"Uses Left: %{uses_left}",
uses_left: @invite.uses_left
) %>
<% else %>
<%= gettext("Uses Left: Unlimited") %>
<% end %>
</h2>
<% else %>
<h2 class="title text-md">
@ -36,6 +42,11 @@ defmodule CanneryWeb.Components.InviteCard do
</h2>
<% end %>
<.qr_code
content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)}
filename={@invite.name}
/>
<div class="flex flex-row flex-wrap justify-center items-center">
<code
id={"code-#{@invite.id}"}

View File

@ -103,7 +103,11 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
{ammo_type_name, name_block}
end
defp get_row_value(:date, %{date: date}, _extra_data), do: date |> display_date()
defp get_row_value(:date, assigns = %{date: _date}, _extra_data) do
~H"""
<.date date={@date} />
"""
end
defp get_row_value(:actions, shot_group, %{actions: actions}) do
assigns = %{actions: actions, shot_group: shot_group}

View File

@ -4,7 +4,7 @@
<tr>
<%= for %{key: key, label: label} = column <- @columns do %>
<%= if column |> Map.get(:sortable, true) do %>
<th class={"p-2 #{column[:class]}"}>
<th class={["p-2", column[:class]]}>
<span
class="cursor-pointer flex justify-center items-center space-x-2"
phx-click="sort_by"
@ -26,7 +26,7 @@
</span>
</th>
<% else %>
<th class={"p-2 cursor-not-allowed #{column[:class]}"}>
<th class={["p-2 cursor-not-allowed", column[:class]]}>
<%= label %>
</th>
<% end %>
@ -37,7 +37,7 @@
<%= for {values, i} <- @rows |> Enum.with_index() do %>
<tr class={if i |> Integer.is_even(), do: @row_class, else: @alternate_row_class}>
<%= for %{key: key} = value <- @columns do %>
<td class={"p-2 #{value[:class]}"}>
<td class={["p-2", value[:class]]}>
<%= case values |> Map.get(key) do %>
<% {_custom_sort_value, value} -> %>
<%= value %>

View File

@ -23,16 +23,23 @@ defmodule CanneryWeb.Components.UserCard do
<h3 class="px-4 py-2 rounded-lg title text-lg">
<p>
<%= if @user.confirmed_at |> is_nil() do %>
Email unconfirmed
<%= if @user.confirmed_at do %>
<%= gettext(
"User was confirmed at%{confirmed_datetime}",
confirmed_datetime: ""
) %>
<.datetime datetime={@user.confirmed_at} />
<% else %>
User was confirmed at <%= @user.confirmed_at |> display_datetime() %>
<%= gettext("Email unconfirmed") %>
<% end %>
</p>
<p>
<%= gettext("User registered on") %>
<%= @user.inserted_at |> display_datetime() %>
<%= gettext(
"User registered on%{registered_datetime}",
registered_datetime: ""
) %>
<.datetime datetime={@user.inserted_at} />
</p>
</h3>

View File

@ -116,7 +116,12 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
value =
case key do
:date ->
{date, date |> display_date()}
assigns = %{date: date}
{date,
~H"""
<.date date={@date} />
"""}
:actions ->
~H"""

View File

@ -28,7 +28,7 @@
<span class="rounded-lg title text-lg">
<%= gettext("Purchased on:") %>
<%= @ammo_group.purchased_on |> display_date() %>
<.date date={@ammo_group.purchased_on} />
</span>
<%= if @ammo_group.price_paid do %>

View File

@ -124,7 +124,7 @@
</h3>
<span class="text-primary-600">
<%= @ammo_type.inserted_at |> display_datetime() %>
<.datetime datetime={@ammo_type.inserted_at} />
</span>
<%= if @avg_cost_per_round do %>

View File

@ -11,15 +11,17 @@ defmodule CanneryWeb.Router do
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :fetch_current_user
plug :put_user_locale, default: Application.compile_env(:gettext, :default_locale, "en_US")
plug :put_user_locale
end
defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, default: default) do
defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, _opts) do
default = Application.fetch_env!(:gettext, :default_locale)
Gettext.put_locale(locale || default)
conn |> put_session(:locale, locale || default)
end
defp put_user_locale(conn, default: default) do
defp put_user_locale(conn, _opts) do
default = Application.fetch_env!(:gettext, :default_locale)
Gettext.put_locale(default)
conn |> put_session(:locale, default)
end

View File

@ -1,4 +1,4 @@
<main class="mb-8 min-w-full">
<main class="pb-8 min-w-full">
<header>
<.topbar current_user={assigns[:current_user]}></.topbar>

View File

@ -10,8 +10,8 @@
type="image/jpg"
href={Routes.static_path(@conn, "/images/cannery.svg")}
/>
<.live_title suffix=" | Cannery">
<%= assigns[:page_title] || "Cannery" %>
<.live_title suffix={" | #{gettext("Cannery")}"}>
<%= assigns[:page_title] || gettext("Cannery") %>
</.live_title>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/css/app.css")} />
<script
@ -23,7 +23,7 @@
</script>
</head>
<body class="m-0 p-0 w-full h-full bg-white">
<body class="m-0 p-0 w-full h-full subpixel-antialiased">
<%= @inner_content %>
</body>
</html>

View File

@ -18,7 +18,7 @@ defmodule CanneryWeb.ErrorHelpers do
~H"""
<%= for error <- Keyword.get_values(@form.errors, @field) do %>
<span class={"invalid-feedback #{@extra_class}"} phx-feedback-for={input_name(@form, @field)}>
<span class={["invalid-feedback", @extra_class]} phx-feedback-for={input_name(@form, @field)}>
<%= translate_error(error) %>
</span>
<% end %>

View File

@ -5,64 +5,62 @@ defmodule CanneryWeb.ViewHelpers do
:view`
"""
import Phoenix.Component
@id_length 16
use Phoenix.Component
@doc """
Returns a <time> element that renders the naivedatetime in the user's local
timezone with Alpine.js
Phoenix.Component for a <time> element that renders the naivedatetime in the
user's local timezone with Alpine.js
"""
@spec display_datetime(NaiveDateTime.t() | nil) :: Phoenix.LiveView.Rendered.t()
def display_datetime(nil), do: ""
def display_datetime(datetime) do
assigns = %{
id: :crypto.strong_rand_bytes(@id_length) |> Base.url_encode64(),
datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
}
attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil"
def datetime(assigns) do
~H"""
<time
id={@id}
datetime={@datetime}
x-data={"{
date:
Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
.format(new Date(\"#{@datetime}\"))
}"}
x-text="date"
>
<%= @datetime %>
</time>
<%= if @datetime do %>
<time
datetime={cast_datetime(@datetime)}
x-data={"{
datetime:
Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
.format(new Date(\"#{cast_datetime(@datetime)}\"))
}"}
x-text="datetime"
>
<%= cast_datetime(@datetime) %>
</time>
<% end %>
"""
end
@spec cast_datetime(NaiveDateTime.t() | nil) :: String.t()
defp cast_datetime(%NaiveDateTime{} = datetime) do
datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
end
defp cast_datetime(_datetime), do: ""
@doc """
Returns a <date> element that renders the Date in the user's local
timezone with Alpine.js
Phoenix.Component for a <date> element that renders the Date in the user's
local timezone with Alpine.js
"""
@spec display_date(Date.t() | nil) :: Phoenix.LiveView.Rendered.t()
def display_date(nil), do: ""
def display_date(date) do
assigns = %{
id: :crypto.strong_rand_bytes(@id_length) |> Base.url_encode64(),
date: date |> Date.to_iso8601(:extended)
}
attr :date, :any, required: true, doc: "A `Date` struct or nil"
def date(assigns) do
~H"""
<time
id={@id}
datetime={@date}
x-data={"{
date:
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\"))
}"}
x-text="date"
>
<%= @date %>
</time>
<%= if @date do %>
<time
datetime={@date |> Date.to_iso8601(:extended)}
x-data={"{
date:
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'})
.format(new Date(\"#{@date |> Date.to_iso8601(:extended)}\"))
}"}
x-text="date"
>
<%= @date |> Date.to_iso8601(:extended) %>
</time>
<% end %>
"""
end
@ -70,12 +68,44 @@ defmodule CanneryWeb.ViewHelpers do
Displays emoji as text emoji if SHIBAO_MODE is set to true :)
"""
@spec display_emoji(String.t()) :: String.t()
def display_emoji("😔"),
do:
if(Application.get_env(:cannery, CanneryWeb.ViewHelpers)[:shibao_mode], do: "q_q", else: "😔")
def display_emoji("😔") do
if Application.get_env(:cannery, CanneryWeb.ViewHelpers)[:shibao_mode], do: "q_q", else: "😔"
end
def display_emoji(other_emoji), do: other_emoji
@doc """
Displays content in a QR code as a base64 encoded PNG
"""
@spec qr_code_image(String.t()) :: String.t()
@spec qr_code_image(String.t(), width :: non_neg_integer()) :: String.t()
def qr_code_image(content, width \\ 384) do
img_data =
content
|> EQRCode.encode()
|> EQRCode.png(width: width)
|> Base.encode64()
"data:image/png;base64," <> img_data
end
@doc """
Creates a downloadable QR Code element
"""
attr :content, :string, required: true
attr :filename, :string, default: "qrcode", doc: "filename without .png extension"
attr :image_class, :string, default: "w-64 h-max"
attr :width, :integer, default: 384, doc: "width of png to generate"
def qr_code(assigns) do
~H"""
<a href={qr_code_image(@content)} download={@filename <> ".png"}>
<img class={@image_class} alt={@filename} src={qr_code_image(@content)} />
</a>
"""
end
@doc """
Get a random color in `#ffffff` hex format

View File

@ -70,6 +70,7 @@ defmodule Cannery.MixProject do
{:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"},
{:ecto_psql_extras, "~> 0.6"},
{:eqrcode, "~> 0.1.10"},
{:credo, "~> 1.5", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}
]

View File

@ -16,6 +16,7 @@
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
"elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
"eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
"expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},

View File

@ -194,7 +194,7 @@ msgstr "Brandmunition"
msgid "Instance Information"
msgstr "Instanzinformationen"
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr "Einladung deaktiviert"
@ -441,11 +441,6 @@ msgstr "Art:"
msgid "Users"
msgstr "Benutzer"
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr "Verbleibende Nutzung:"
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -734,11 +729,6 @@ msgstr "Kopien"
msgid "Added on:"
msgstr "Hinzugefügt am:"
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr "Benutzer registriert am"
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1146,3 +1136,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format, fuzzy
msgid "Cannery"
msgstr ""
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format, fuzzy
msgid "User registered on%{registered_datetime}"
msgstr "Benutzer registriert am"
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: %{uses_left}"
msgstr "Verbleibende Nutzung:"
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: Unlimited"
msgstr "Verbleibende Nutzung:"

View File

@ -208,7 +208,7 @@ msgstr "Schüsse erfolgreich dokumentiert"
msgid "Are you sure you want to unstage this ammo?"
msgstr "Sind sie sicher, dass Sie diese Munition demarkieren möchten?"
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"

View File

@ -179,7 +179,7 @@ msgstr ""
msgid "Instance Information"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr ""
@ -424,11 +424,6 @@ msgstr ""
msgid "Users"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr ""
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -717,11 +712,6 @@ msgstr ""
msgid "Added on:"
msgstr ""
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1129,3 +1119,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format
msgid "Cannery"
msgstr ""
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format
msgid "User registered on%{registered_datetime}"
msgstr ""
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format
msgid "Uses Left: %{uses_left}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format
msgid "Uses Left: Unlimited"
msgstr ""

View File

@ -179,7 +179,7 @@ msgstr ""
msgid "Instance Information"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr ""
@ -424,11 +424,6 @@ msgstr ""
msgid "Users"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr ""
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -717,11 +712,6 @@ msgstr ""
msgid "Added on:"
msgstr ""
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1129,3 +1119,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format, fuzzy
msgid "Cannery"
msgstr ""
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format, fuzzy
msgid "User registered on%{registered_datetime}"
msgstr ""
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: %{uses_left}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: Unlimited"
msgstr ""

View File

@ -187,7 +187,7 @@ msgstr ""
msgid "Are you sure you want to unstage this ammo?"
msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"

View File

@ -194,7 +194,7 @@ msgstr "Incendiaria"
msgid "Instance Information"
msgstr "Información de Instancia"
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr "Invitación Desactivada"
@ -442,11 +442,6 @@ msgstr "Tipo:"
msgid "Users"
msgstr "Usuarios"
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr "Usos Restantes:"
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -735,11 +730,6 @@ msgstr "Copias"
msgid "Added on:"
msgstr "Añadido en:"
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr "Usuario registrado en"
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1148,3 +1138,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format, fuzzy
msgid "Cannery"
msgstr "Logo de cannery"
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format, fuzzy
msgid "User registered on%{registered_datetime}"
msgstr "Usuario registrado en"
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: %{uses_left}"
msgstr "Usos Restantes:"
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: Unlimited"
msgstr "Usos Restantes:"

View File

@ -207,7 +207,7 @@ msgstr "Tiros registrados exitosamente"
msgid "Are you sure you want to unstage this ammo?"
msgstr "Está seguro que desea desmontar esta munición?"
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"

View File

@ -194,7 +194,7 @@ msgstr "Incendiaire"
msgid "Instance Information"
msgstr "Information de linstance"
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr "Invitation désactivée"
@ -443,11 +443,6 @@ msgstr "Type:"
msgid "Users"
msgstr "Utilisateurs"
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr "Utilisations restantes:"
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -736,11 +731,6 @@ msgstr "Exemplaires"
msgid "Added on:"
msgstr "Ajouté le:"
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr "Utilisateur·ice enregistré·e le"
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1149,3 +1139,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format, fuzzy
msgid "Cannery"
msgstr "Logo de Cannery"
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format, fuzzy
msgid "User registered on%{registered_datetime}"
msgstr "Utilisateur·ice enregistré·e le"
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: %{uses_left}"
msgstr "Utilisations restantes:"
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: Unlimited"
msgstr "Utilisations restantes:"

View File

@ -209,7 +209,7 @@ msgstr "Tirs enregistré avec succès"
msgid "Are you sure you want to unstage this ammo?"
msgstr "Êtes-vous certain·e de vouloir désélectionner cette munition?"
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"

View File

@ -190,7 +190,7 @@ msgstr ""
msgid "Instance Information"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:35
#: lib/cannery_web/components/invite_card.ex:41
#, elixir-autogen, elixir-format
msgid "Invite Disabled"
msgstr ""
@ -435,11 +435,6 @@ msgstr ""
msgid "Users"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:30
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr ""
#: lib/cannery_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format
msgid "Uses left"
@ -728,11 +723,6 @@ msgstr ""
msgid "Added on:"
msgstr ""
#: lib/cannery_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/cannery_web/templates/user_registration/new.html.heex:37
#: lib/cannery_web/templates/user_settings/edit.html.heex:130
#, elixir-autogen, elixir-format
@ -1140,3 +1130,34 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Spanish"
msgstr ""
#: lib/cannery_web/templates/layout/root.html.heex:13
#: lib/cannery_web/templates/layout/root.html.heex:14
#, elixir-autogen, elixir-format, fuzzy
msgid "Cannery"
msgstr ""
#: lib/cannery_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "Email unconfirmed"
msgstr ""
#: lib/cannery_web/components/user_card.ex:38
#, elixir-autogen, elixir-format, fuzzy
msgid "User registered on%{registered_datetime}"
msgstr ""
#: lib/cannery_web/components/user_card.ex:27
#, elixir-autogen, elixir-format
msgid "User was confirmed at%{confirmed_datetime}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:31
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: %{uses_left}"
msgstr ""
#: lib/cannery_web/components/invite_card.ex:36
#, elixir-autogen, elixir-format, fuzzy
msgid "Uses Left: Unlimited"
msgstr ""

View File

@ -198,7 +198,7 @@ msgstr ""
msgid "Are you sure you want to unstage this ammo?"
msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"

View File

@ -187,7 +187,7 @@ msgstr ""
msgid "Are you sure you want to unstage this ammo?"
msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:137
#: lib/cannery_web/live/ammo_group_live/show.ex:142
#: lib/cannery_web/live/range_live/index.html.heex:118
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?"