1 Commits

Author SHA1 Message Date
41b9d87278 add german
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-27 01:18:44 -05:00
87 changed files with 1420 additions and 2304 deletions

View File

@ -1,4 +1,4 @@
FROM elixir:1.14.1-alpine AS build FROM elixir:1.14-alpine AS build
# install build dependencies # install build dependencies
RUN apk add --no-cache build-base npm git python3 RUN apk add --no-cache build-base npm git python3
@ -37,7 +37,7 @@ RUN mix do compile, release
FROM alpine:latest AS app FROM alpine:latest AS app
RUN apk upgrade --no-cache && \ RUN apk upgrade --no-cache && \
apk add --no-cache bash openssl libssl1.1 libcrypto1.1 libgcc libstdc++ ncurses-libs apk add --no-cache bash openssl libgcc libstdc++ ncurses-libs
WORKDIR /app WORKDIR /app

View File

@ -25,13 +25,12 @@ $fa-font-path: "@fortawesome/fontawesome-free/webfonts";
100% { scale: 1.0; opacity: 1; } 100% { scale: 1.0; opacity: 1; }
} }
// disconnect toast .phx-connected > #disconnect, #loading {
.phx-connected > #disconnect {
opacity: 0 !important; opacity: 0 !important;
pointer-events: none; pointer-events: none;
} }
.phx-error > #disconnect { .phx-loading:not(.phx-error) > #loading, .phx-error > #disconnect {
opacity: 0.95 !important; opacity: 0.95 !important;
} }

View File

@ -25,7 +25,7 @@ import 'phoenix_html'
// Establish Phoenix Socket and LiveView configuration. // Establish Phoenix Socket and LiveView configuration.
import { Socket } from 'phoenix' import { Socket } from 'phoenix'
import { LiveSocket } from 'phoenix_live_view' import { LiveSocket } from 'phoenix_live_view'
import topbar from 'topbar' import topbar from '../vendor/topbar'
import MaintainAttrs from './maintain_attrs' import MaintainAttrs from './maintain_attrs'
import Alpine from 'alpinejs' import Alpine from 'alpinejs'
@ -50,8 +50,6 @@ Alpine.start()
topbar.config({ barThickness: 1, barColors: { 0: '#fff' }, shadowColor: 'rgba(0, 0, 0, .3)' }) topbar.config({ barThickness: 1, barColors: { 0: '#fff' }, shadowColor: 'rgba(0, 0, 0, .3)' })
window.addEventListener('phx:page-loading-start', info => topbar.show()) window.addEventListener('phx:page-loading-start', info => topbar.show())
window.addEventListener('phx:page-loading-stop', info => topbar.hide()) window.addEventListener('phx:page-loading-stop', info => topbar.hide())
window.addEventListener('submit', info => topbar.show())
window.addEventListener('beforeunload', info => topbar.show())
// connect if there are any LiveViews on the page // connect if there are any LiveViews on the page
liveSocket.connect() liveSocket.connect()

