Compare commits
	
		
			8 Commits
		
	
	
		
			0d61e5ee96
			...
			178111ce80
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 178111ce80 | |||
| 50c4878453 | |||
| 51e6f49d17 | |||
| 9289b79afa | |||
| 87f4d18a6d | |||
| 30bee92e37 | |||
| f32d52db26 | |||
| d77421613c | 
@@ -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 '../vendor/topbar'
 | 
					import topbar from 'topbar'
 | 
				
			||||||
import MaintainAttrs from './maintain_attrs'
 | 
					import MaintainAttrs from './maintain_attrs'
 | 
				
			||||||
import Alpine from 'alpinejs'
 | 
					import Alpine from 'alpinejs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1455
									
								
								assets/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1455
									
								
								assets/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -2,6 +2,10 @@
 | 
				
			|||||||
  "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",
 | 
				
			||||||
@@ -26,16 +30,14 @@
 | 
				
			|||||||
    "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
									
									
								
							
							
						
						
									
										157
									
								
								assets/vendor/topbar.js
									
									
									
									
										vendored
									
									
								
							@@ -1,157 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * @license MIT
 | 
					 | 
				
			||||||
 * topbar 1.0.0, 2021-01-06
 | 
					 | 
				
			||||||
 * https://buunguyen.github.io/topbar
 | 
					 | 
				
			||||||
 * Copyright (c) 2021 Buu Nguyen
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
(function (window, document) {
 | 
					 | 
				
			||||||
  "use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // https://gist.github.com/paulirish/1579671
 | 
					 | 
				
			||||||
  (function () {
 | 
					 | 
				
			||||||
    var lastTime = 0;
 | 
					 | 
				
			||||||
    var vendors = ["ms", "moz", "webkit", "o"];
 | 
					 | 
				
			||||||
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
 | 
					 | 
				
			||||||
      window.requestAnimationFrame =
 | 
					 | 
				
			||||||
        window[vendors[x] + "RequestAnimationFrame"];
 | 
					 | 
				
			||||||
      window.cancelAnimationFrame =
 | 
					 | 
				
			||||||
        window[vendors[x] + "CancelAnimationFrame"] ||
 | 
					 | 
				
			||||||
        window[vendors[x] + "CancelRequestAnimationFrame"];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!window.requestAnimationFrame)
 | 
					 | 
				
			||||||
      window.requestAnimationFrame = function (callback, element) {
 | 
					 | 
				
			||||||
        var currTime = new Date().getTime();
 | 
					 | 
				
			||||||
        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
 | 
					 | 
				
			||||||
        var id = window.setTimeout(function () {
 | 
					 | 
				
			||||||
          callback(currTime + timeToCall);
 | 
					 | 
				
			||||||
        }, timeToCall);
 | 
					 | 
				
			||||||
        lastTime = currTime + timeToCall;
 | 
					 | 
				
			||||||
        return id;
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    if (!window.cancelAnimationFrame)
 | 
					 | 
				
			||||||
      window.cancelAnimationFrame = function (id) {
 | 
					 | 
				
			||||||
        clearTimeout(id);
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
  })();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var canvas,
 | 
					 | 
				
			||||||
    progressTimerId,
 | 
					 | 
				
			||||||
    fadeTimerId,
 | 
					 | 
				
			||||||
    currentProgress,
 | 
					 | 
				
			||||||
    showing,
 | 
					 | 
				
			||||||
    addEvent = function (elem, type, handler) {
 | 
					 | 
				
			||||||
      if (elem.addEventListener) elem.addEventListener(type, handler, false);
 | 
					 | 
				
			||||||
      else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
 | 
					 | 
				
			||||||
      else elem["on" + type] = handler;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    options = {
 | 
					 | 
				
			||||||
      autoRun: true,
 | 
					 | 
				
			||||||
      barThickness: 3,
 | 
					 | 
				
			||||||
      barColors: {
 | 
					 | 
				
			||||||
        0: "rgba(26,  188, 156, .9)",
 | 
					 | 
				
			||||||
        ".25": "rgba(52,  152, 219, .9)",
 | 
					 | 
				
			||||||
        ".50": "rgba(241, 196, 15,  .9)",
 | 
					 | 
				
			||||||
        ".75": "rgba(230, 126, 34,  .9)",
 | 
					 | 
				
			||||||
        "1.0": "rgba(211, 84,  0,   .9)",
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      shadowBlur: 10,
 | 
					 | 
				
			||||||
      shadowColor: "rgba(0,   0,   0,   .6)",
 | 
					 | 
				
			||||||
      className: null,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    repaint = function () {
 | 
					 | 
				
			||||||
      canvas.width = window.innerWidth;
 | 
					 | 
				
			||||||
      canvas.height = options.barThickness * 5; // need space for shadow
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      var ctx = canvas.getContext("2d");
 | 
					 | 
				
			||||||
      ctx.shadowBlur = options.shadowBlur;
 | 
					 | 
				
			||||||
      ctx.shadowColor = options.shadowColor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
 | 
					 | 
				
			||||||
      for (var stop in options.barColors)
 | 
					 | 
				
			||||||
        lineGradient.addColorStop(stop, options.barColors[stop]);
 | 
					 | 
				
			||||||
      ctx.lineWidth = options.barThickness;
 | 
					 | 
				
			||||||
      ctx.beginPath();
 | 
					 | 
				
			||||||
      ctx.moveTo(0, options.barThickness / 2);
 | 
					 | 
				
			||||||
      ctx.lineTo(
 | 
					 | 
				
			||||||
        Math.ceil(currentProgress * canvas.width),
 | 
					 | 
				
			||||||
        options.barThickness / 2
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      ctx.strokeStyle = lineGradient;
 | 
					 | 
				
			||||||
      ctx.stroke();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    createCanvas = function () {
 | 
					 | 
				
			||||||
      canvas = document.createElement("canvas");
 | 
					 | 
				
			||||||
      var style = canvas.style;
 | 
					 | 
				
			||||||
      style.position = "fixed";
 | 
					 | 
				
			||||||
      style.top = style.left = style.right = style.margin = style.padding = 0;
 | 
					 | 
				
			||||||
      style.zIndex = 100001;
 | 
					 | 
				
			||||||
      style.display = "none";
 | 
					 | 
				
			||||||
      if (options.className) canvas.classList.add(options.className);
 | 
					 | 
				
			||||||
      document.body.appendChild(canvas);
 | 
					 | 
				
			||||||
      addEvent(window, "resize", repaint);
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    topbar = {
 | 
					 | 
				
			||||||
      config: function (opts) {
 | 
					 | 
				
			||||||
        for (var key in opts)
 | 
					 | 
				
			||||||
          if (options.hasOwnProperty(key)) options[key] = opts[key];
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      show: function () {
 | 
					 | 
				
			||||||
        if (showing) return;
 | 
					 | 
				
			||||||
        showing = true;
 | 
					 | 
				
			||||||
        if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
 | 
					 | 
				
			||||||
        if (!canvas) createCanvas();
 | 
					 | 
				
			||||||
        canvas.style.opacity = 1;
 | 
					 | 
				
			||||||
        canvas.style.display = "block";
 | 
					 | 
				
			||||||
        topbar.progress(0);
 | 
					 | 
				
			||||||
        if (options.autoRun) {
 | 
					 | 
				
			||||||
          (function loop() {
 | 
					 | 
				
			||||||
            progressTimerId = window.requestAnimationFrame(loop);
 | 
					 | 
				
			||||||
            topbar.progress(
 | 
					 | 
				
			||||||
              "+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          })();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      progress: function (to) {
 | 
					 | 
				
			||||||
        if (typeof to === "undefined") return currentProgress;
 | 
					 | 
				
			||||||
        if (typeof to === "string") {
 | 
					 | 
				
			||||||
          to =
 | 
					 | 
				
			||||||
            (to.indexOf("+") >= 0 || to.indexOf("-") >= 0
 | 
					 | 
				
			||||||
              ? currentProgress
 | 
					 | 
				
			||||||
              : 0) + parseFloat(to);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        currentProgress = to > 1 ? 1 : to;
 | 
					 | 
				
			||||||
        repaint();
 | 
					 | 
				
			||||||
        return currentProgress;
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      hide: function () {
 | 
					 | 
				
			||||||
        if (!showing) return;
 | 
					 | 
				
			||||||
        showing = false;
 | 
					 | 
				
			||||||
        if (progressTimerId != null) {
 | 
					 | 
				
			||||||
          window.cancelAnimationFrame(progressTimerId);
 | 
					 | 
				
			||||||
          progressTimerId = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        (function loop() {
 | 
					 | 
				
			||||||
          if (topbar.progress("+.1") >= 1) {
 | 
					 | 
				
			||||||
            canvas.style.opacity -= 0.05;
 | 
					 | 
				
			||||||
            if (canvas.style.opacity <= 0.05) {
 | 
					 | 
				
			||||||
              canvas.style.display = "none";
 | 
					 | 
				
			||||||
              fadeTimerId = null;
 | 
					 | 
				
			||||||
              return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          fadeTimerId = window.requestAnimationFrame(loop);
 | 
					 | 
				
			||||||
        })();
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (typeof module === "object" && typeof module.exports === "object") {
 | 
					 | 
				
			||||||
    module.exports = topbar;
 | 
					 | 
				
			||||||
  } else if (typeof define === "function" && define.amd) {
 | 
					 | 
				
			||||||
    define(function () {
 | 
					 | 
				
			||||||
      return topbar;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    this.topbar = topbar;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}.call(this, window, document));
 | 
					 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
- Update dependencies
 | 
					- Update dependencies
 | 
				
			||||||
- Show topbar on form submit/page refresh
 | 
					- Show topbar on form submit/page refresh
 | 
				
			||||||
- Make loading/reconnection less intrusive
 | 
					- Make loading/reconnection less intrusive
 | 
				
			||||||
 | 
					- Add QR code for invite link
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v0.1.6
 | 
					# v0.1.6
 | 
				
			||||||
- fix formatting in note/context/step contents
 | 
					- fix formatting in note/context/step contents
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,17 +13,18 @@ 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("TEST_DATABASE_URL") ||
 | 
					    System.get_env(
 | 
				
			||||||
 | 
					      "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") ||
 | 
					    System.get_env("DATABASE_URL", "ecto://postgres:postgres@memex-db/memex")
 | 
				
			||||||
      "ecto://postgres:postgres@memex-db/memex"
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
host =
 | 
					host =
 | 
				
			||||||
@@ -38,7 +39,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,
 | 
				
			||||||
@@ -47,10 +48,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.
 | 
				
			||||||
@@ -74,12 +75,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
 | 
				
			||||||
  #
 | 
					  #
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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}
 | 
					        layout: {MemexWeb.LayoutView, "live.html"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      on_mount MemexWeb.InitAssigns
 | 
					      on_mount MemexWeb.InitAssigns
 | 
				
			||||||
      unquote(view_helpers())
 | 
					      unquote(view_helpers())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ 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
 | 
				
			||||||
@@ -16,8 +18,14 @@ 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">
 | 
				
			||||||
          <%= gettext("Uses Left:") %>
 | 
					          <%= if @invite.uses_left do %>
 | 
				
			||||||
          <%= @invite.uses_left || gettext("unlimited") %>
 | 
					            <%= gettext(
 | 
				
			||||||
 | 
					              "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">
 | 
				
			||||||
@@ -25,17 +33,18 @@ 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) %>
 | 
					        ><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code>
 | 
				
			||||||
        </code>
 | 
					        <%= render_slot(@code_actions) %>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <%= if @code_actions do %>
 | 
					 | 
				
			||||||
          <%= render_slot(@code_actions) %>
 | 
					 | 
				
			||||||
        <% end %>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <%= if @inner_block do %>
 | 
					      <%= if @inner_block do %>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 %>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,19 +19,23 @@ 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 |> is_nil() do %>
 | 
					          <%= if @user.confirmed_at do %>
 | 
				
			||||||
            <%= gettext("email unconfirmed") %>
 | 
					            <%= gettext(
 | 
				
			||||||
 | 
					              "user confirmed on%{confirmed_datetime}",
 | 
				
			||||||
 | 
					              confirmed_datetime: ""
 | 
				
			||||||
 | 
					            ) %>
 | 
				
			||||||
 | 
					            <.datetime datetime={@user.confirmed_at} />
 | 
				
			||||||
          <% else %>
 | 
					          <% else %>
 | 
				
			||||||
            <p>
 | 
					            <%= gettext("email unconfirmed") %>
 | 
				
			||||||
              <%= gettext("user confirmed on") %>
 | 
					 | 
				
			||||||
              <%= @user.confirmed_at |> display_datetime() %>
 | 
					 | 
				
			||||||
            </p>
 | 
					 | 
				
			||||||
          <% end %>
 | 
					          <% end %>
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <p>
 | 
					        <p>
 | 
				
			||||||
          <%= gettext("user registered on") %>
 | 
					          <%= gettext(
 | 
				
			||||||
          <%= @user.inserted_at |> display_datetime() %>
 | 
					            "user registered on%{registered_datetime}",
 | 
				
			||||||
 | 
					            registered_datetime: ""
 | 
				
			||||||
 | 
					          ) %>
 | 
				
			||||||
 | 
					          <.datetime datetime={@user.inserted_at} />
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
      </h3>
 | 
					      </h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,15 +11,17 @@ 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, default: Application.compile_env(:gettext, :default_locale, "en_US")
 | 
					    plug :put_user_locale
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, default: default) do
 | 
					  defp put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, _opts) do
 | 
				
			||||||
 | 
					    default = Application.fetch_env!(:gettext, :default_locale)
 | 
				
			||||||
    Gettext.put_locale(locale || default)
 | 
					    Gettext.put_locale(locale || default)
 | 
				
			||||||
    conn |> put_session(:locale, locale || default)
 | 
					    conn |> put_session(:locale, locale || default)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  defp put_user_locale(conn, default: default) do
 | 
					  defp put_user_locale(conn, _opts) do
 | 
				
			||||||
 | 
					    default = Application.fetch_env!(:gettext, :default_locale)
 | 
				
			||||||
    Gettext.put_locale(default)
 | 
					    Gettext.put_locale(default)
 | 
				
			||||||
    conn |> put_session(:locale, default)
 | 
					    conn |> put_session(:locale, default)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<main class="mb-8 min-w-full">
 | 
					<main class="pb-8 min-w-full">
 | 
				
			||||||
  <header>
 | 
					  <header>
 | 
				
			||||||
    <.topbar current_user={assigns[:current_user]}></.topbar>
 | 
					    <.topbar current_user={assigns[:current_user]}></.topbar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en" class="m-0 p-0 w-full h-full">
 | 
					<html lang="en" class="m-0 p-0 w-full h-full bg-primary-800">
 | 
				
			||||||
  <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 bg-primary-800 text-primary-400 subpixel-antialiased">
 | 
					  <body class="m-0 p-0 w-full h-full text-primary-400 subpixel-antialiased">
 | 
				
			||||||
    <%= @inner_content %>
 | 
					    <%= @inner_content %>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 %>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,56 +5,94 @@ defmodule MemexWeb.ViewHelpers do
 | 
				
			|||||||
  :view`
 | 
					  :view`
 | 
				
			||||||
  """
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  import Phoenix.Component
 | 
					  use Phoenix.Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @doc """
 | 
					  @doc """
 | 
				
			||||||
  Returns a <time> element that renders the naivedatetime in the user's local
 | 
					  Phoenix.Component for a <time> element that renders the naivedatetime in the
 | 
				
			||||||
  timezone with Alpine.js
 | 
					  user's local timezone with Alpine.js
 | 
				
			||||||
  """
 | 
					  """
 | 
				
			||||||
  @spec display_datetime(NaiveDateTime.t() | nil) :: Phoenix.LiveView.Rendered.t()
 | 
					 | 
				
			||||||
  def display_datetime(nil), do: ""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def display_datetime(datetime) do
 | 
					  attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil"
 | 
				
			||||||
    assigns = %{
 | 
					 | 
				
			||||||
      datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def datetime(assigns) do
 | 
				
			||||||
    ~H"""
 | 
					    ~H"""
 | 
				
			||||||
    <time
 | 
					    <%= if @datetime do %>
 | 
				
			||||||
      datetime={@datetime}
 | 
					      <time
 | 
				
			||||||
      x-data={"{
 | 
					        datetime={cast_datetime(@datetime)}
 | 
				
			||||||
        date:
 | 
					        x-data={"{
 | 
				
			||||||
          Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
 | 
					          datetime:
 | 
				
			||||||
            .format(new Date(\"#{@datetime}\"))
 | 
					            Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
 | 
				
			||||||
      }"}
 | 
					              .format(new Date(\"#{cast_datetime(@datetime)}\"))
 | 
				
			||||||
      x-text="date"
 | 
					        }"}
 | 
				
			||||||
    >
 | 
					        x-text="datetime"
 | 
				
			||||||
      <%= @datetime %>
 | 
					      >
 | 
				
			||||||
    </time>
 | 
					        <%= 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={"{
 | 
				
			||||||
 | 
					          date:
 | 
				
			||||||
 | 
					            Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'})
 | 
				
			||||||
 | 
					              .format(new Date(\"#{@date |> Date.to_iso8601(:extended)}\"))
 | 
				
			||||||
 | 
					        }"}
 | 
				
			||||||
 | 
					        x-text="date"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <%= @date |> Date.to_iso8601(:extended) %>
 | 
				
			||||||
 | 
					      </time>
 | 
				
			||||||
 | 
					    <% end %>
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @doc """
 | 
					  @doc """
 | 
				
			||||||
  Returns a <date> element that renders the Date in the user's local
 | 
					  Displays content in a QR code as a base64 encoded PNG
 | 
				
			||||||
  timezone with Alpine.js
 | 
					 | 
				
			||||||
  """
 | 
					  """
 | 
				
			||||||
  @spec display_date(Date.t() | nil) :: Phoenix.LiveView.Rendered.t()
 | 
					  @spec qr_code_image(String.t()) :: String.t()
 | 
				
			||||||
  def display_date(nil), do: ""
 | 
					  @spec qr_code_image(String.t(), width :: non_neg_integer()) :: String.t()
 | 
				
			||||||
 | 
					  def qr_code_image(content, width \\ 384) do
 | 
				
			||||||
 | 
					    img_data =
 | 
				
			||||||
 | 
					      content
 | 
				
			||||||
 | 
					      |> EQRCode.encode()
 | 
				
			||||||
 | 
					      |> EQRCode.png(width: width, background_color: <<39, 39, 42>>, color: <<255, 255, 255>>)
 | 
				
			||||||
 | 
					      |> Base.encode64()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def display_date(date) do
 | 
					    "data:image/png;base64," <> img_data
 | 
				
			||||||
    assigns = %{date: date |> Date.to_iso8601(:extended)}
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Creates a downloadable QR Code element
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  attr :content, :string, required: true
 | 
				
			||||||
 | 
					  attr :filename, :string, default: "qrcode", doc: "filename without .png extension"
 | 
				
			||||||
 | 
					  attr :image_class, :string, default: "w-64 h-max"
 | 
				
			||||||
 | 
					  attr :width, :integer, default: 384, doc: "width of png to generate"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def qr_code(assigns) do
 | 
				
			||||||
    ~H"""
 | 
					    ~H"""
 | 
				
			||||||
    <time
 | 
					    <a href={qr_code_image(@content)} download={@filename <> ".png"}>
 | 
				
			||||||
      datetime={@date}
 | 
					      <img class={@image_class} alt={@filename} src={qr_code_image(@content)} />
 | 
				
			||||||
      x-data={"{
 | 
					    </a>
 | 
				
			||||||
        date:
 | 
					 | 
				
			||||||
          Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\"))
 | 
					 | 
				
			||||||
      }"}
 | 
					 | 
				
			||||||
      x-text="date"
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <%= @date %>
 | 
					 | 
				
			||||||
    </time>
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								mix.exs
									
									
									
									
									
								
							@@ -48,27 +48,29 @@ defmodule Memex.MixProject do
 | 
				
			|||||||
  defp deps do
 | 
					  defp deps do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      {:bcrypt_elixir, "~> 2.0"},
 | 
					      {:bcrypt_elixir, "~> 2.0"},
 | 
				
			||||||
      {:phoenix, "~> 1.6.6"},
 | 
					      {:phoenix, "~> 1.6.0"},
 | 
				
			||||||
      {:phoenix_ecto, "~> 4.4"},
 | 
					      {:phoenix_ecto, "~> 4.4"},
 | 
				
			||||||
      {:ecto_sql, "~> 3.6"},
 | 
					 | 
				
			||||||
      {:postgrex, ">= 0.0.0"},
 | 
					 | 
				
			||||||
      {:phoenix_html, "~> 3.0"},
 | 
					      {:phoenix_html, "~> 3.0"},
 | 
				
			||||||
      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
      {:phoenix_live_view, "~> 0.18.3"},
 | 
					      {:phoenix_live_view, "~> 0.18.0"},
 | 
				
			||||||
 | 
					      {:phoenix_view, "~> 1.1"},
 | 
				
			||||||
 | 
					      {:phoenix_live_dashboard, "~> 0.6"},
 | 
				
			||||||
 | 
					      {:ecto_sql, "~> 3.6"},
 | 
				
			||||||
 | 
					      {:postgrex, ">= 0.0.0"},
 | 
				
			||||||
      {: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}
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								mix.lock
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								mix.lock
									
									
									
									
									
								
							@@ -16,6 +16,7 @@
 | 
				
			|||||||
  "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
 | 
					  "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
 | 
				
			||||||
  "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
 | 
					  "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
 | 
				
			||||||
  "elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
 | 
					  "elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
 | 
				
			||||||
 | 
					  "eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
 | 
				
			||||||
  "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
 | 
					  "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"},
 | 
					  "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
 | 
				
			||||||
  "expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},
 | 
					  "expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},
 | 
				
			||||||
@@ -33,13 +34,12 @@
 | 
				
			|||||||
  "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.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_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_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.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_view": {:hex, :phoenix_live_view, "0.18.11", "c50eac83dae6b5488859180422dfb27b2c609de87f4aa5b9c926ecd0501cd44f", [: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", "76c99a0ffb47cd95bf06a917e74f282a603f3e77b00375f3c2dd95110971b102"},
 | 
					  "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_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_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, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
 | 
				
			||||||
  "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"},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,11 +56,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Settings"
 | 
					msgid "Settings"
 | 
				
			||||||
msgstr ""
 | 
					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"
 | 
				
			||||||
@@ -147,7 +142,7 @@ msgstr ""
 | 
				
			|||||||
msgid "email"
 | 
					msgid "email"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:23
 | 
					#: lib/memex_web/components/user_card.ex:29
 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					#, elixir-autogen, elixir-format
 | 
				
			||||||
msgid "email unconfirmed"
 | 
					msgid "email unconfirmed"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -183,7 +178,7 @@ msgstr ""
 | 
				
			|||||||
msgid "instance information"
 | 
					msgid "instance information"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/invite_card.ex:24
 | 
					#: lib/memex_web/components/invite_card.ex:32
 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					#, elixir-autogen, elixir-format
 | 
				
			||||||
msgid "invite disabled"
 | 
					msgid "invite disabled"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -330,11 +325,6 @@ msgstr ""
 | 
				
			|||||||
msgid "tags"
 | 
					msgid "tags"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/invite_card.ex:20
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					 | 
				
			||||||
msgid "unlimited"
 | 
					 | 
				
			||||||
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"
 | 
				
			||||||
@@ -646,17 +636,27 @@ msgstr ""
 | 
				
			|||||||
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
 | 
					msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:26
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					 | 
				
			||||||
msgid "user confirmed on"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:33
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format, fuzzy
 | 
					 | 
				
			||||||
msgid "user registered on"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: lib/memex_web/templates/user_registration/new.html.heex:32
 | 
					#: lib/memex_web/templates/user_registration/new.html.heex:32
 | 
				
			||||||
#, elixir-autogen, elixir-format, fuzzy
 | 
					#, elixir-autogen, elixir-format, fuzzy
 | 
				
			||||||
msgid "language"
 | 
					msgid "language"
 | 
				
			||||||
msgstr ""
 | 
					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 ""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,11 +45,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Settings"
 | 
					msgid "Settings"
 | 
				
			||||||
msgstr ""
 | 
					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"
 | 
				
			||||||
@@ -136,7 +131,7 @@ msgstr ""
 | 
				
			|||||||
msgid "email"
 | 
					msgid "email"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:23
 | 
					#: lib/memex_web/components/user_card.ex:29
 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					#, elixir-autogen, elixir-format
 | 
				
			||||||
msgid "email unconfirmed"
 | 
					msgid "email unconfirmed"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -172,7 +167,7 @@ msgstr ""
 | 
				
			|||||||
msgid "instance information"
 | 
					msgid "instance information"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/invite_card.ex:24
 | 
					#: lib/memex_web/components/invite_card.ex:32
 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					#, elixir-autogen, elixir-format
 | 
				
			||||||
msgid "invite disabled"
 | 
					msgid "invite disabled"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -319,11 +314,6 @@ msgstr ""
 | 
				
			|||||||
msgid "tags"
 | 
					msgid "tags"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/invite_card.ex:20
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					 | 
				
			||||||
msgid "unlimited"
 | 
					 | 
				
			||||||
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"
 | 
				
			||||||
@@ -635,17 +625,27 @@ msgstr ""
 | 
				
			|||||||
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
 | 
					msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:26
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					 | 
				
			||||||
msgid "user confirmed on"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: lib/memex_web/components/user_card.ex:33
 | 
					 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					 | 
				
			||||||
msgid "user registered on"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: lib/memex_web/templates/user_registration/new.html.heex:32
 | 
					#: lib/memex_web/templates/user_registration/new.html.heex:32
 | 
				
			||||||
#, elixir-autogen, elixir-format
 | 
					#, elixir-autogen, elixir-format
 | 
				
			||||||
msgid "language"
 | 
					msgid "language"
 | 
				
			||||||
msgstr ""
 | 
					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 ""
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user