1482
assets/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,6 @@
"repository": {}, "repository": {},
"description": " ", "description": " ",
"license": "MIT", "license": "MIT",
"engines": {
"node": "18.12.1",
"npm": "8.19.2"
},
"scripts": { "scripts": {
"deploy": "NODE_ENV=production webpack --mode production", "deploy": "NODE_ENV=production webpack --mode production",
"watch": "webpack --mode development --watch --watch-options-stdin", "watch": "webpack --mode development --watch --watch-options-stdin",
@ -30,14 +26,16 @@
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1", "css-minimizer-webpack-plugin": "^3.4.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"hard-source-webpack-plugin": "^0.13.1",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
"node-sass": "^7.0.1",
"postcss": "^8.4.13", "postcss": "^8.4.13",
"postcss-import": "^14.1.0", "postcss-import": "^14.1.0",
"postcss-loader": "^6.2.1", "postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.5.0", "postcss-preset-env": "^7.5.0",
"sass": "^1.56.0",
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"standard": "^17.0.0", "standard": "^17.0.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.24", "tailwindcss": "^3.0.24",
"terser-webpack-plugin": "^5.3.1", "terser-webpack-plugin": "^5.3.1",
"webpack": "^5.72.0", "webpack": "^5.72.0",

157
assets/vendor/topbar.js vendored Normal file
View File

@ -0,0 +1,157 @@
/**
* @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

@ -1,36 +0,0 @@
# v0.1.7
- Update dependencies
- Show topbar on form submit/page refresh
- Make loading/reconnection less intrusive
- Add QR code for invite link
# v0.1.6
- fix formatting in note/context/step contents
- add json export for data
# v0.1.5
- fix overflow on note/contexts/step contents
# v0.1.4
- fix docker-compose
- fix newlines in note/context/step contents
- fix user invite page
- improve tagging logic
# v0.1.3
- backlink to other notes in notes
- search tags on click
# v0.1.2
- fix more typos
- add to faq
- check for slug uniqueness before submitting
# v0.1.1
- improve search a whole lot
- improve table information for notes and contexts
- fix some typos
- use project version on homepage
# v0.1.0
- initial release >:3c

View File

@ -13,18 +13,17 @@ if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
end end
# Set default locale # 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: [] maybe_ipv6 = if System.get_env("ECTO_IPV6") == "true", do: [:inet6], else: []
database_url = database_url =
if config_env() == :test do if config_env() == :test do
System.get_env( System.get_env("TEST_DATABASE_URL") ||
"TEST_DATABASE_URL",
"ecto://postgres:postgres@localhost/memex_test#{System.get_env("MIX_TEST_PARTITION")}" "ecto://postgres:postgres@localhost/memex_test#{System.get_env("MIX_TEST_PARTITION")}"
)
else else
System.get_env("DATABASE_URL", "ecto://postgres:postgres@memex-db/memex") System.get_env("DATABASE_URL") ||
"ecto://postgres:postgres@memex-db/memex"
end end
host = host =
@ -39,7 +38,7 @@ interface =
config :memex, Memex.Repo, config :memex, Memex.Repo,
# ssl: true, # ssl: true,
url: database_url, 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 socket_options: maybe_ipv6
config :memex, MemexWeb.Endpoint, config :memex, MemexWeb.Endpoint,
@ -48,10 +47,10 @@ config :memex, MemexWeb.Endpoint,
# See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
# for details about using IPv6 vs IPv4 and loopback vs public addresses. # for details about using IPv6 vs IPv4 and loopback vs public addresses.
ip: interface, ip: interface,
port: String.to_integer(System.get_env("PORT", "4000")) port: String.to_integer(System.get_env("PORT") || "4000")
], ],
server: true, server: true,
registration: System.get_env("REGISTRATION", "invite") registration: System.get_env("REGISTRATION") || "invite"
if config_env() == :prod do if config_env() == :prod do
# The secret key base is used to sign/encrypt cookies and other secrets. # The secret key base is used to sign/encrypt cookies and other secrets.
@ -63,7 +62,7 @@ if config_env() == :prod do
System.get_env("SECRET_KEY_BASE") || System.get_env("SECRET_KEY_BASE") ||
raise """ raise """
environment variable SECRET_KEY_BASE is missing. environment variable SECRET_KEY_BASE is missing.
You can generate one by running: mix phx.gen.secret You can generate one by calling: mix phx.gen.secret
""" """
config :memex, MemexWeb.Endpoint, secret_key_base: secret_key_base config :memex, MemexWeb.Endpoint, secret_key_base: secret_key_base
@ -75,12 +74,12 @@ if config_env() == :prod do
config :memex, Memex.Mailer, config :memex, Memex.Mailer,
adapter: Swoosh.Adapters.SMTP, adapter: Swoosh.Adapters.SMTP,
relay: System.get_env("SMTP_HOST") || raise("No SMTP_HOST set!"), 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!"), username: System.get_env("SMTP_USERNAME") || raise("No SMTP_USERNAME set!"),
password: System.get_env("SMTP_PASSWORD") || raise("No SMTP_PASSWORD set!"), password: System.get_env("SMTP_PASSWORD") || raise("No SMTP_PASSWORD set!"),
ssl: System.get_env("SMTP_SSL") == "true", ssl: System.get_env("SMTP_SSL") == "true",
email_from: System.get_env("EMAIL_FROM", "no-reply@#{System.get_env("HOST")}"), email_from: System.get_env("EMAIL_FROM") || "no-reply@#{System.get_env("HOST")}",
email_name: System.get_env("EMAIL_NAME", "memEx") email_name: System.get_env("EMAIL_NAME") || "Memex"
# ## Using releases # ## Using releases
# #

View File

@ -77,14 +77,14 @@ Check them out!
For development, I recommend setting environment variables with For development, I recommend setting environment variables with
[direnv](https://direnv.net). [direnv](https://direnv.net).
By default, memEx will always bind to all external IPv4 and IPv6 addresses in By default, Memex will always bind to all external IPv4 and IPv6 addresses in
`dev` and `prod` mode, respectively. If you would like to use different values, `dev` and `prod` mode, respectively. If you would like to use different values,
they will need to be overridden in `config/dev.exs` and `config/runtime.exs` for they will need to be overridden in `config/dev.exs` and `config/runtime.exs` for
`dev` and `prod` modes, respectively. `dev` and `prod` modes, respectively.
## `MIX_ENV=dev` ## `MIX_ENV=dev`
In `dev` mode, memEx will listen for these environment variables at runtime. In `dev` mode, Memex will listen for these environment variables at runtime.
- `HOST`: External url to generate links with. Set this especially if you're - `HOST`: External url to generate links with. Set this especially if you're
behind a reverse proxy. Defaults to `localhost`. External URLs will always be behind a reverse proxy. Defaults to `localhost`. External URLs will always be
@ -100,7 +100,7 @@ In `dev` mode, memEx will listen for these environment variables at runtime.
## `MIX_ENV=test` ## `MIX_ENV=test`
In `test` mode (or in the Docker container), memEx will listen for the same environment variables as dev mode, but also include the following at runtime: In `test` mode (or in the Docker container), Memex will listen for the same environment variables as dev mode, but also include the following at runtime:
- `TEST_DATABASE_URL`: REPLACES `DATABASE_URL`. Controls the database url to - `TEST_DATABASE_URL`: REPLACES `DATABASE_URL`. Controls the database url to
connect to. Defaults to `ecto://postgres:postgres@localhost/memex_test`. connect to. Defaults to `ecto://postgres:postgres@localhost/memex_test`.
@ -110,7 +110,7 @@ In `test` mode (or in the Docker container), memEx will listen for the same envi
## `MIX_ENV=prod` ## `MIX_ENV=prod`
In `prod` mode (or in the Docker container), memEx will listen for the same environment variables as dev mode, but also include the following at runtime: In `prod` mode (or in the Docker container), Memex will listen for the same environment variables as dev mode, but also include the following at runtime:
- `SECRET_KEY_BASE`: Secret key base used to sign cookies. Must be generated - `SECRET_KEY_BASE`: Secret key base used to sign cookies. Must be generated
with `docker run -it shibaobun/memex mix phx.gen.secret` and set for server to start. with `docker run -it shibaobun/memex mix phx.gen.secret` and set for server to start.
@ -121,4 +121,4 @@ In `prod` mode (or in the Docker container), memEx will listen for the same envi
- `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`. - `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`.
- `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to - `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to
`no-reply@HOST` where `HOST` was previously defined. `no-reply@HOST` where `HOST` was previously defined.
- `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "memEx". - `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "Memex".

10
de.tbx
View File

@ -1,10 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE martif PUBLIC "ISO 12200:1999A//DTD MARTIF core (DXFcdV04)//EN" "TBXcdv04.dtd">
<martif type="TBX">
<martifHeader>
<fileDesc>
<sourceDesc><p>Translate Toolkit</p></sourceDesc>
</fileDesc>
</martifHeader>
<text><body></body></text>
</martif>

View File

@ -2,7 +2,8 @@ version: '3'
services: services:
memex: memex:
image: shibaobun/memex build:
context: .
container_name: memex container_name: memex
restart: always restart: always
environment: environment:
@ -24,8 +25,8 @@ services:
# - SMTP_SSL=false # - SMTP_SSL=false
# optional, default is format below # optional, default is format below
# - EMAIL_FROM=no-reply@memex.example.tld # - EMAIL_FROM=no-reply@memex.example.tld
# optional, default is "memEx" # optional, default is "Memex"
# - EMAIL_NAME=memEx # - EMAIL_NAME=Memex
expose: expose:
- "4000" - "4000"
depends_on: depends_on:

View File

@ -19,8 +19,8 @@ defmodule Memex.Email do
@spec base_email(User.t(), String.t()) :: t() @spec base_email(User.t(), String.t()) :: t()
defp base_email(%User{email: email}, subject) do defp base_email(%User{email: email}, subject) do
from = Application.get_env(:memex, Memex.Mailer)[:email_from] || "noreply@localhost" from = Application.get_env(:Memex, Memex.Mailer)[:email_from] || "noreply@localhost"
name = Application.get_env(:memex, Memex.Mailer)[:email_name] name = Application.get_env(:Memex, Memex.Mailer)[:email_name]
new() |> to(email) |> from({name, from}) |> subject(subject) new() |> to(email) |> from({name, from}) |> subject(subject)
end end

View File

@ -9,16 +9,6 @@ defmodule Memex.Accounts.User do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.Invites.Invite alias Memex.Invites.Invite
@derive {Jason.Encoder,
only: [
:id,
:email,
:confirmed_at,
:role,
:locale,
:inserted_at,
:updated_at
]}
@derive {Inspect, except: [:password]} @derive {Inspect, except: [:password]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id

View File

@ -4,6 +4,7 @@ defmodule Memex.Contexts do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Contexts.Context, Repo} alias Memex.{Accounts.User, Contexts.Context, Repo}
@doc """ @doc """
@ -34,13 +35,13 @@ defmodule Memex.Contexts do
where: c.user_id == ^user_id, where: c.user_id == ^user_id,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -75,13 +76,13 @@ defmodule Memex.Contexts do
where: c.visibility == :public, where: c.visibility == :public,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -228,4 +229,18 @@ defmodule Memex.Contexts do
def change_context(%Context{} = context, attrs \\ %{}, user) do def change_context(%Context{} = context, attrs \\ %{}, user) do
context |> Context.update_changeset(attrs, user) context |> Context.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Note
"""
@spec get_tags_string(Context.t() | Context.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Context{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -7,17 +7,8 @@ defmodule Memex.Contexts.Context do
import Ecto.Changeset import Ecto.Changeset
import MemexWeb.Gettext import MemexWeb.Gettext
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.Accounts.User
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "contexts" do schema "contexts" do
@ -36,7 +27,7 @@ defmodule Memex.Contexts.Context do
slug: slug(), slug: slug(),
content: String.t(), content: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t() | nil, tags_string: String.t(),
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -58,8 +49,6 @@ defmodule Memex.Contexts.Context do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :content, :user_id, :visibility]) |> validate_required([:slug, :content, :user_id, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
@spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset()
@ -71,42 +60,18 @@ defmodule Memex.Contexts.Context do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :content, :visibility]) |> validate_required([:slug, :content, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, attrs) do defp cast_tags_string(changeset, %{"tags_string" => tags_string})
changeset when tags_string |> is_binary() do
|> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string()) tags =
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp process_tags(_other_tags_string), do: [] defp cast_tags_string(changeset, _attrs), do: changeset
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -4,6 +4,7 @@ defmodule Memex.Notes do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Notes.Note, Repo} alias Memex.{Accounts.User, Notes.Note, Repo}
@doc """ @doc """
@ -34,13 +35,13 @@ defmodule Memex.Notes do
where: n.user_id == ^user_id, where: n.user_id == ^user_id,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -74,13 +75,13 @@ defmodule Memex.Notes do
where: n.visibility == :public, where: n.visibility == :public,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -228,4 +229,18 @@ defmodule Memex.Notes do
def change_note(%Note{} = note, attrs \\ %{}, user) do def change_note(%Note{} = note, attrs \\ %{}, user) do
note |> Note.update_changeset(attrs, user) note |> Note.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Note
"""
@spec get_tags_string(Note.t() | Note.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Note{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -6,17 +6,8 @@ defmodule Memex.Notes.Note do
import Ecto.Changeset import Ecto.Changeset
import MemexWeb.Gettext import MemexWeb.Gettext
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.Accounts.User
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "notes" do schema "notes" do
@ -35,7 +26,7 @@ defmodule Memex.Notes.Note do
slug: slug(), slug: slug(),
content: String.t(), content: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t() | nil, tags_string: String.t(),
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -57,8 +48,6 @@ defmodule Memex.Notes.Note do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :content, :user_id, :visibility]) |> validate_required([:slug, :content, :user_id, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
@spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset()
@ -70,42 +59,18 @@ defmodule Memex.Notes.Note do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :content, :visibility]) |> validate_required([:slug, :content, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, attrs) do defp cast_tags_string(changeset, %{"tags_string" => tags_string})
changeset when tags_string |> is_binary() do
|> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string()) tags =
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp process_tags(_other_tags_string), do: [] defp cast_tags_string(changeset, _attrs), do: changeset
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -4,6 +4,7 @@ defmodule Memex.Pipelines do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Pipelines.Pipeline, Repo} alias Memex.{Accounts.User, Pipelines.Pipeline, Repo}
@doc """ @doc """
@ -34,13 +35,13 @@ defmodule Memex.Pipelines do
where: p.user_id == ^user_id, where: p.user_id == ^user_id,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -74,13 +75,13 @@ defmodule Memex.Pipelines do
where: p.visibility == :public, where: p.visibility == :public,
where: where:
fragment( fragment(
"search @@ websearch_to_tsquery('english', ?)", "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
^trimmed_search ^trimmed_search
), ),
order_by: { order_by: {
:desc, :desc,
fragment( fragment(
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)", "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
^trimmed_search ^trimmed_search
) )
} }
@ -230,4 +231,18 @@ defmodule Memex.Pipelines do
def change_pipeline(%Pipeline{} = pipeline, attrs \\ %{}, user) do def change_pipeline(%Pipeline{} = pipeline, attrs \\ %{}, user) do
pipeline |> Pipeline.update_changeset(attrs, user) pipeline |> Pipeline.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Pipeline
"""
@spec get_tags_string(Pipeline.t() | Pipeline.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Pipeline{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -6,18 +6,8 @@ defmodule Memex.Pipelines.Pipeline do
import Ecto.Changeset import Ecto.Changeset
import MemexWeb.Gettext import MemexWeb.Gettext
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Steps.Step, Repo} alias Memex.{Accounts.User, Pipelines.Steps.Step}
@derive {Jason.Encoder,
only: [
:slug,
:description,
:tags,
:visibility,
:inserted_at,
:steps,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "pipelines" do schema "pipelines" do
@ -38,7 +28,7 @@ defmodule Memex.Pipelines.Pipeline do
slug: slug(), slug: slug(),
description: String.t(), description: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t() | nil, tags_string: String.t(),
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -60,8 +50,6 @@ defmodule Memex.Pipelines.Pipeline do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :user_id, :visibility]) |> validate_required([:slug, :user_id, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
@spec update_changeset(t(), attrs :: map(), User.t()) :: changeset() @spec update_changeset(t(), attrs :: map(), User.t()) :: changeset()
@ -73,42 +61,18 @@ defmodule Memex.Pipelines.Pipeline do
message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted") message: dgettext("errors", "invalid format: only numbers, letters and hyphen are accepted")
) )
|> validate_required([:slug, :visibility]) |> validate_required([:slug, :visibility])
|> unique_constraint(:slug)
|> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, attrs) do defp cast_tags_string(changeset, %{"tags_string" => tags_string})
changeset when tags_string |> is_binary() do
|> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string()) tags =
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp process_tags(_other_tags_string), do: [] defp cast_tags_string(changeset, _attrs), do: changeset
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -7,14 +7,6 @@ defmodule Memex.Pipelines.Steps.Step do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Pipeline} alias Memex.{Accounts.User, Pipelines.Pipeline}
@derive {Jason.Encoder,
only: [
:title,
:content,
:position,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "steps" do schema "steps" do

View File

@ -46,7 +46,7 @@ defmodule MemexWeb do
def live_view do def live_view do
quote do quote do
use Phoenix.LiveView, use Phoenix.LiveView,
layout: {MemexWeb.LayoutView, "live.html"} layout: {MemexWeb.LayoutView, :live}
on_mount MemexWeb.InitAssigns on_mount MemexWeb.InitAssigns
unquote(view_helpers()) unquote(view_helpers())

View File

@ -12,7 +12,7 @@ defmodule MemexWeb.Components.ContextContent do
~H""" ~H"""
<div <div
id={"show-context-content-#{@context.id}"} id={"show-context-content-#{@context.id}"}
class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto" class="input input-primary h-128 min-h-128 inline-block"
phx-hook="MaintainAttrs" phx-hook="MaintainAttrs"
phx-update="ignore" phx-update="ignore"
readonly readonly

View File

@ -4,7 +4,7 @@ defmodule MemexWeb.Components.ContextsTableComponent do
""" """
use MemexWeb, :live_component use MemexWeb, :live_component
alias Ecto.UUID alias Ecto.UUID
alias Memex.{Accounts.User, Contexts.Context} alias Memex.{Accounts.User, Contexts, Contexts.Context}
alias Phoenix.LiveView.{Rendered, Socket} alias Phoenix.LiveView.{Rendered, Socket}
@impl true @impl true
@ -45,6 +45,7 @@ defmodule MemexWeb.Components.ContextsTableComponent do
columns = [ columns = [
%{label: gettext("slug"), key: :slug}, %{label: gettext("slug"), key: :slug},
%{label: gettext("content"), key: :content},
%{label: gettext("tags"), key: :tags}, %{label: gettext("tags"), key: :tags},
%{label: gettext("visibility"), key: :visibility} %{label: gettext("visibility"), key: :visibility}
| columns | columns
@ -104,18 +105,20 @@ defmodule MemexWeb.Components.ContextsTableComponent do
{slug, slug_block} {slug, slug_block}
end end
defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do defp get_value_for_key(:content, %{content: content}, _additional_data) do
assigns = %{tags: tags} assigns = %{content: content}
~H""" content_block = ~H"""
<div class="flex flex-wrap justify-center space-x-1"> <div class="truncate max-w-sm">
<%= for tag <- @tags do %> <%= @content %>
<.link patch={Routes.context_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div> </div>
""" """
{content, content_block}
end
defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do
tags |> Contexts.get_tags_string()
end end
defp get_value_for_key(:actions, context, %{actions: actions}) do defp get_value_for_key(:actions, context, %{actions: actions}) do

View File

@ -6,8 +6,6 @@ defmodule MemexWeb.Components.InviteCard do
use MemexWeb, :component use MemexWeb, :component
def invite_card(assigns) do def invite_card(assigns) do
assigns = assigns |> assign_new(:code_actions, fn -> [] end)
~H""" ~H"""
<div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4 <div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
border border-gray-400 rounded-lg shadow-lg hover:shadow-md border border-gray-400 rounded-lg shadow-lg hover:shadow-md
@ -18,14 +16,8 @@ defmodule MemexWeb.Components.InviteCard do
<%= if @invite.disabled_at |> is_nil() do %> <%= if @invite.disabled_at |> is_nil() do %>
<h2 class="title text-md"> <h2 class="title text-md">
<%= if @invite.uses_left do %> <%= gettext("Uses Left:") %>
<%= gettext( <%= @invite.uses_left || gettext("unlimited") %>
"uses left: %{uses_left}",
uses_left: @invite.uses_left
) %>
<% else %>
<%= gettext("uses left: unlimited") %>
<% end %>
</h2> </h2>
<% else %> <% else %>
<h2 class="title text-md"> <h2 class="title text-md">
@ -33,18 +25,17 @@ defmodule MemexWeb.Components.InviteCard do
</h2> </h2>
<% end %> <% 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"> <div class="flex flex-row flex-wrap justify-center items-center">
<code <code
id={"code-#{@invite.id}"} id={"code-#{@invite.id}"}
class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800" class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800"
phx-no-format >
><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code> <%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %>
</code>
<%= if @code_actions do %>
<%= render_slot(@code_actions) %> <%= render_slot(@code_actions) %>
<% end %>
</div> </div>
<%= if @inner_block do %> <%= if @inner_block do %>

View File

@ -1,44 +0,0 @@
defmodule MemexWeb.Components.NoteContent do
@moduledoc """
Display the content for a note
"""
use MemexWeb, :component
alias Memex.Notes.Note
alias Phoenix.HTML
attr :note, Note, required: true
def note_content(assigns) do
~H"""
<div
id={"show-note-content-#{@note.id}"}
class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
phx-hook="MaintainAttrs"
phx-update="ignore"
readonly
phx-no-format
><p class="inline"><%= add_links_to_content(@note.content) %></p></div>
"""
end
defp add_links_to_content(content) do
Regex.replace(
~r/\[\[([\p{L}\p{N}\-]+)\]\]/,
content,
fn _whole_match, slug ->
link =
HTML.Link.link(
"[[#{slug}]]",
to: Routes.note_show_path(Endpoint, :show, slug),
class: "link inline",
data: [qa: "note-link-#{slug}"]
)
|> HTML.Safe.to_iodata()
|> IO.iodata_to_binary()
"</p>#{link}<p class=\"inline\">"
end
)
|> HTML.raw()
end
end

View File

@ -4,7 +4,7 @@ defmodule MemexWeb.Components.NotesTableComponent do
""" """
use MemexWeb, :live_component use MemexWeb, :live_component
alias Ecto.UUID alias Ecto.UUID
alias Memex.{Accounts.User, Notes.Note} alias Memex.{Accounts.User, Notes, Notes.Note}
alias Phoenix.LiveView.{Rendered, Socket} alias Phoenix.LiveView.{Rendered, Socket}
@impl true @impl true
@ -45,6 +45,7 @@ defmodule MemexWeb.Components.NotesTableComponent do
columns = [ columns = [
%{label: gettext("slug"), key: :slug}, %{label: gettext("slug"), key: :slug},
%{label: gettext("content"), key: :content},
%{label: gettext("tags"), key: :tags}, %{label: gettext("tags"), key: :tags},
%{label: gettext("visibility"), key: :visibility} %{label: gettext("visibility"), key: :visibility}
| columns | columns
@ -104,18 +105,20 @@ defmodule MemexWeb.Components.NotesTableComponent do
{slug, slug_block} {slug, slug_block}
end end
defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do defp get_value_for_key(:content, %{content: content}, _additional_data) do
assigns = %{tags: tags} assigns = %{content: content}
~H""" content_block = ~H"""
<div class="flex flex-wrap justify-center space-x-1"> <div class="truncate max-w-sm">
<%= for tag <- @tags do %> <%= @content %>
<.link patch={Routes.note_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div> </div>
""" """
{content, content_block}
end
defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do
tags |> Notes.get_tags_string()
end end
defp get_value_for_key(:actions, note, %{actions: actions}) do defp get_value_for_key(:actions, note, %{actions: actions}) do

View File

@ -4,7 +4,7 @@ defmodule MemexWeb.Components.PipelinesTableComponent do
""" """
use MemexWeb, :live_component use MemexWeb, :live_component
alias Ecto.UUID alias Ecto.UUID
alias Memex.{Accounts.User, Pipelines.Pipeline} alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline}
alias Phoenix.LiveView.{Rendered, Socket} alias Phoenix.LiveView.{Rendered, Socket}
@impl true @impl true
@ -118,17 +118,7 @@ defmodule MemexWeb.Components.PipelinesTableComponent do
end end
defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do defp get_value_for_key(:tags, %{tags: tags}, _additional_data) do
assigns = %{tags: tags} tags |> Pipelines.get_tags_string()
~H"""
<div class="flex flex-wrap justify-center space-x-1">
<%= for tag <- @tags do %>
<.link patch={Routes.pipeline_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div>
"""
end end
defp get_value_for_key(:actions, pipeline, %{actions: actions}) do defp get_value_for_key(:actions, pipeline, %{actions: actions}) do

View File

@ -12,7 +12,7 @@ defmodule MemexWeb.Components.StepContent do
~H""" ~H"""
<div <div
id={"show-step-content-#{@step.id}"} id={"show-step-content-#{@step.id}"}
class="input input-primary h-32 min-h-32 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto" class="input input-primary h-32 min-h-32 inline-block"
phx-hook="MaintainAttrs" phx-hook="MaintainAttrs"
phx-update="ignore" phx-update="ignore"
readonly readonly

View File

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

View File

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

View File

@ -1,17 +0,0 @@
defmodule MemexWeb.ExportController do
use MemexWeb, :controller
alias Memex.{Contexts, Notes, Pipelines, Pipelines.Steps}
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
pipelines =
Pipelines.list_pipelines(current_user)
|> Enum.map(fn pipeline -> Steps.preload_steps(pipeline, current_user) end)
json(conn, %{
user: current_user,
notes: Notes.list_notes(current_user),
contexts: Contexts.list_contexts(current_user),
pipelines: pipelines
})
end
end

View File

@ -6,7 +6,7 @@ defmodule MemexWeb.UserResetPasswordController do
plug :get_user_by_reset_password_token when action in [:edit, :update] plug :get_user_by_reset_password_token when action in [:edit, :update]
def new(conn, _params) do def new(conn, _params) do
render(conn, "new.html", page_title: gettext("forgot your password?")) render(conn, "new.html", page_title: gettext("Forgot your password?"))
end end
def create(conn, %{"user" => %{"email" => email}}) do def create(conn, %{"user" => %{"email" => email}}) do

View File

@ -20,7 +20,7 @@ defmodule MemexWeb.UserSessionController do
def delete(conn, _params) do def delete(conn, _params) do
conn conn
|> put_flash(:info, dgettext("prompts", "logged out successfully.")) |> put_flash(:info, dgettext("prompts", "Logged out successfully."))
|> UserAuth.log_out_user() |> UserAuth.log_out_user()
end end
end end

View File

@ -62,7 +62,7 @@ defmodule MemexWeb.UserSettingsController do
case Accounts.update_user_locale(user, locale) do case Accounts.update_user_locale(user, locale) do
{:ok, _user} -> {:ok, _user} ->
conn conn
|> put_flash(:info, dgettext("prompts", "language updated successfully.")) |> put_flash(:info, dgettext("prompts", "Language updated successfully."))
|> redirect(to: Routes.user_settings_path(conn, :edit)) |> redirect(to: Routes.user_settings_path(conn, :edit))
{:error, changeset} -> {:error, changeset} ->

View File

@ -27,7 +27,9 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2") placeholder: gettext("tag1,tag2"),
phx_update: "ignore",
value: Contexts.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -3,13 +3,7 @@
<%= @context.slug %> <%= @context.slug %>
</h1> </h1>
<div class="flex flex-wrap space-x-1"> <p><%= if @context.tags, do: @context.tags |> Enum.join(", ") %></p>
<%= for tag <- @context.tags do %>
<.link navigate={Routes.context_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div>
<.context_content context={@context} /> <.context_content context={@context} />

View File

@ -120,26 +120,5 @@
) %> ) %>
</p> </p>
</li> </li>
<li class="flex flex-col justify-center items-stretch space-y-2">
<b class="whitespace-nowrap">
<%= gettext("how many people should i invite?") %>
</b>
<p>
<%= gettext(
"while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
) %>
</p>
<p>
<%= gettext(
"note, context and pipeline slugs must be unique, and you are free to backlink to notes not written by you."
) %>
</p>
<p>
<%= gettext(
"so, i'd recommend inviting anyone you'd like to work on your collective memEx. however, when in doubt, hopefully setting up a new instance is easy enough. if it isn't, then feel free to let me know :)"
) %>
</p>
</li>
</ul> </ul>
</div> </div>

View File

@ -3,8 +3,6 @@ defmodule MemexWeb.HomeLive do
Liveview for the main home page Liveview for the main home page
""" """
@version Mix.Project.config()[:version]
use MemexWeb, :live_view use MemexWeb, :live_view
alias Memex.Accounts alias Memex.Accounts
alias MemexWeb.{Endpoint, FaqLive} alias MemexWeb.{Endpoint, FaqLive}
@ -12,6 +10,6 @@ defmodule MemexWeb.HomeLive do
@impl true @impl true
def mount(_params, _session, socket) do def mount(_params, _session, socket) do
admins = Accounts.list_users_by_role(:admin) admins = Accounts.list_users_by_role(:admin)
{:ok, socket |> assign(page_title: gettext("home"), admins: admins, version: @version)} {:ok, socket |> assign(page_title: gettext("home"), admins: admins)}
end end
end end

View File

@ -120,12 +120,12 @@
<li class="flex flex-row justify-center items-center space-x-2"> <li class="flex flex-row justify-center items-center space-x-2">
<b><%= gettext("version:") %></b> <b><%= gettext("version:") %></b>
<.link <.link
href="https://gitea.bubbletea.dev/shibao/memEx/src/branch/stable/changelog.md" href="https://gitea.bubbletea.dev/shibao/memex/src/branch/stable/CHANGELOG.md"
class="flex flex-row justify-center items-center space-x-2 link" class="flex flex-row justify-center items-center space-x-2 link"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<p><%= @version %></p> <p>0.1.0</p>
<i class="fas fa-md fa-info-circle"></i> <i class="fas fa-md fa-info-circle"></i>
</.link> </.link>
</li> </li>
@ -140,7 +140,7 @@
<li class="flex flex-col justify-center space-x-2"> <li class="flex flex-col justify-center space-x-2">
<.link <.link
href="https://gitea.bubbletea.dev/shibao/memEx" href="https://gitea.bubbletea.dev/shibao/memex"
class="flex flex-row justify-center items-center space-x-2 link" class="flex flex-row justify-center items-center space-x-2 link"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -151,7 +151,7 @@
</li> </li>
<li class="flex flex-col justify-center space-x-2"> <li class="flex flex-col justify-center space-x-2">
<.link <.link
href="https://weblate.bubbletea.dev/engage/memEx" href="https://weblate.bubbletea.dev/engage/memex"
class="flex flex-row justify-center items-center space-x-2 link" class="flex flex-row justify-center items-center space-x-2 link"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -162,7 +162,7 @@
</li> </li>
<li class="flex flex-col justify-center space-x-2"> <li class="flex flex-col justify-center space-x-2">
<.link <.link
href="https://gitea.bubbletea.dev/shibao/memEx/issues/new" href="https://gitea.bubbletea.dev/shibao/memex/issues/new"
class="flex flex-row justify-center items-center space-x-2 link" class="flex flex-row justify-center items-center space-x-2 link"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"

View File

@ -27,7 +27,9 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2") placeholder: gettext("tag1,tag2"),
phx_update: "ignore",
value: Notes.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -1,6 +1,6 @@
defmodule MemexWeb.NoteLive.Show do defmodule MemexWeb.NoteLive.Show do
use MemexWeb, :live_view use MemexWeb, :live_view
import MemexWeb.Components.NoteContent
alias Memex.{Accounts.User, Notes, Notes.Note} alias Memex.{Accounts.User, Notes, Notes.Note}
@impl true @impl true

View File

@ -3,15 +3,16 @@
<%= @note.slug %> <%= @note.slug %>
</h1> </h1>
<div class="flex flex-wrap space-x-1"> <p><%= if @note.tags, do: @note.tags |> Enum.join(", ") %></p>
<%= for tag <- @note.tags do %>
<.link navigate={Routes.note_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div>
<.note_content note={@note} /> <textarea
id="show-note-content"
class="input input-primary h-128 min-h-128"
phx-hook="MaintainAttrs"
phx-update="ignore"
readonly
phx-no-format
><%= @note.content %></textarea>
<p class="self-end"> <p class="self-end">
<%= gettext("Visibility: %{visibility}", visibility: @note.visibility) %> <%= gettext("Visibility: %{visibility}", visibility: @note.visibility) %>

View File

@ -27,7 +27,9 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2") placeholder: gettext("tag1,tag2"),
phx_update: "ignore",
value: Pipelines.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -3,13 +3,7 @@
<%= @pipeline.slug %> <%= @pipeline.slug %>
</h1> </h1>
<div class="flex flex-wrap space-x-1"> <p><%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %></p>
<%= for tag <- @pipeline.tags do %>
<.link navigate={Routes.pipeline_index_path(Endpoint, :search, tag)} class="link">
<%= tag %>
</.link>
<% end %>
</div>
<%= if @pipeline.description do %> <%= if @pipeline.description do %>
<textarea <textarea

View File

@ -11,17 +11,15 @@ defmodule MemexWeb.Router do
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :fetch_current_user plug :fetch_current_user
plug :put_user_locale plug :put_user_locale, default: Application.compile_env(:gettext, :default_locale, "en_US")
end end
defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, _opts) do defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, default: default) do
default = Application.fetch_env!(:gettext, :default_locale)
Gettext.put_locale(locale || default) Gettext.put_locale(locale || default)
conn |> put_session(:locale, locale || default) conn |> put_session(:locale, locale || default)
end end
defp put_user_locale(conn, _opts) do defp put_user_locale(conn, default: default) do
default = Application.fetch_env!(:gettext, :default_locale)
Gettext.put_locale(default) Gettext.put_locale(default)
conn |> put_session(:locale, default) conn |> put_session(:locale, default)
end end
@ -78,7 +76,6 @@ defmodule MemexWeb.Router do
put "/users/settings", UserSettingsController, :update put "/users/settings", UserSettingsController, :update
delete "/users/settings/:id", UserSettingsController, :delete delete "/users/settings/:id", UserSettingsController, :delete
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
get "/export/:mode", ExportController, :export
end end
scope "/", MemexWeb do scope "/", MemexWeb do

View File

@ -6,7 +6,7 @@
<br /> <br />
<span style="margin-bottom: 1em; font-size: 1.25em;"> <span style="margin-bottom: 1em; font-size: 1.25em;">
<%= dgettext("emails", "Welcome to memEx") %> <%= dgettext("emails", "Welcome to Memex") %>
</span> </span>
<br /> <br />
@ -19,5 +19,5 @@
<br /> <br />
<%= dgettext("emails", "If you didn't create an account at memEx, please ignore this.") %> <%= dgettext("emails", "If you didn't create an account at Memex, please ignore this.") %>
</div> </div>

View File

@ -1,7 +1,7 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %> <%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "Welcome to memEx") %> <%= dgettext("emails", "Welcome to Memex") %>
<%= dgettext("emails", "You can confirm your account by visiting the URL below:") %> <%= dgettext("emails", "You can confirm your account by visiting the URL below:") %>

View File

@ -13,5 +13,5 @@
<br /> <br />
<%= dgettext("emails", "If you didn't request this change from memEx, please ignore this.") %> <%= dgettext("emails", "If you didn't request this change from Memex, please ignore this.") %>
</div> </div>

View File

@ -15,6 +15,6 @@
<%= dgettext( <%= dgettext(
"emails", "emails",
"If you didn't request this change from memEx, please ignore this." "If you didn't request this change from Memex, please ignore this."
) %> ) %>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title> <title>
<%= dgettext("errors", "Error") %> | memEx <%= dgettext("errors", "Error") %>| Memex
</title> </title>
<link rel="stylesheet" href="/css/app.css" /> <link rel="stylesheet" href="/css/app.css" />
<script defer type="text/javascript" src="/js/app.js"> <script defer type="text/javascript" src="/js/app.js">

View File

@ -4,15 +4,15 @@
<%= @email.subject %> <%= @email.subject %>
</title> </title>
</head> </head>
<body style="padding: 2em; color: rgb(161, 161, 170); background-color: rgb(39, 39, 42); font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; text-align: center;"> <body style="padding: 2em; color: rgb(31, 31, 31); background-color: rgb(220, 220, 228); font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; text-align: center;">
<%= @inner_content %> <%= @inner_content %>
<hr style="margin: 2em auto; border-width: 1px; border-color: rgb(161, 161, 170); width: 100%; max-width: 42rem;" /> <hr style="margin: 2em auto; border-width: 1px; border-color: rgb(212, 212, 216); width: 100%; max-width: 42rem;" />
<a style="color: rgb(161, 161, 170);" href={Routes.live_url(Endpoint, HomeLive)}> <a style="color: rgb(31, 31, 31);" href={Routes.live_url(Endpoint, HomeLive)}>
<%= dgettext( <%= dgettext(
"emails", "emails",
"This email was sent from memEx" "This email was sent from Memex, the self-hosted firearm tracker website."
) %> ) %>
</a> </a>
</body> </body>

View File

@ -7,5 +7,5 @@
===================== =====================
<%= dgettext("emails", <%= dgettext("emails",
"This email was sent from memEx at %{url}", "This email was sent from Memex at %{url}, the self-hosted firearm tracker website.",
url: Routes.live_url(Endpoint, HomeLive)) %> url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -1,4 +1,4 @@
<main class="pb-8 min-w-full"> <main class="mb-8 min-w-full">
<header> <header>
<.topbar current_user={assigns[:current_user]}></.topbar> <.topbar current_user={assigns[:current_user]}></.topbar>
@ -28,15 +28,27 @@
</main> </main>
<div <div
id="disconnect" id="loading"
class="z-50 fixed opacity-0 bottom-12 right-12 px-8 py-4 w-max h-max class="fixed opacity-0 top-0 left-0 w-screen h-screen bg-primary-900 z-50
border border-primary-400 shadow-lg rounded-lg bg-primary-800 text-primary-400 flex flex-col justify-center items-center space-y-4
flex justify-center items-center space-x-4 transition-opacity ease-in-out duration-500"
transition-opacity ease-in-out duration-500 delay-[2000ms]"
> >
<i class="fas fa-fade text-md fa-satellite-dish"></i> <h1 class="title text-2xl title-primary-500 text-primary-400">
<%= gettext("Loading...") %>
</h1>
<h1 class="title text-md"> <i class="fas fa-3x fa-spin fa-gear text-primary-400"></i>
</div>
<div
id="disconnect"
class="fixed opacity-0 top-0 left-0 w-screen h-screen bg-primary-900 z-50
flex flex-col justify-center items-center space-y-4
transition-opacity ease-in-out duration-500"
>
<h1 class="title text-2xl title-primary-500 text-primary-400">
<%= gettext("Reconnecting...") %> <%= gettext("Reconnecting...") %>
</h1> </h1>
<i class="fas fa-3x fa-fade fa-satellite-dish text-primary-400"></i>
</div> </div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="m-0 p-0 w-full h-full bg-primary-800"> <html lang="en" class="m-0 p-0 w-full h-full">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
@ -18,7 +18,7 @@
</script> </script>
</head> </head>
<body class="m-0 p-0 w-full h-full text-primary-400 subpixel-antialiased"> <body class="m-0 p-0 w-full h-full bg-primary-800 text-primary-400 subpixel-antialiased">
<%= @inner_content %> <%= @inner_content %>
</body> </body>
</html> </html>

View File

@ -29,11 +29,11 @@
<%= password_input(f, :password, required: true, class: "input input-primary col-span-2") %> <%= password_input(f, :password, required: true, class: "input input-primary col-span-2") %>
<%= error_tag(f, :password, "col-span-3") %> <%= error_tag(f, :password, "col-span-3") %>
<%= label(f, :locale, gettext("language"), class: "title text-lg text-primary-400") %> <%= label(f, :locale, gettext("Language"), class: "title text-lg text-primary-400") %>
<%= select( <%= select(
f, f,
:locale, :locale,
[{gettext("english"), "en_US"}], [{gettext("English"), "en_US"}],
class: "input input-primary col-span-2" class: "input input-primary col-span-2"
) %> ) %>
<%= error_tag(f, :locale) %> <%= error_tag(f, :locale) %>
@ -48,7 +48,7 @@
<%= dgettext("actions", "log in") %> <%= dgettext("actions", "log in") %>
</.link> </.link>
<.link href={Routes.user_reset_password_path(@conn, :new)} class="btn btn-primary"> <.link href={Routes.user_reset_password_path(@conn, :new)} class="btn btn-primary">
<%= dgettext("actions", "forgot your password?") %> <%= dgettext("actions", "Forgot your password?") %>
</.link> </.link>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<div class="mx-auto mb-8 max-w-2xl flex flex-col justify-center items-center space-y-4"> <div class="mx-auto mb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
<h1 class="title text-primary-400 text-xl"> <h1 class="title text-primary-400 text-xl">
<%= dgettext("actions", "forgot your password?") %> <%= dgettext("actions", "Forgot your password?") %>
</h1> </h1>
<.form <.form
@ -12,7 +12,7 @@
<%= label(f, :email, class: "title text-lg text-primary-400") %> <%= label(f, :email, class: "title text-lg text-primary-400") %>
<%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %> <%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %>
<%= submit(dgettext("actions", "send instructions to reset password"), <%= submit(dgettext("actions", "Send instructions to reset password"),
class: "mx-auto btn btn-primary col-span-3" class: "mx-auto btn btn-primary col-span-3"
) %> ) %>
</.form> </.form>

View File

@ -41,7 +41,7 @@
</.link> </.link>
<% end %> <% end %>
<.link href={Routes.user_reset_password_path(@conn, :new)} class="btn btn-primary"> <.link href={Routes.user_reset_password_path(@conn, :new)} class="btn btn-primary">
<%= dgettext("actions", "forgot your password?") %> <%= dgettext("actions", "Forgot your password?") %>
</.link> </.link>
</div> </div>
</div> </div>

View File

@ -136,22 +136,12 @@
<hr class="hr" /> <hr class="hr" />
<div class="flex justify-center items-center">
<.link
href={Routes.export_path(@conn, :export, :json)}
class="mx-4 my-2 btn btn-primary"
target="_blank"
>
<%= dgettext("actions", "export data as json") %>
</.link>
<.link <.link
href={Routes.user_settings_path(@conn, :delete, @current_user)} href={Routes.user_settings_path(@conn, :delete, @current_user)}
method={:delete} method={:delete}
class="mx-4 my-2 btn btn-alert" class="btn btn-alert"
data-confirm={dgettext("prompts", "are you sure you want to delete your account?")} data-confirm={dgettext("prompts", "are you sure you want to delete your account?")}
> >
<%= dgettext("actions", "delete user") %> <%= dgettext("actions", "delete user") %>
</.link> </.link>
</div> </div>
</div>

View File

@ -18,7 +18,7 @@ defmodule MemexWeb.ErrorHelpers do
~H""" ~H"""
<%= for error <- Keyword.get_values(@form.errors, @field) do %> <%= 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) %> <%= translate_error(error) %>
</span> </span>
<% end %> <% end %>

View File

@ -1,12 +1,17 @@
defmodule MemexWeb.LayoutView do defmodule MemexWeb.LayoutView do
use MemexWeb, :view use MemexWeb, :view
import MemexWeb.{Components.Topbar, Gettext} import MemexWeb.Components.Topbar
alias MemexWeb.HomeLive alias MemexWeb.HomeLive
# Phoenix LiveDashboard is available only in development by default, # Phoenix LiveDashboard is available only in development by default,
# so we instruct Elixir to not warn if the dashboard route is missing. # so we instruct Elixir to not warn if the dashboard route is missing.
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}} @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
def get_title(%{assigns: %{title: title}}), do: gettext("memEx | %{title}", title: title) def get_title(conn) do
def get_title(_conn), do: gettext("memEx") if conn.assigns |> Map.has_key?(:title) do
"Memex | #{conn.assigns.title}"
else
"Memex"
end
end
end end

View File

@ -5,94 +5,56 @@ defmodule MemexWeb.ViewHelpers do
:view` :view`
""" """
use Phoenix.Component import Phoenix.Component
@doc """ @doc """
Phoenix.Component for a <time> element that renders the naivedatetime in the Returns a <time> element that renders the naivedatetime in the user's local
user's local timezone with Alpine.js timezone with Alpine.js
""" """
@spec display_datetime(NaiveDateTime.t() | nil) :: Phoenix.LiveView.Rendered.t()
def display_datetime(nil), do: ""
attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil" def display_datetime(datetime) do
assigns = %{
datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
}
def datetime(assigns) do
~H""" ~H"""
<%= if @datetime do %>
<time <time
datetime={cast_datetime(@datetime)} 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 """
Phoenix.Component for a <date> element that renders the Date in the user's
local timezone with Alpine.js
"""
attr :date, :any, required: true, doc: "A `Date` struct or nil"
def date(assigns) do
~H"""
<%= if @date do %>
<time
datetime={@date |> Date.to_iso8601(:extended)}
x-data={"{ x-data={"{
date: date:
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}) Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
.format(new Date(\"#{@date |> Date.to_iso8601(:extended)}\")) .format(new Date(\"#{@datetime}\"))
}"} }"}
x-text="date" x-text="date"
> >
<%= @date |> Date.to_iso8601(:extended) %> <%= @datetime %>
</time> </time>
<% end %>
""" """
end end
@doc """ @doc """
Displays content in a QR code as a base64 encoded PNG Returns a <date> element that renders the Date in the user's local
timezone with Alpine.js
""" """
@spec qr_code_image(String.t()) :: String.t() @spec display_date(Date.t() | nil) :: Phoenix.LiveView.Rendered.t()
@spec qr_code_image(String.t(), width :: non_neg_integer()) :: String.t() def display_date(nil), do: ""
def qr_code_image(content, width \\ 384) do
img_data =
content
|> EQRCode.encode()
|> EQRCode.png(width: width, background_color: <<39, 39, 42>>, color: <<255, 255, 255>>)
|> Base.encode64()
"data:image/png;base64," <> img_data def display_date(date) do
end assigns = %{date: date |> Date.to_iso8601(:extended)}
@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""" ~H"""
<a href={qr_code_image(@content)} download={@filename <> ".png"}> <time
<img class={@image_class} alt={@filename} src={qr_code_image(@content)} /> datetime={@date}
</a> x-data={"{
date:
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\"))
}"}
x-text="date"
>
<%= @date %>
</time>
""" """
end end
end end

18
mix.exs
View File

@ -4,7 +4,7 @@ defmodule Memex.MixProject do
def project do def project do
[ [
app: :memex, app: :memex,
version: "0.1.7", version: "0.1.0",
elixir: "~> 1.14", elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(), compilers: Mix.compilers(),
@ -13,7 +13,7 @@ defmodule Memex.MixProject do
deps: deps(), deps: deps(),
dialyzer: [plt_add_apps: [:ex_unit]], dialyzer: [plt_add_apps: [:ex_unit]],
consolidate_protocols: Mix.env() not in [:dev, :test], consolidate_protocols: Mix.env() not in [:dev, :test],
preferred_cli_env: ["test.all": :test], preferred_cli_env: [test: :test, "test.all": :test],
# ExDoc # ExDoc
name: "memEx", name: "memEx",
source_url: "https://gitea.bubbletea.dev/shibao/memEx", source_url: "https://gitea.bubbletea.dev/shibao/memEx",
@ -48,29 +48,27 @@ defmodule Memex.MixProject do
defp deps do defp deps do
[ [
{:bcrypt_elixir, "~> 2.0"}, {:bcrypt_elixir, "~> 2.0"},
{:phoenix, "~> 1.6.0"}, {:phoenix, "~> 1.6.6"},
{:phoenix_ecto, "~> 4.4"}, {:phoenix_ecto, "~> 4.4"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.18.0"},
{:phoenix_view, "~> 1.1"},
{:phoenix_live_dashboard, "~> 0.6"},
{:ecto_sql, "~> 3.6"}, {:ecto_sql, "~> 3.6"},
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.18.3"},
{:floki, ">= 0.30.0", only: :test}, {:floki, ">= 0.30.0", only: :test},
{:phoenix_live_dashboard, "~> 0.7.0"},
{:oban, "~> 2.10"},
# {:esbuild, "~> 0.3", runtime: Mix.env() == :dev}, # {:esbuild, "~> 0.3", runtime: Mix.env() == :dev},
{:ex_doc, "~> 0.27", only: :dev, runtime: false}, {:ex_doc, "~> 0.27", only: :dev, runtime: false},
{:swoosh, "~> 1.6"}, {:swoosh, "~> 1.6"},
{:gen_smtp, "~> 1.0"}, {:gen_smtp, "~> 1.0"},
{:phoenix_swoosh, "~> 1.0"}, {:phoenix_swoosh, "~> 1.0"},
{:oban, "~> 2.10"},
{:telemetry_metrics, "~> 0.6"}, {:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"}, {:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.18"}, {:gettext, "~> 0.18"},
{:jason, "~> 1.2"}, {:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"}, {:plug_cowboy, "~> 2.5"},
{:ecto_psql_extras, "~> 0.6"}, {:ecto_psql_extras, "~> 0.6"},
{:eqrcode, "~> 0.1.10"},
{:credo, "~> 1.5", only: [:dev, :test], runtime: false}, {:credo, "~> 1.5", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false} {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}
] ]

View File

@ -1,53 +1,56 @@
%{ %{
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "castore": {:hex, :castore, "0.1.19", "a2c3e46d62b7f3aa2e6f88541c21d7400381e53704394462b9fd4f06f6d42bb6", [:mix], [], "hexpm", "e96e0161a5dc82ef441da24d5fa74aefc40d920f3a6645d15e1f9f3e66bb2109"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"}, "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"}, "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"}, "ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"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_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"},
"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"}, "ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [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", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"}, "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "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"}, "esbuild": {:hex, :esbuild, "0.4.0", "9f17db148aead4cf1e6e6a584214357287a93407b5fb51a031f122b61385d4c2", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "b61e4e6b92ffe45e4ee4755a22de6211a67c67987dc02afb35a425a0add1d447"},
"expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"}, "ex_doc": {:hex, :ex_doc, "0.29.0", "4a1cb903ce746aceef9c1f9ae8a6c12b742a5461e6959b9d3b24d813ffbea146", [: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", "f096adb8bbca677d35d278223361c7792d496b3fc0d0224c9d4bc2f651af5db1"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"}, "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
"gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"}, "gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"heex_formatter": {:git, "https://github.com/feliperenan/heex_formatter.git", "efa8f8092afae62d19128bf2bd9f1c0fb86e0b92", []},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"hut": {:hex, :hut, "1.3.0", "71f2f054e657c03f959cf1acc43f436ea87580696528ca2a55c8afb1b06c85e7", [:"erlang.mk", :rebar, :rebar3], [], "hexpm", "7e15d28555d8a1f2b5a3a931ec120af0753e4853a4c66053db354f35bf9ab563"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"oban": {:hex, :oban, "2.13.6", "a0cb1bce3bd393770512231fb5a3695fa19fd3af10d7575bf73f837aee7abf43", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c1c5eb16f377b3cbbf2ea14be24d20e3d91285af9d1ac86260b7c2af5464887"}, "oban": {:hex, :oban, "2.13.5", "6ba77f96bf8d8c57dd95c31292c76dd50104ac110c0bee8345cdf5e42f8afe89", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e5d93843377c7aa6417a6e89dfa63cb3043a4d959b9e946cb1d0018cafc0219b"},
"phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"}, "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.1", "b0bf8f3348dec4910907a2ad1453e642f6fe4d444376c1c9b26222d63c73cf97", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "b6c5d744bf4b40692b1b361d3608bdfd05aeab83e17c7bc217d730f007f31abf"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.0", "4fe222c0be55fdc3f9c711e24955fc42a7cd9b7a2f5f406f2580a567c335a573", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "bebf0fc2d2113b61cb5968f585367234b7b4c21d963d691de7b4b2dc6cdaae6f"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.2", "635cf07de947235deb030cd6b776c71a3b790ab04cebf526aa8c879fe17c7784", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da287a77327e996cc166e4c440c3ad5ab33ccdb151b91c793209b39ebbce5b75"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.3", "2e3d009422addf8b15c3dccc65ce53baccbe26f7cfd21d264680b5867789a9c1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c8845177a866e017dcb7083365393c8f00ab061b8b6b2bda575891079dce81b2"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.1.0", "f8e4780705c9f254cc853f7a40e25f7198ba4d91102bcfad2226669b69766b35", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "aa82f10afd9a4b6080fdf3274dbb9432b25b210d42b4b6b55308f6e59cd87c3d"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.1.0", "f8e4780705c9f254cc853f7a40e25f7198ba4d91102bcfad2226669b69766b35", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "aa82f10afd9a4b6080fdf3274dbb9432b25b210d42b4b6b55308f6e59cd87c3d"},
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"}, "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"}, "swoosh": {:hex, :swoosh, "1.8.2", "af9a22ab2c0d20b266f61acca737fa11a121902de9466a39e91bacdce012101c", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d058ba750eafadb6c09a84a352c14c5d1eeeda6e84945fcc95785b7f3067b7db"},
"table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"}, "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
} }

View File

@ -15,6 +15,13 @@ msgstr ""
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:51
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
#: lib/memex_web/templates/user_session/new.html.heex:44
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_confirmation/new.html.heex:3 #: lib/memex_web/templates/user_confirmation/new.html.heex:3
#: lib/memex_web/templates/user_confirmation/new.html.heex:15 #: lib/memex_web/templates/user_confirmation/new.html.heex:15
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -32,6 +39,11 @@ msgstr ""
msgid "Save" msgid "Save"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_reset_password/new.html.heex:15
#, elixir-autogen, elixir-format
msgid "Send instructions to reset password"
msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:15 #: lib/memex_web/templates/user_settings/edit.html.heex:15
#: lib/memex_web/templates/user_settings/edit.html.heex:44 #: lib/memex_web/templates/user_settings/edit.html.heex:44
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -56,28 +68,28 @@ msgid "create invite"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:49 #: lib/memex_web/live/context_live/index.html.heex:49
#: lib/memex_web/live/context_live/show.html.heex:40 #: lib/memex_web/live/context_live/show.html.heex:34
#: lib/memex_web/live/note_live/index.html.heex:49 #: lib/memex_web/live/note_live/index.html.heex:49
#: lib/memex_web/live/note_live/show.html.heex:37 #: lib/memex_web/live/note_live/show.html.heex:38
#: lib/memex_web/live/pipeline_live/index.html.heex:49 #: lib/memex_web/live/pipeline_live/index.html.heex:49
#: lib/memex_web/live/pipeline_live/show.html.heex:49 #: lib/memex_web/live/pipeline_live/show.html.heex:43
#: lib/memex_web/live/pipeline_live/show.html.heex:119 #: lib/memex_web/live/pipeline_live/show.html.heex:113
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:154 #: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:38 #: lib/memex_web/live/context_live/index.html.heex:38
#: lib/memex_web/live/context_live/show.html.heex:29 #: lib/memex_web/live/context_live/show.html.heex:23
#: lib/memex_web/live/note_live/index.html.heex:38 #: lib/memex_web/live/note_live/index.html.heex:38
#: lib/memex_web/live/note_live/show.html.heex:26 #: lib/memex_web/live/note_live/show.html.heex:27
#: lib/memex_web/live/pipeline_live/index.html.heex:38 #: lib/memex_web/live/pipeline_live/index.html.heex:38
#: lib/memex_web/live/pipeline_live/show.html.heex:38 #: lib/memex_web/live/pipeline_live/show.html.heex:32
#: lib/memex_web/live/pipeline_live/show.html.heex:108 #: lib/memex_web/live/pipeline_live/show.html.heex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "edit" msgid "edit"
msgstr "" msgstr ""
@ -124,39 +136,22 @@ msgstr ""
msgid "register" msgid "register"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:40 #: lib/memex_web/live/context_live/form_component.html.heex:42
#: lib/memex_web/live/note_live/form_component.html.heex:40 #: lib/memex_web/live/note_live/form_component.html.heex:42
#: lib/memex_web/live/pipeline_live/form_component.html.heex:40 #: lib/memex_web/live/pipeline_live/form_component.html.heex:42
#: lib/memex_web/live/step_live/form_component.html.heex:28 #: lib/memex_web/live/step_live/form_component.html.heex:28
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "save" msgid "save"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:22 #: lib/memex_web/live/context_live/show.html.heex:16
#: lib/memex_web/live/note_live/show.html.heex:22 #: lib/memex_web/live/note_live/show.html.heex:23
#: lib/memex_web/live/pipeline_live/show.html.heex:31 #: lib/memex_web/live/pipeline_live/show.html.heex:25
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "back" msgid "back"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:135 #: lib/memex_web/live/pipeline_live/show.html.heex:129
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "add step" msgid "add step"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:51
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
#: lib/memex_web/templates/user_session/new.html.heex:44
#, elixir-autogen, elixir-format
msgid "forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_reset_password/new.html.heex:15
#, elixir-autogen, elixir-format
msgid "send instructions to reset password"
msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -9,12 +9,20 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2\n"
#: lib/memex_web/live/invite_live/index.html.heex:30 #: lib/memex_web/live/invite_live/index.html.heex:30
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:51
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
#: lib/memex_web/templates/user_session/new.html.heex:44
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_confirmation/new.html.heex:3 #: lib/memex_web/templates/user_confirmation/new.html.heex:3
#: lib/memex_web/templates/user_confirmation/new.html.heex:15 #: lib/memex_web/templates/user_confirmation/new.html.heex:15
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -32,6 +40,11 @@ msgstr ""
msgid "Save" msgid "Save"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_reset_password/new.html.heex:15
#, elixir-autogen, elixir-format
msgid "Send instructions to reset password"
msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:15 #: lib/memex_web/templates/user_settings/edit.html.heex:15
#: lib/memex_web/templates/user_settings/edit.html.heex:44 #: lib/memex_web/templates/user_settings/edit.html.heex:44
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -56,28 +69,28 @@ msgid "create invite"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:49 #: lib/memex_web/live/context_live/index.html.heex:49
#: lib/memex_web/live/context_live/show.html.heex:40 #: lib/memex_web/live/context_live/show.html.heex:34
#: lib/memex_web/live/note_live/index.html.heex:49 #: lib/memex_web/live/note_live/index.html.heex:49
#: lib/memex_web/live/note_live/show.html.heex:37 #: lib/memex_web/live/note_live/show.html.heex:38
#: lib/memex_web/live/pipeline_live/index.html.heex:49 #: lib/memex_web/live/pipeline_live/index.html.heex:49
#: lib/memex_web/live/pipeline_live/show.html.heex:49 #: lib/memex_web/live/pipeline_live/show.html.heex:43
#: lib/memex_web/live/pipeline_live/show.html.heex:119 #: lib/memex_web/live/pipeline_live/show.html.heex:113
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:154 #: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:38 #: lib/memex_web/live/context_live/index.html.heex:38
#: lib/memex_web/live/context_live/show.html.heex:29 #: lib/memex_web/live/context_live/show.html.heex:23
#: lib/memex_web/live/note_live/index.html.heex:38 #: lib/memex_web/live/note_live/index.html.heex:38
#: lib/memex_web/live/note_live/show.html.heex:26 #: lib/memex_web/live/note_live/show.html.heex:27
#: lib/memex_web/live/pipeline_live/index.html.heex:38 #: lib/memex_web/live/pipeline_live/index.html.heex:38
#: lib/memex_web/live/pipeline_live/show.html.heex:38 #: lib/memex_web/live/pipeline_live/show.html.heex:32
#: lib/memex_web/live/pipeline_live/show.html.heex:108 #: lib/memex_web/live/pipeline_live/show.html.heex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "edit" msgid "edit"
msgstr "" msgstr ""
@ -124,39 +137,22 @@ msgstr ""
msgid "register" msgid "register"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:40 #: lib/memex_web/live/context_live/form_component.html.heex:42
#: lib/memex_web/live/note_live/form_component.html.heex:40 #: lib/memex_web/live/note_live/form_component.html.heex:42
#: lib/memex_web/live/pipeline_live/form_component.html.heex:40 #: lib/memex_web/live/pipeline_live/form_component.html.heex:42
#: lib/memex_web/live/step_live/form_component.html.heex:28 #: lib/memex_web/live/step_live/form_component.html.heex:28
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "save" msgid "save"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:22 #: lib/memex_web/live/context_live/show.html.heex:16
#: lib/memex_web/live/note_live/show.html.heex:22 #: lib/memex_web/live/note_live/show.html.heex:23
#: lib/memex_web/live/pipeline_live/show.html.heex:31 #: lib/memex_web/live/pipeline_live/show.html.heex:25
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "back" msgid "back"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:135 #: lib/memex_web/live/pipeline_live/show.html.heex:129
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "add step" msgid "add step"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:51
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
#: lib/memex_web/templates/user_session/new.html.heex:44
#, elixir-autogen, elixir-format, fuzzy
msgid "forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_reset_password/new.html.heex:15
#, elixir-autogen, elixir-format, fuzzy
msgid "send instructions to reset password"
msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -31,17 +31,37 @@ msgstr ""
msgid "Confirm your account" msgid "Confirm your account"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:36
#, elixir-autogen, elixir-format
msgid "English"
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:9
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_session/new.html.heex:27 #: lib/memex_web/templates/user_session/new.html.heex:27
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Keep me logged in for 60 days" msgid "Keep me logged in for 60 days"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:32
#, elixir-autogen, elixir-format
msgid "Language"
msgstr ""
#: lib/memex_web/templates/layout/live.html.heex:37
#, elixir-autogen, elixir-format
msgid "Loading..."
msgstr ""
#: lib/memex_web/live/invite_live/form_component.html.heex:20 #: lib/memex_web/live/invite_live/form_component.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/live.html.heex:40 #: lib/memex_web/templates/layout/live.html.heex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Reconnecting..." msgid "Reconnecting..."
msgstr "" msgstr ""
@ -56,14 +76,24 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/memex_web/components/invite_card.ex:19
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr ""
#: lib/memex_web/live/invite_live/form_component.html.heex:24 #: lib/memex_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Uses left" msgid "Uses left"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:17 #: lib/memex_web/live/context_live/show.html.heex:11
#: lib/memex_web/live/note_live/show.html.heex:17 #: lib/memex_web/live/note_live/show.html.heex:18
#: lib/memex_web/live/pipeline_live/show.html.heex:26 #: lib/memex_web/live/pipeline_live/show.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Visibility: %{visibility}" msgid "Visibility: %{visibility}"
msgstr "" msgstr ""
@ -88,6 +118,8 @@ msgstr ""
msgid "confirm new password" msgid "confirm new password"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:48
#: lib/memex_web/components/notes_table_component.ex:48
#: lib/memex_web/live/note_live/form_component.html.heex:23 #: lib/memex_web/live/note_live/form_component.html.heex:23
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "content" msgid "content"
@ -142,7 +174,7 @@ msgstr ""
msgid "email" msgid "email"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:29 #: lib/memex_web/components/user_card.ex:23
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "email unconfirmed" msgid "email unconfirmed"
msgstr "" msgstr ""
@ -152,7 +184,6 @@ msgstr ""
msgid "enable" msgid "enable"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:36
#: lib/memex_web/templates/user_settings/edit.html.heex:126 #: lib/memex_web/templates/user_settings/edit.html.heex:126
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "english" msgid "english"
@ -178,7 +209,7 @@ msgstr ""
msgid "instance information" msgid "instance information"
msgstr "" msgstr ""
#: lib/memex_web/components/invite_card.ex:32 #: lib/memex_web/components/invite_card.ex:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invite disabled" msgid "invite disabled"
msgstr "" msgstr ""
@ -286,17 +317,17 @@ msgstr ""
msgid "report bugs or request features" msgid "report bugs or request features"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:41 #: lib/memex_web/live/context_live/form_component.html.heex:43
#: lib/memex_web/live/note_live/form_component.html.heex:41 #: lib/memex_web/live/note_live/form_component.html.heex:43
#: lib/memex_web/live/pipeline_live/form_component.html.heex:41 #: lib/memex_web/live/pipeline_live/form_component.html.heex:43
#: lib/memex_web/live/step_live/form_component.html.heex:29 #: lib/memex_web/live/step_live/form_component.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "saving..." msgid "saving..."
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:37 #: lib/memex_web/live/context_live/form_component.html.heex:39
#: lib/memex_web/live/note_live/form_component.html.heex:37 #: lib/memex_web/live/note_live/form_component.html.heex:39
#: lib/memex_web/live/pipeline_live/form_component.html.heex:37 #: lib/memex_web/live/pipeline_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "select privacy" msgid "select privacy"
msgstr "" msgstr ""
@ -318,13 +349,23 @@ msgstr ""
msgid "tag1,tag2" msgid "tag1,tag2"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:48 #: lib/memex_web/components/contexts_table_component.ex:49
#: lib/memex_web/components/notes_table_component.ex:48 #: lib/memex_web/components/notes_table_component.ex:49
#: lib/memex_web/components/pipelines_table_component.ex:49 #: lib/memex_web/components/pipelines_table_component.ex:49
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "tags" msgid "tags"
msgstr "" msgstr ""
#: lib/memex_web/components/invite_card.ex:20
#, elixir-autogen, elixir-format
msgid "unlimited"
msgstr ""
#: lib/memex_web/components/user_card.ex:25
#, elixir-autogen, elixir-format
msgid "user was confirmed at %{relative_datetime}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:120 #: lib/memex_web/live/invite_live/index.html.heex:120
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "users" msgid "users"
@ -340,8 +381,8 @@ msgstr ""
msgid "view the source code" msgid "view the source code"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:49 #: lib/memex_web/components/contexts_table_component.ex:50
#: lib/memex_web/components/notes_table_component.ex:49 #: lib/memex_web/components/notes_table_component.ex:50
#: lib/memex_web/components/pipelines_table_component.ex:50 #: lib/memex_web/components/pipelines_table_component.ex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "visibility" msgid "visibility"
@ -436,7 +477,7 @@ msgstr ""
msgid "%{slug} could not be found" msgid "%{slug} could not be found"
msgstr "" msgstr ""
#: lib/memex_web/live/home_live.ex:15 #: lib/memex_web/live/home_live.ex:13
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "home" msgid "home"
msgstr "" msgstr ""
@ -456,7 +497,6 @@ msgstr ""
#: lib/memex_web/live/home_live.html.heex:3 #: lib/memex_web/live/home_live.html.heex:3
#: lib/memex_web/templates/layout/root.html.heex:8 #: lib/memex_web/templates/layout/root.html.heex:8
#: lib/memex_web/templates/layout/root.html.heex:9 #: lib/memex_web/templates/layout/root.html.heex:9
#: lib/memex_web/views/layout_view.ex:11
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "memEx" msgid "memEx"
msgstr "" msgstr ""
@ -471,7 +511,7 @@ msgstr ""
msgid "what is this?" msgid "what is this?"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:68 #: lib/memex_web/live/pipeline_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{position}. %{title}" msgid "%{position}. %{title}"
msgstr "" msgstr ""
@ -496,12 +536,12 @@ msgstr ""
msgid "add step to %{slug}" msgid "add step to %{slug}"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:62 #: lib/memex_web/live/pipeline_live/show.html.heex:56
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "no steps" msgid "no steps"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:57 #: lib/memex_web/live/pipeline_live/show.html.heex:51
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "steps:" msgid "steps:"
msgstr "" msgstr ""
@ -605,58 +645,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "zettelkasten" msgid "zettelkasten"
msgstr "" msgstr ""
#: lib/memex_web/views/layout_view.ex:10
#, elixir-autogen, elixir-format
msgid "memEx | %{title}"
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:9
#, elixir-autogen, elixir-format, fuzzy
msgid "forgot your password?"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:126
#, elixir-autogen, elixir-format
msgid "how many people should i invite?"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:134
#, elixir-autogen, elixir-format
msgid "note, context and pipeline slugs must be unique, and you are free to backlink to notes not written by you."
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:139
#, elixir-autogen, elixir-format
msgid "so, i'd recommend inviting anyone you'd like to work on your collective memEx. however, when in doubt, hopefully setting up a new instance is easy enough. if it isn't, then feel free to let me know :)"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:129
#, elixir-autogen, elixir-format
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:32
#, elixir-autogen, elixir-format, fuzzy
msgid "language"
msgstr ""
#: lib/memex_web/components/user_card.ex:23
#, elixir-autogen, elixir-format, fuzzy
msgid "user confirmed on%{confirmed_datetime}"
msgstr ""
#: lib/memex_web/components/user_card.ex:34
#, elixir-autogen, elixir-format, fuzzy
msgid "user registered on%{registered_datetime}"
msgstr ""
#: lib/memex_web/components/invite_card.ex:22
#, elixir-autogen, elixir-format
msgid "uses left: %{uses_left}"
msgstr ""
#: lib/memex_web/components/invite_card.ex:27
#, elixir-autogen, elixir-format
msgid "uses left: unlimited"
msgstr ""

View File

@ -9,6 +9,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2\n"
#: lib/memex/accounts/email.ex:30 #: lib/memex/accounts/email.ex:30
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -30,22 +31,49 @@ msgstr ""
msgid "If you didn't create an account at %{url}, please ignore this." msgid "If you didn't create an account at %{url}, please ignore this."
msgstr "" msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:22
#, elixir-autogen, elixir-format
msgid "If you didn't create an account at Memex, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/reset_password.txt.eex:8 #: lib/memex_web/templates/email/reset_password.txt.eex:8
#: lib/memex_web/templates/email/update_email.txt.eex:8 #: lib/memex_web/templates/email/update_email.txt.eex:8
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "If you didn't request this change from %{url}, please ignore this." msgid "If you didn't request this change from %{url}, please ignore this."
msgstr "" msgstr ""
#: lib/memex_web/templates/email/reset_password.html.heex:16
#: lib/memex_web/templates/email/update_email.html.heex:16
#, elixir-autogen, elixir-format
msgid "If you didn't request this change from Memex, please ignore this."
msgstr ""
#: lib/memex/accounts/email.ex:37 #: lib/memex/accounts/email.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Reset your Memex password" msgid "Reset your Memex password"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/email.txt.eex:9
#, elixir-autogen, elixir-format
msgid "This email was sent from Memex at %{url}, the self-hosted firearm tracker website."
msgstr ""
#: lib/memex_web/templates/layout/email.html.heex:13
#, elixir-autogen, elixir-format
msgid "This email was sent from Memex, the self-hosted firearm tracker website."
msgstr ""
#: lib/memex/accounts/email.ex:44 #: lib/memex/accounts/email.ex:44
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Update your Memex email" msgid "Update your Memex email"
msgstr "" msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:9
#: lib/memex_web/templates/email/confirm_email.txt.eex:4
#, elixir-autogen, elixir-format
msgid "Welcome to Memex"
msgstr ""
#: lib/memex_web/templates/email/update_email.html.heex:8 #: lib/memex_web/templates/email/update_email.html.heex:8
#: lib/memex_web/templates/email/update_email.txt.eex:4 #: lib/memex_web/templates/email/update_email.txt.eex:4
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -63,30 +91,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You can reset your password by visiting the URL below:" msgid "You can reset your password by visiting the URL below:"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/email.html.heex:13
#, elixir-autogen, elixir-format
msgid "This email was sent from memEx"
msgstr ""
#: lib/memex_web/templates/layout/email.txt.eex:9
#, elixir-autogen, elixir-format
msgid "This email was sent from memEx at %{url}"
msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:22
#, elixir-autogen, elixir-format, fuzzy
msgid "If you didn't create an account at memEx, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/reset_password.html.heex:16
#: lib/memex_web/templates/email/update_email.html.heex:16
#, elixir-autogen, elixir-format, fuzzy
msgid "If you didn't request this change from memEx, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:9
#: lib/memex_web/templates/email/confirm_email.txt.eex:4
#, elixir-autogen, elixir-format, fuzzy
msgid "Welcome to memEx"
msgstr ""

View File

@ -9,6 +9,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2\n"
#: lib/memex_web/controllers/user_settings_controller.ex:84 #: lib/memex_web/controllers/user_settings_controller.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -75,22 +76,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:139 #: lib/memex/accounts/user.ex:129
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:160 #: lib/memex/accounts/user.ex:150
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:197 #: lib/memex/accounts/user.ex:187
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:95 #: lib/memex/accounts/user.ex:85
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -101,12 +102,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:58 #: lib/memex/contexts/context.ex:49
#: lib/memex/contexts/context.ex:71 #: lib/memex/contexts/context.ex:60
#: lib/memex/notes/note.ex:57 #: lib/memex/notes/note.ex:48
#: lib/memex/notes/note.ex:70 #: lib/memex/notes/note.ex:59
#: lib/memex/pipelines/pipeline.ex:60 #: lib/memex/pipelines/pipeline.ex:50
#: lib/memex/pipelines/pipeline.ex:73 #: lib/memex/pipelines/pipeline.ex:61
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -130,10 +131,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format, fuzzy
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr ""

View File

@ -9,6 +9,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2\n"
#: lib/memex_web/controllers/user_confirmation_controller.ex:38 #: lib/memex_web/controllers/user_confirmation_controller.ex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -75,6 +76,16 @@ msgstr ""
msgid "If your email is in our system, you will receive instructions to reset your password shortly." msgid "If your email is in our system, you will receive instructions to reset your password shortly."
msgstr "" msgstr ""
#: lib/memex_web/controllers/user_settings_controller.ex:65
#, elixir-autogen, elixir-format
msgid "Language updated successfully."
msgstr ""
#: lib/memex_web/controllers/user_session_controller.ex:23
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:46 #: lib/memex_web/controllers/user_reset_password_controller.ex:46
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password reset successfully." msgid "Password reset successfully."
@ -116,7 +127,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:152 #: lib/memex_web/templates/user_settings/edit.html.heex:143
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""
@ -132,12 +143,12 @@ msgid "are you sure you want to make %{invite_name} unlimited?"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:46 #: lib/memex_web/live/context_live/index.html.heex:46
#: lib/memex_web/live/context_live/show.html.heex:37 #: lib/memex_web/live/context_live/show.html.heex:31
#: lib/memex_web/live/note_live/index.html.heex:46 #: lib/memex_web/live/note_live/index.html.heex:46
#: lib/memex_web/live/note_live/show.html.heex:34 #: lib/memex_web/live/note_live/show.html.heex:35
#: lib/memex_web/live/pipeline_live/index.html.heex:46 #: lib/memex_web/live/pipeline_live/index.html.heex:46
#: lib/memex_web/live/pipeline_live/show.html.heex:46 #: lib/memex_web/live/pipeline_live/show.html.heex:40
#: lib/memex_web/live/pipeline_live/show.html.heex:116 #: lib/memex_web/live/pipeline_live/show.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure?" msgid "are you sure?"
msgstr "" msgstr ""
@ -146,13 +157,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "register to setup %{name}" msgid "register to setup %{name}"
msgstr "" msgstr ""
#: lib/memex_web/controllers/user_session_controller.ex:23
#, elixir-autogen, elixir-format, fuzzy
msgid "logged out successfully."
msgstr ""
#: lib/memex_web/controllers/user_settings_controller.ex:65
#, elixir-autogen, elixir-format, fuzzy
msgid "language updated successfully."
msgstr ""

View File

@ -20,17 +20,37 @@ msgstr ""
msgid "Confirm your account" msgid "Confirm your account"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:36
#, elixir-autogen, elixir-format
msgid "English"
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:9
#, elixir-autogen, elixir-format
msgid "Forgot your password?"
msgstr ""
#: lib/memex_web/templates/user_session/new.html.heex:27 #: lib/memex_web/templates/user_session/new.html.heex:27
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Keep me logged in for 60 days" msgid "Keep me logged in for 60 days"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:32
#, elixir-autogen, elixir-format
msgid "Language"
msgstr ""
#: lib/memex_web/templates/layout/live.html.heex:37
#, elixir-autogen, elixir-format
msgid "Loading..."
msgstr ""
#: lib/memex_web/live/invite_live/form_component.html.heex:20 #: lib/memex_web/live/invite_live/form_component.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/live.html.heex:40 #: lib/memex_web/templates/layout/live.html.heex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Reconnecting..." msgid "Reconnecting..."
msgstr "" msgstr ""
@ -45,14 +65,24 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/memex_web/components/invite_card.ex:19
#, elixir-autogen, elixir-format
msgid "Uses Left:"
msgstr ""
#: lib/memex_web/live/invite_live/form_component.html.heex:24 #: lib/memex_web/live/invite_live/form_component.html.heex:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Uses left" msgid "Uses left"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:17 #: lib/memex_web/live/context_live/show.html.heex:11
#: lib/memex_web/live/note_live/show.html.heex:17 #: lib/memex_web/live/note_live/show.html.heex:18
#: lib/memex_web/live/pipeline_live/show.html.heex:26 #: lib/memex_web/live/pipeline_live/show.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Visibility: %{visibility}" msgid "Visibility: %{visibility}"
msgstr "" msgstr ""
@ -77,6 +107,8 @@ msgstr ""
msgid "confirm new password" msgid "confirm new password"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:48
#: lib/memex_web/components/notes_table_component.ex:48
#: lib/memex_web/live/note_live/form_component.html.heex:23 #: lib/memex_web/live/note_live/form_component.html.heex:23
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "content" msgid "content"
@ -131,7 +163,7 @@ msgstr ""
msgid "email" msgid "email"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:29 #: lib/memex_web/components/user_card.ex:23
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "email unconfirmed" msgid "email unconfirmed"
msgstr "" msgstr ""
@ -141,7 +173,6 @@ msgstr ""
msgid "enable" msgid "enable"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:36
#: lib/memex_web/templates/user_settings/edit.html.heex:126 #: lib/memex_web/templates/user_settings/edit.html.heex:126
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "english" msgid "english"
@ -167,7 +198,7 @@ msgstr ""
msgid "instance information" msgid "instance information"
msgstr "" msgstr ""
#: lib/memex_web/components/invite_card.ex:32 #: lib/memex_web/components/invite_card.ex:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invite disabled" msgid "invite disabled"
msgstr "" msgstr ""
@ -275,17 +306,17 @@ msgstr ""
msgid "report bugs or request features" msgid "report bugs or request features"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:41 #: lib/memex_web/live/context_live/form_component.html.heex:43
#: lib/memex_web/live/note_live/form_component.html.heex:41 #: lib/memex_web/live/note_live/form_component.html.heex:43
#: lib/memex_web/live/pipeline_live/form_component.html.heex:41 #: lib/memex_web/live/pipeline_live/form_component.html.heex:43
#: lib/memex_web/live/step_live/form_component.html.heex:29 #: lib/memex_web/live/step_live/form_component.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "saving..." msgid "saving..."
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:37 #: lib/memex_web/live/context_live/form_component.html.heex:39
#: lib/memex_web/live/note_live/form_component.html.heex:37 #: lib/memex_web/live/note_live/form_component.html.heex:39
#: lib/memex_web/live/pipeline_live/form_component.html.heex:37 #: lib/memex_web/live/pipeline_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "select privacy" msgid "select privacy"
msgstr "" msgstr ""
@ -307,13 +338,23 @@ msgstr ""
msgid "tag1,tag2" msgid "tag1,tag2"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:48 #: lib/memex_web/components/contexts_table_component.ex:49
#: lib/memex_web/components/notes_table_component.ex:48 #: lib/memex_web/components/notes_table_component.ex:49
#: lib/memex_web/components/pipelines_table_component.ex:49 #: lib/memex_web/components/pipelines_table_component.ex:49
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "tags" msgid "tags"
msgstr "" msgstr ""
#: lib/memex_web/components/invite_card.ex:20
#, elixir-autogen, elixir-format
msgid "unlimited"
msgstr ""
#: lib/memex_web/components/user_card.ex:25
#, elixir-autogen, elixir-format
msgid "user was confirmed at %{relative_datetime}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:120 #: lib/memex_web/live/invite_live/index.html.heex:120
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "users" msgid "users"
@ -329,8 +370,8 @@ msgstr ""
msgid "view the source code" msgid "view the source code"
msgstr "" msgstr ""
#: lib/memex_web/components/contexts_table_component.ex:49 #: lib/memex_web/components/contexts_table_component.ex:50
#: lib/memex_web/components/notes_table_component.ex:49 #: lib/memex_web/components/notes_table_component.ex:50
#: lib/memex_web/components/pipelines_table_component.ex:50 #: lib/memex_web/components/pipelines_table_component.ex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "visibility" msgid "visibility"
@ -425,7 +466,7 @@ msgstr ""
msgid "%{slug} could not be found" msgid "%{slug} could not be found"
msgstr "" msgstr ""
#: lib/memex_web/live/home_live.ex:15 #: lib/memex_web/live/home_live.ex:13
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "home" msgid "home"
msgstr "" msgstr ""
@ -445,7 +486,6 @@ msgstr ""
#: lib/memex_web/live/home_live.html.heex:3 #: lib/memex_web/live/home_live.html.heex:3
#: lib/memex_web/templates/layout/root.html.heex:8 #: lib/memex_web/templates/layout/root.html.heex:8
#: lib/memex_web/templates/layout/root.html.heex:9 #: lib/memex_web/templates/layout/root.html.heex:9
#: lib/memex_web/views/layout_view.ex:11
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "memEx" msgid "memEx"
msgstr "" msgstr ""
@ -460,7 +500,7 @@ msgstr ""
msgid "what is this?" msgid "what is this?"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:68 #: lib/memex_web/live/pipeline_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{position}. %{title}" msgid "%{position}. %{title}"
msgstr "" msgstr ""
@ -485,12 +525,12 @@ msgstr ""
msgid "add step to %{slug}" msgid "add step to %{slug}"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:62 #: lib/memex_web/live/pipeline_live/show.html.heex:56
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "no steps" msgid "no steps"
msgstr "" msgstr ""
#: lib/memex_web/live/pipeline_live/show.html.heex:57 #: lib/memex_web/live/pipeline_live/show.html.heex:51
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "steps:" msgid "steps:"
msgstr "" msgstr ""
@ -594,58 +634,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "zettelkasten" msgid "zettelkasten"
msgstr "" msgstr ""
#: lib/memex_web/views/layout_view.ex:10
#, elixir-autogen, elixir-format
msgid "memEx | %{title}"
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:9
#, elixir-autogen, elixir-format
msgid "forgot your password?"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:126
#, elixir-autogen, elixir-format
msgid "how many people should i invite?"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:134
#, elixir-autogen, elixir-format
msgid "note, context and pipeline slugs must be unique, and you are free to backlink to notes not written by you."
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:139
#, elixir-autogen, elixir-format
msgid "so, i'd recommend inviting anyone you'd like to work on your collective memEx. however, when in doubt, hopefully setting up a new instance is easy enough. if it isn't, then feel free to let me know :)"
msgstr ""
#: lib/memex_web/live/faq_live.html.heex:129
#, elixir-autogen, elixir-format
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
msgstr ""
#: lib/memex_web/templates/user_registration/new.html.heex:32
#, elixir-autogen, elixir-format
msgid "language"
msgstr ""
#: lib/memex_web/components/user_card.ex:23
#, elixir-autogen, elixir-format
msgid "user confirmed on%{confirmed_datetime}"
msgstr ""
#: lib/memex_web/components/user_card.ex:34
#, elixir-autogen, elixir-format
msgid "user registered on%{registered_datetime}"
msgstr ""
#: lib/memex_web/components/invite_card.ex:22
#, elixir-autogen, elixir-format
msgid "uses left: %{uses_left}"
msgstr ""
#: lib/memex_web/components/invite_card.ex:27
#, elixir-autogen, elixir-format
msgid "uses left: unlimited"
msgstr ""

View File

@ -30,22 +30,49 @@ msgstr ""
msgid "If you didn't create an account at %{url}, please ignore this." msgid "If you didn't create an account at %{url}, please ignore this."
msgstr "" msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:22
#, elixir-autogen, elixir-format
msgid "If you didn't create an account at Memex, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/reset_password.txt.eex:8 #: lib/memex_web/templates/email/reset_password.txt.eex:8
#: lib/memex_web/templates/email/update_email.txt.eex:8 #: lib/memex_web/templates/email/update_email.txt.eex:8
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "If you didn't request this change from %{url}, please ignore this." msgid "If you didn't request this change from %{url}, please ignore this."
msgstr "" msgstr ""
#: lib/memex_web/templates/email/reset_password.html.heex:16
#: lib/memex_web/templates/email/update_email.html.heex:16
#, elixir-autogen, elixir-format
msgid "If you didn't request this change from Memex, please ignore this."
msgstr ""
#: lib/memex/accounts/email.ex:37 #: lib/memex/accounts/email.ex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Reset your Memex password" msgid "Reset your Memex password"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/email.txt.eex:9
#, elixir-autogen, elixir-format
msgid "This email was sent from Memex at %{url}, the self-hosted firearm tracker website."
msgstr ""
#: lib/memex_web/templates/layout/email.html.heex:13
#, elixir-autogen, elixir-format
msgid "This email was sent from Memex, the self-hosted firearm tracker website."
msgstr ""
#: lib/memex/accounts/email.ex:44 #: lib/memex/accounts/email.ex:44
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Update your Memex email" msgid "Update your Memex email"
msgstr "" msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:9
#: lib/memex_web/templates/email/confirm_email.txt.eex:4
#, elixir-autogen, elixir-format
msgid "Welcome to Memex"
msgstr ""
#: lib/memex_web/templates/email/update_email.html.heex:8 #: lib/memex_web/templates/email/update_email.html.heex:8
#: lib/memex_web/templates/email/update_email.txt.eex:4 #: lib/memex_web/templates/email/update_email.txt.eex:4
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -63,30 +90,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You can reset your password by visiting the URL below:" msgid "You can reset your password by visiting the URL below:"
msgstr "" msgstr ""
#: lib/memex_web/templates/layout/email.html.heex:13
#, elixir-autogen, elixir-format
msgid "This email was sent from memEx"
msgstr ""
#: lib/memex_web/templates/layout/email.txt.eex:9
#, elixir-autogen, elixir-format
msgid "This email was sent from memEx at %{url}"
msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:22
#, elixir-autogen, elixir-format
msgid "If you didn't create an account at memEx, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/reset_password.html.heex:16
#: lib/memex_web/templates/email/update_email.html.heex:16
#, elixir-autogen, elixir-format
msgid "If you didn't request this change from memEx, please ignore this."
msgstr ""
#: lib/memex_web/templates/email/confirm_email.html.heex:9
#: lib/memex_web/templates/email/confirm_email.txt.eex:4
#, elixir-autogen, elixir-format
msgid "Welcome to memEx"
msgstr ""

View File

@ -75,22 +75,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:139 #: lib/memex/accounts/user.ex:129
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:160 #: lib/memex/accounts/user.ex:150
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:197 #: lib/memex/accounts/user.ex:187
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:95 #: lib/memex/accounts/user.ex:85
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -101,12 +101,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:58 #: lib/memex/contexts/context.ex:49
#: lib/memex/contexts/context.ex:71 #: lib/memex/contexts/context.ex:60
#: lib/memex/notes/note.ex:57 #: lib/memex/notes/note.ex:48
#: lib/memex/notes/note.ex:70 #: lib/memex/notes/note.ex:59
#: lib/memex/pipelines/pipeline.ex:60 #: lib/memex/pipelines/pipeline.ex:50
#: lib/memex/pipelines/pipeline.ex:73 #: lib/memex/pipelines/pipeline.ex:61
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -130,10 +130,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr ""

View File

@ -75,6 +75,16 @@ msgstr ""
msgid "If your email is in our system, you will receive instructions to reset your password shortly." msgid "If your email is in our system, you will receive instructions to reset your password shortly."
msgstr "" msgstr ""
#: lib/memex_web/controllers/user_settings_controller.ex:65
#, elixir-autogen, elixir-format
msgid "Language updated successfully."
msgstr ""
#: lib/memex_web/controllers/user_session_controller.ex:23
#, elixir-autogen, elixir-format
msgid "Logged out successfully."
msgstr ""
#: lib/memex_web/controllers/user_reset_password_controller.ex:46 #: lib/memex_web/controllers/user_reset_password_controller.ex:46
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password reset successfully." msgid "Password reset successfully."
@ -116,7 +126,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:152 #: lib/memex_web/templates/user_settings/edit.html.heex:143
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""
@ -132,12 +142,12 @@ msgid "are you sure you want to make %{invite_name} unlimited?"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/index.html.heex:46 #: lib/memex_web/live/context_live/index.html.heex:46
#: lib/memex_web/live/context_live/show.html.heex:37 #: lib/memex_web/live/context_live/show.html.heex:31
#: lib/memex_web/live/note_live/index.html.heex:46 #: lib/memex_web/live/note_live/index.html.heex:46
#: lib/memex_web/live/note_live/show.html.heex:34 #: lib/memex_web/live/note_live/show.html.heex:35
#: lib/memex_web/live/pipeline_live/index.html.heex:46 #: lib/memex_web/live/pipeline_live/index.html.heex:46
#: lib/memex_web/live/pipeline_live/show.html.heex:46 #: lib/memex_web/live/pipeline_live/show.html.heex:40
#: lib/memex_web/live/pipeline_live/show.html.heex:116 #: lib/memex_web/live/pipeline_live/show.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure?" msgid "are you sure?"
msgstr "" msgstr ""
@ -146,13 +156,3 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "register to setup %{name}" msgid "register to setup %{name}"
msgstr "" msgstr ""
#: lib/memex_web/controllers/user_session_controller.ex:23
#, elixir-autogen, elixir-format
msgid "logged out successfully."
msgstr ""
#: lib/memex_web/controllers/user_settings_controller.ex:65
#, elixir-autogen, elixir-format
msgid "language updated successfully."
msgstr ""

View File

@ -1,56 +0,0 @@
defmodule Memex.Repo.Migrations.FixSearch do
use Ecto.Migration
def up do
reset_search_columns()
end
def down do
# no way to rollback this migration since the previous generated search columns were invalid
reset_search_columns()
end
defp reset_search_columns() do
alter table(:notes), do: remove(:search)
alter table(:contexts), do: remove(:search)
alter table(:pipelines), do: remove(:search)
flush()
execute """
ALTER TABLE notes
ADD COLUMN search tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(slug, '')), 'A') ||
setweight(to_tsvector('english', coalesce(immutable_array_to_string(tags, ' '), '')), 'B') ||
setweight(to_tsvector('english', coalesce(content, '')), 'C')
) STORED
"""
execute("CREATE INDEX notes_trgm_idx ON notes USING GIN (search)")
execute """
ALTER TABLE contexts
ADD COLUMN search tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(slug, '')), 'A') ||
setweight(to_tsvector('english', coalesce(immutable_array_to_string(tags, ' '), '')), 'B') ||
setweight(to_tsvector('english', coalesce(content, '')), 'C')
) STORED
"""
execute("CREATE INDEX contexts_trgm_idx ON contexts USING GIN (search)")
execute """
ALTER TABLE pipelines
ADD COLUMN search tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(slug, '')), 'A') ||
setweight(to_tsvector('english', coalesce(immutable_array_to_string(tags, ' '), '')), 'B') ||
setweight(to_tsvector('english', coalesce(description, '')), 'C')
) STORED
"""
execute("CREATE INDEX pipelines_trgm_idx ON pipelines USING GIN (search)")
end
end

View File

@ -52,7 +52,7 @@ You can use the following environment variables to configure memEx in
- `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`. - `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`.
- `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to - `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to
`no-reply@HOST` where `HOST` was previously defined. `no-reply@HOST` where `HOST` was previously defined.
- `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "memEx". - `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "Memex".
--- ---

View File

@ -17,36 +17,6 @@ defmodule Memex.ContextsTest do
assert Contexts.list_contexts(user) == [context_a, context_b, context_c] assert Contexts.list_contexts(user) == [context_a, context_b, context_c]
end end
test "list_contexts/2 returns relevant contexts for a user", %{user: user} do
context_a = context_fixture(%{slug: "dogs", content: "has some treats in it"}, user)
context_b = context_fixture(%{slug: "cats", tags: ["home"]}, user)
context_c =
%{slug: "chickens", content: "bananas stuff", tags: ["life", "decisions"]}
|> context_fixture(user)
_shouldnt_return =
%{slug: "dog", content: "banana treat stuff", visibility: :private}
|> context_fixture(user_fixture())
# slug
assert Contexts.list_contexts("dog", user) == [context_a]
assert Contexts.list_contexts("dogs", user) == [context_a]
assert Contexts.list_contexts("cat", user) == [context_b]
assert Contexts.list_contexts("chicken", user) == [context_c]
# content
assert Contexts.list_contexts("treat", user) == [context_a]
assert Contexts.list_contexts("banana", user) == [context_c]
assert Contexts.list_contexts("stuff", user) == [context_c]
# tag
assert Contexts.list_contexts("home", user) == [context_b]
assert Contexts.list_contexts("life", user) == [context_c]
assert Contexts.list_contexts("decision", user) == [context_c]
assert Contexts.list_contexts("decisions", user) == [context_c]
end
test "list_public_contexts/0 returns public contexts", %{user: user} do test "list_public_contexts/0 returns public contexts", %{user: user} do
public_context = context_fixture(%{visibility: :public}, user) public_context = context_fixture(%{visibility: :public}, user)
context_fixture(%{visibility: :unlisted}, user) context_fixture(%{visibility: :unlisted}, user)
@ -54,51 +24,6 @@ defmodule Memex.ContextsTest do
assert Contexts.list_public_contexts() == [public_context] assert Contexts.list_public_contexts() == [public_context]
end end
test "list_public_contexts/1 returns relevant contexts for a user", %{user: user} do
context_a =
%{slug: "dogs", content: "has some treats in it", visibility: :public}
|> context_fixture(user)
context_b =
%{slug: "cats", tags: ["home"], visibility: :public}
|> context_fixture(user)
context_c =
%{
slug: "chickens",
content: "bananas stuff",
tags: ["life", "decisions"],
visibility: :public
}
|> context_fixture(user)
_shouldnt_return =
%{
slug: "dog",
content: "treats bananas stuff",
tags: ["home", "life", "decisions"],
visibility: :private
}
|> context_fixture(user)
# slug
assert Contexts.list_public_contexts("dog") == [context_a]
assert Contexts.list_public_contexts("dogs") == [context_a]
assert Contexts.list_public_contexts("cat") == [context_b]
assert Contexts.list_public_contexts("chicken") == [context_c]
# content
assert Contexts.list_public_contexts("treat") == [context_a]
assert Contexts.list_public_contexts("banana") == [context_c]
assert Contexts.list_public_contexts("stuff") == [context_c]
# tag
assert Contexts.list_public_contexts("home") == [context_b]
assert Contexts.list_public_contexts("life") == [context_c]
assert Contexts.list_public_contexts("decision") == [context_c]
assert Contexts.list_public_contexts("decisions") == [context_c]
end
test "get_context!/1 returns the context with given id", %{user: user} do test "get_context!/1 returns the context with given id", %{user: user} do
context = context_fixture(%{visibility: :public}, user) context = context_fixture(%{visibility: :public}, user)
assert Contexts.get_context!(context.id, user) == context assert Contexts.get_context!(context.id, user) == context

View File

@ -14,41 +14,9 @@ defmodule Memex.NotesTest do
note_a = note_fixture(%{slug: "a", visibility: :public}, user) note_a = note_fixture(%{slug: "a", visibility: :public}, user)
note_b = note_fixture(%{slug: "b", visibility: :unlisted}, user) note_b = note_fixture(%{slug: "b", visibility: :unlisted}, user)
note_c = note_fixture(%{slug: "c", visibility: :private}, user) note_c = note_fixture(%{slug: "c", visibility: :private}, user)
_shouldnt_return = note_fixture(%{visibility: :private}, user_fixture())
assert Notes.list_notes(user) == [note_a, note_b, note_c] assert Notes.list_notes(user) == [note_a, note_b, note_c]
end end
test "list_notes/2 returns relevant notes for a user", %{user: user} do
note_a = note_fixture(%{slug: "dogs", content: "has some treats in it"}, user)
note_b = note_fixture(%{slug: "cats", tags: ["home"]}, user)
note_c =
%{slug: "chickens", content: "bananas stuff", tags: ["life", "decisions"]}
|> note_fixture(user)
_shouldnt_return =
%{slug: "dog", content: "banana treat stuff", visibility: :private}
|> note_fixture(user_fixture())
# slug
assert Notes.list_notes("dog", user) == [note_a]
assert Notes.list_notes("dogs", user) == [note_a]
assert Notes.list_notes("cat", user) == [note_b]
assert Notes.list_notes("chicken", user) == [note_c]
# content
assert Notes.list_notes("treat", user) == [note_a]
assert Notes.list_notes("banana", user) == [note_c]
assert Notes.list_notes("stuff", user) == [note_c]
# tag
assert Notes.list_notes("home", user) == [note_b]
assert Notes.list_notes("life", user) == [note_c]
assert Notes.list_notes("decision", user) == [note_c]
assert Notes.list_notes("decisions", user) == [note_c]
end
test "list_public_notes/0 returns public notes", %{user: user} do test "list_public_notes/0 returns public notes", %{user: user} do
public_note = note_fixture(%{visibility: :public}, user) public_note = note_fixture(%{visibility: :public}, user)
note_fixture(%{visibility: :unlisted}, user) note_fixture(%{visibility: :unlisted}, user)
@ -56,51 +24,6 @@ defmodule Memex.NotesTest do
assert Notes.list_public_notes() == [public_note] assert Notes.list_public_notes() == [public_note]
end end
test "list_public_notes/1 returns relevant notes for a user", %{user: user} do
note_a =
%{slug: "dogs", content: "has some treats in it", visibility: :public}
|> note_fixture(user)
note_b =
%{slug: "cats", tags: ["home"], visibility: :public}
|> note_fixture(user)
note_c =
%{
slug: "chickens",
content: "bananas stuff",
tags: ["life", "decisions"],
visibility: :public
}
|> note_fixture(user)
_shouldnt_return =
%{
slug: "dog",
content: "treats bananas stuff",
tags: ["home", "life", "decisions"],
visibility: :private
}
|> note_fixture(user)
# slug
assert Notes.list_public_notes("dog") == [note_a]
assert Notes.list_public_notes("dogs") == [note_a]
assert Notes.list_public_notes("cat") == [note_b]
assert Notes.list_public_notes("chicken") == [note_c]
# content
assert Notes.list_public_notes("treat") == [note_a]
assert Notes.list_public_notes("banana") == [note_c]
assert Notes.list_public_notes("stuff") == [note_c]
# tag
assert Notes.list_public_notes("home") == [note_b]
assert Notes.list_public_notes("life") == [note_c]
assert Notes.list_public_notes("decision") == [note_c]
assert Notes.list_public_notes("decisions") == [note_c]
end
test "get_note!/1 returns the note with given id", %{user: user} do test "get_note!/1 returns the note with given id", %{user: user} do
note = note_fixture(%{visibility: :public}, user) note = note_fixture(%{visibility: :public}, user)
assert Notes.get_note!(note.id, user) == note assert Notes.get_note!(note.id, user) == note

View File

@ -17,36 +17,6 @@ defmodule Memex.PipelinesTest do
assert Pipelines.list_pipelines(user) == [pipeline_a, pipeline_b, pipeline_c] assert Pipelines.list_pipelines(user) == [pipeline_a, pipeline_b, pipeline_c]
end end
test "list_pipelines/2 returns relevant pipelines for a user", %{user: user} do
pipeline_a = pipeline_fixture(%{slug: "dogs", description: "has some treats in it"}, user)
pipeline_b = pipeline_fixture(%{slug: "cats", tags: ["home"]}, user)
pipeline_c =
%{slug: "chickens", description: "bananas stuff", tags: ["life", "decisions"]}
|> pipeline_fixture(user)
_shouldnt_return =
%{slug: "dog", description: "banana treat stuff", visibility: :private}
|> pipeline_fixture(user_fixture())
# slug
assert Pipelines.list_pipelines("dog", user) == [pipeline_a]
assert Pipelines.list_pipelines("dogs", user) == [pipeline_a]
assert Pipelines.list_pipelines("cat", user) == [pipeline_b]
assert Pipelines.list_pipelines("chicken", user) == [pipeline_c]
# description
assert Pipelines.list_pipelines("treat", user) == [pipeline_a]
assert Pipelines.list_pipelines("banana", user) == [pipeline_c]
assert Pipelines.list_pipelines("stuff", user) == [pipeline_c]
# tag
assert Pipelines.list_pipelines("home", user) == [pipeline_b]
assert Pipelines.list_pipelines("life", user) == [pipeline_c]
assert Pipelines.list_pipelines("decision", user) == [pipeline_c]
assert Pipelines.list_pipelines("decisions", user) == [pipeline_c]
end
test "list_public_pipelines/0 returns public pipelines", %{user: user} do test "list_public_pipelines/0 returns public pipelines", %{user: user} do
public_pipeline = pipeline_fixture(%{visibility: :public}, user) public_pipeline = pipeline_fixture(%{visibility: :public}, user)
pipeline_fixture(%{visibility: :unlisted}, user) pipeline_fixture(%{visibility: :unlisted}, user)
@ -54,51 +24,6 @@ defmodule Memex.PipelinesTest do
assert Pipelines.list_public_pipelines() == [public_pipeline] assert Pipelines.list_public_pipelines() == [public_pipeline]
end end
test "list_public_pipelines/1 returns relevant pipelines for a user", %{user: user} do
pipeline_a =
%{slug: "dogs", description: "has some treats in it", visibility: :public}
|> pipeline_fixture(user)
pipeline_b =
%{slug: "cats", tags: ["home"], visibility: :public}
|> pipeline_fixture(user)
pipeline_c =
%{
slug: "chickens",
description: "bananas stuff",
tags: ["life", "decisions"],
visibility: :public
}
|> pipeline_fixture(user)
_shouldnt_return =
%{
slug: "dog",
description: "treats bananas stuff",
tags: ["home", "life", "decisions"],
visibility: :private
}
|> pipeline_fixture(user)
# slug
assert Pipelines.list_public_pipelines("dog") == [pipeline_a]
assert Pipelines.list_public_pipelines("dogs") == [pipeline_a]
assert Pipelines.list_public_pipelines("cat") == [pipeline_b]
assert Pipelines.list_public_pipelines("chicken") == [pipeline_c]
# description
assert Pipelines.list_public_pipelines("treat") == [pipeline_a]
assert Pipelines.list_public_pipelines("banana") == [pipeline_c]
assert Pipelines.list_public_pipelines("stuff") == [pipeline_c]
# tag
assert Pipelines.list_public_pipelines("home") == [pipeline_b]
assert Pipelines.list_public_pipelines("life") == [pipeline_c]
assert Pipelines.list_public_pipelines("decision") == [pipeline_c]
assert Pipelines.list_public_pipelines("decisions") == [pipeline_c]
end
test "get_pipeline!/1 returns the pipeline with given id", %{user: user} do test "get_pipeline!/1 returns the pipeline with given id", %{user: user} do
pipeline = pipeline_fixture(%{visibility: :public}, user) pipeline = pipeline_fixture(%{visibility: :public}, user)
assert Pipelines.get_pipeline!(pipeline.id, user) == pipeline assert Pipelines.get_pipeline!(pipeline.id, user) == pipeline

View File

@ -1,101 +0,0 @@
defmodule MemexWeb.ExportControllerTest do
@moduledoc """
Tests the export function
"""
use MemexWeb.ConnCase
import Memex.{ContextsFixtures, NotesFixtures, PipelinesFixtures, StepsFixtures}
@moduletag :export_controller_test
setup %{conn: conn} do
current_user = user_fixture() |> confirm_user()
[
current_user: current_user,
conn: conn |> log_in_user(current_user)
]
end
defp add_data(%{current_user: current_user}) do
note = note_fixture(current_user)
context = context_fixture(current_user)
pipeline = pipeline_fixture(current_user)
step = step_fixture(0, pipeline, current_user)
%{
note: note,
context: context,
pipeline: pipeline,
step: step
}
end
describe "Exports data" do
setup [:add_data]
test "in JSON", %{
conn: conn,
current_user: current_user,
note: note,
context: context,
pipeline: pipeline,
step: step
} do
conn = get(conn, Routes.export_path(conn, :export, :json))
ideal_note = %{
"slug" => note.slug,
"content" => note.content,
"tags" => note.tags,
"visibility" => note.visibility |> to_string(),
"inserted_at" => note.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => note.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_context = %{
"slug" => context.slug,
"content" => context.content,
"tags" => context.tags,
"visibility" => context.visibility |> to_string(),
"inserted_at" => context.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => context.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_pipeline = %{
"slug" => pipeline.slug,
"description" => pipeline.description,
"tags" => pipeline.tags,
"visibility" => pipeline.visibility |> to_string(),
"inserted_at" => pipeline.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => pipeline.updated_at |> NaiveDateTime.to_iso8601(),
"steps" => [
%{
"title" => step.title,
"content" => step.content,
"position" => step.position,
"inserted_at" => step.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => step.updated_at |> NaiveDateTime.to_iso8601()
}
]
}
ideal_user = %{
"confirmed_at" =>
current_user.confirmed_at |> Jason.encode!() |> String.replace(~r/\"/, ""),
"email" => current_user.email,
"id" => current_user.id,
"locale" => current_user.locale,
"role" => to_string(current_user.role),
"inserted_at" => current_user.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => current_user.updated_at |> NaiveDateTime.to_iso8601()
}
json_resp = conn |> json_response(200)
assert %{"notes" => [^ideal_note]} = json_resp
assert %{"contexts" => [^ideal_context]} = json_resp
assert %{"pipelines" => [^ideal_pipeline]} = json_resp
assert %{"user" => ^ideal_user} = json_resp
end
end
end

View File

@ -17,7 +17,7 @@ defmodule MemexWeb.UserResetPasswordControllerTest do
test "renders the reset password page", %{conn: conn} do test "renders the reset password page", %{conn: conn} do
conn = get(conn, Routes.user_reset_password_path(conn, :new)) conn = get(conn, Routes.user_reset_password_path(conn, :new))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ dgettext("actions", "forgot your password?") assert response =~ dgettext("actions", "Forgot your password?")
end end
end end

View File

@ -88,14 +88,14 @@ defmodule MemexWeb.UserSessionControllerTest do
conn = conn |> log_in_user(current_user) |> delete(Routes.user_session_path(conn, :delete)) conn = conn |> log_in_user(current_user) |> delete(Routes.user_session_path(conn, :delete))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
refute get_session(conn, :user_token) refute get_session(conn, :user_token)
assert get_flash(conn, :info) =~ gettext("logged out successfully") assert get_flash(conn, :info) =~ gettext("Logged out successfully")
end end
test "succeeds even if the user is not logged in", %{conn: conn} do test "succeeds even if the user is not logged in", %{conn: conn} do
conn = delete(conn, Routes.user_session_path(conn, :delete)) conn = delete(conn, Routes.user_session_path(conn, :delete))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
refute get_session(conn, :user_token) refute get_session(conn, :user_token)
assert get_flash(conn, :info) =~ gettext("logged out successfully") assert get_flash(conn, :info) =~ gettext("Logged out successfully")
end end
end end
end end

View File

@ -34,15 +34,7 @@ defmodule MemexWeb.ContextLiveTest do
{:ok, _index_live, html} = live(conn, Routes.context_index_path(conn, :index)) {:ok, _index_live, html} = live(conn, Routes.context_index_path(conn, :index))
assert html =~ "contexts" assert html =~ "contexts"
assert html =~ context.slug assert html =~ context.content
end
test "searches by tag", %{conn: conn} do
{:ok, index_live, html} = live(conn, Routes.context_index_path(conn, :index))
assert html =~ "example-tag"
assert index_live |> element("a", "example-tag") |> render_click()
assert_patch(index_live, Routes.context_index_path(conn, :search, "example-tag"))
end end
test "saves new context", %{conn: conn} do test "saves new context", %{conn: conn} do
@ -64,7 +56,7 @@ defmodule MemexWeb.ContextLiveTest do
|> follow_redirect(conn, Routes.context_index_path(conn, :index)) |> follow_redirect(conn, Routes.context_index_path(conn, :index))
assert html =~ "#{@create_attrs |> Map.get("slug")} created" assert html =~ "#{@create_attrs |> Map.get("slug")} created"
assert html =~ "some-slug" assert html =~ "some content"
end end
test "updates context in listing", %{conn: conn, context: context} do test "updates context in listing", %{conn: conn, context: context} do
@ -86,7 +78,7 @@ defmodule MemexWeb.ContextLiveTest do
|> follow_redirect(conn, Routes.context_index_path(conn, :index)) |> follow_redirect(conn, Routes.context_index_path(conn, :index))
assert html =~ "#{@update_attrs |> Map.get("slug")} saved" assert html =~ "#{@update_attrs |> Map.get("slug")} saved"
assert html =~ "some-updated-slug" assert html =~ "some updated content"
end end
test "deletes context in listing", %{conn: conn, context: context} do test "deletes context in listing", %{conn: conn, context: context} do
@ -104,7 +96,7 @@ defmodule MemexWeb.ContextLiveTest do
{:ok, _show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug)) {:ok, _show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug))
assert html =~ "context" assert html =~ "context"
assert html =~ context.slug assert html =~ context.content
end end
test "updates context within modal", %{conn: conn, context: context} do test "updates context within modal", %{conn: conn, context: context} do
@ -114,13 +106,9 @@ defmodule MemexWeb.ContextLiveTest do
assert_patch(show_live, Routes.context_show_path(conn, :edit, context.slug)) assert_patch(show_live, Routes.context_show_path(conn, :edit, context.slug))
html = assert show_live
show_live
|> form("#context-form", context: @invalid_attrs) |> form("#context-form", context: @invalid_attrs)
|> render_change() |> render_change() =~ "can&#39;t be blank"
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
show_live show_live
@ -129,7 +117,7 @@ defmodule MemexWeb.ContextLiveTest do
|> follow_redirect(conn, Routes.context_show_path(conn, :show, context.slug)) |> follow_redirect(conn, Routes.context_show_path(conn, :show, context.slug))
assert html =~ "#{context.slug} saved" assert html =~ "#{context.slug} saved"
assert html =~ "tag2" assert html =~ "some updated content"
end end
test "deletes context", %{conn: conn, context: context} do test "deletes context", %{conn: conn, context: context} do
@ -158,14 +146,6 @@ defmodule MemexWeb.ContextLiveTest do
] ]
end end
test "searches by tag", %{conn: conn, context: context} do
{:ok, show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug))
assert html =~ "example-tag"
assert show_live |> element("a", "example-tag") |> render_click()
assert_redirect(show_live, Routes.context_index_path(conn, :search, "example-tag"))
end
test "displays context", %{conn: conn, context: context, note: %{slug: note_slug}} do test "displays context", %{conn: conn, context: context, note: %{slug: note_slug}} do
{:ok, show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug)) {:ok, show_live, html} = live(conn, Routes.context_show_path(conn, :show, context.slug))

View File

@ -3,7 +3,6 @@ defmodule MemexWeb.NoteLiveTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import Memex.NotesFixtures import Memex.NotesFixtures
alias MemexWeb.Endpoint
@create_attrs %{ @create_attrs %{
"content" => "some content", "content" => "some content",
@ -35,15 +34,7 @@ defmodule MemexWeb.NoteLiveTest do
{:ok, _index_live, html} = live(conn, Routes.note_index_path(conn, :index)) {:ok, _index_live, html} = live(conn, Routes.note_index_path(conn, :index))
assert html =~ "notes" assert html =~ "notes"
assert html =~ note.slug assert html =~ note.content
end
test "searches by tag", %{conn: conn} do
{:ok, index_live, html} = live(conn, Routes.note_index_path(conn, :index))
assert html =~ "example-tag"
assert index_live |> element("a", "example-tag") |> render_click()
assert_patch(index_live, Routes.note_index_path(conn, :search, "example-tag"))
end end
test "saves new note", %{conn: conn} do test "saves new note", %{conn: conn} do
@ -54,13 +45,9 @@ defmodule MemexWeb.NoteLiveTest do
assert_patch(index_live, Routes.note_index_path(conn, :new)) assert_patch(index_live, Routes.note_index_path(conn, :new))
html = assert index_live
index_live
|> form("#note-form", note: @invalid_attrs) |> form("#note-form", note: @invalid_attrs)
|> render_change() |> render_change() =~ "can&#39;t be blank"
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
index_live index_live
@ -69,7 +56,7 @@ defmodule MemexWeb.NoteLiveTest do
|> follow_redirect(conn, Routes.note_index_path(conn, :index)) |> follow_redirect(conn, Routes.note_index_path(conn, :index))
assert html =~ "#{@create_attrs |> Map.get("slug")} created" assert html =~ "#{@create_attrs |> Map.get("slug")} created"
assert html =~ "some-slug" assert html =~ "some content"
end end
test "updates note in listing", %{conn: conn, note: note} do test "updates note in listing", %{conn: conn, note: note} do
@ -91,7 +78,7 @@ defmodule MemexWeb.NoteLiveTest do
|> follow_redirect(conn, Routes.note_index_path(conn, :index)) |> follow_redirect(conn, Routes.note_index_path(conn, :index))
assert html =~ "#{@update_attrs |> Map.get("slug")} saved" assert html =~ "#{@update_attrs |> Map.get("slug")} saved"
assert html =~ "some-updated-slug" assert html =~ "some updated content"
end end
test "deletes note in listing", %{conn: conn, note: note} do test "deletes note in listing", %{conn: conn, note: note} do
@ -109,7 +96,7 @@ defmodule MemexWeb.NoteLiveTest do
{:ok, _show_live, html} = live(conn, Routes.note_show_path(conn, :show, note.slug)) {:ok, _show_live, html} = live(conn, Routes.note_show_path(conn, :show, note.slug))
assert html =~ "note" assert html =~ "note"
assert html =~ note.slug assert html =~ note.content
end end
test "updates note within modal", %{conn: conn, note: note} do test "updates note within modal", %{conn: conn, note: note} do
@ -130,7 +117,7 @@ defmodule MemexWeb.NoteLiveTest do
|> follow_redirect(conn, Routes.note_show_path(conn, :show, note.slug)) |> follow_redirect(conn, Routes.note_show_path(conn, :show, note.slug))
assert html =~ "#{note.slug} saved" assert html =~ "#{note.slug} saved"
assert html =~ "tag2" assert html =~ "some updated content"
end end
test "deletes note", %{conn: conn, note: note} do test "deletes note", %{conn: conn, note: note} do
@ -145,39 +132,4 @@ defmodule MemexWeb.NoteLiveTest do
refute has_element?(index_live, "#note-#{note.id}") refute has_element?(index_live, "#note-#{note.id}")
end end
end end
describe "show with note" do
setup [:register_and_log_in_user]
setup %{user: user} do
%{slug: note_slug} = note = note_fixture(user)
[
note: note,
backlinked_note:
note_fixture(%{content: "example with backlink to [[#{note_slug}]] note"}, user)
]
end
test "searches by tag", %{conn: conn, note: note} do
{:ok, show_live, html} = live(conn, Routes.note_show_path(conn, :show, note.slug))
assert html =~ "example-tag"
assert show_live |> element("a", "example-tag") |> render_click()
assert_redirect(show_live, Routes.note_index_path(conn, :search, "example-tag"))
end
test "displays context", %{
conn: conn,
backlinked_note: %{slug: backlinked_note_slug},
note: %{slug: note_slug}
} do
{:ok, show_live, html} =
live(conn, Routes.note_show_path(conn, :show, backlinked_note_slug))
assert html =~ "context"
assert html =~ Routes.note_show_path(Endpoint, :show, note_slug)
assert has_element?(show_live, "[data-qa=\"note-link-#{note_slug}\"]")
end
end
end end

View File

@ -48,14 +48,6 @@ defmodule MemexWeb.PipelineLiveTest do
assert html =~ pipeline.description assert html =~ pipeline.description
end end
test "searches by tag", %{conn: conn} do
{:ok, index_live, html} = live(conn, Routes.pipeline_index_path(conn, :index))
assert html =~ "example-tag"
assert index_live |> element("a", "example-tag") |> render_click()
assert_patch(index_live, Routes.pipeline_index_path(conn, :search, "example-tag"))
end
test "saves new pipeline", %{conn: conn} do test "saves new pipeline", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.pipeline_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.pipeline_index_path(conn, :index))
@ -128,13 +120,9 @@ defmodule MemexWeb.PipelineLiveTest do
assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline.slug)) assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline.slug))
html = assert show_live
show_live
|> form("#pipeline-form", pipeline: @invalid_attrs) |> form("#pipeline-form", pipeline: @invalid_attrs)
|> render_change() |> render_change() =~ "can&#39;t be blank"
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
show_live show_live
@ -187,14 +175,6 @@ defmodule MemexWeb.PipelineLiveTest do
] ]
end end
test "searches by tag", %{conn: conn, pipeline: pipeline} do
{:ok, show_live, html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug))
assert html =~ "example-tag"
assert show_live |> element("a", "example-tag") |> render_click()
assert_redirect(show_live, Routes.pipeline_index_path(conn, :search, "example-tag"))
end
test "updates a step", %{conn: conn, pipeline: pipeline, step: step} do test "updates a step", %{conn: conn, pipeline: pipeline, step: step} do
{:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug))

View File

@ -16,12 +16,12 @@ defmodule Memex.ContextsFixtures do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
content: "some content", content: "some content",
tags: ["example-tag"], tag: [],
slug: random_slug(), slug: random_slug(),
visibility: :private visibility: :private
}) })
|> Contexts.create_context(user) |> Contexts.create_context(user)
%{context | tags_string: nil} context
end end
end end

View File

@ -16,12 +16,12 @@ defmodule Memex.NotesFixtures do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
content: "some content", content: "some content",
tags: ["example-tag"], tag: [],
slug: random_slug(), slug: random_slug(),
visibility: :private visibility: :private
}) })
|> Notes.create_note(user) |> Notes.create_note(user)
%{note | tags_string: nil} note
end end
end end

View File

@ -16,12 +16,12 @@ defmodule Memex.PipelinesFixtures do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
description: "some description", description: "some description",
tags: ["example-tag"], tag: [],
slug: random_slug(), slug: random_slug(),
visibility: :private visibility: :private
}) })
|> Pipelines.create_pipeline(user) |> Pipelines.create_pipeline(user)
%{pipeline | tags_string: nil} pipeline
end end
end end