diff --git a/lib/core.ex b/lib/core.ex index 0ba92d0..f49c395 100644 --- a/lib/core.ex +++ b/lib/core.ex @@ -1,4 +1,4 @@ defmodule Core do @moduledoc false - use Boundary, deps: [Schema], exports: [Author, Posts, Statuses] + use Boundary, deps: [Schema], exports: [] end diff --git a/lib/core/author.ex b/lib/core/author.ex deleted file mode 100644 index 6ec9eef..0000000 --- a/lib/core/author.ex +++ /dev/null @@ -1,46 +0,0 @@ -defmodule Core.Author do - @moduledoc """ - Properties of the author, Sloane - - Modeled after the [h-card] specification. - - [h-card]: https://microformats.org/wiki/h-card - """ - use TypedStruct - - @public_properties ~w[name nickname url]a - - typedstruct do - field :name, String.t() - field :given_name, String.t() - field :additional_name, String.t() - field :family_name, String.t() - field :nickname, String.t() - field :email, String.t() - field :url, String.t() - end - - def sloane do - %__MODULE__{ - name: "Sloane Perrault", - given_name: "Sloane", - additional_name: "Loretta", - family_name: "Perrault", - nickname: "sloanely_but_surely", - email: "sloane@fastmail.com", - url: "https://sloanelybutsurely.com" - } - end - - def full do - sloane() - end - - def public do - author = full() - - for key <- @public_properties, reduce: %__MODULE__{} do - acc -> Map.put(acc, key, Map.get(author, key)) - end - end -end diff --git a/lib/core/posts.ex b/lib/core/posts.ex deleted file mode 100644 index e674b08..0000000 --- a/lib/core/posts.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Core.Posts do - @moduledoc false - import Ecto.Changeset - import Ecto.Query - - alias Core.Repo - - def changeset(%Schema.Post{} = post, attrs) do - post - |> cast(attrs, [:title, :body]) - |> validate_required([:body]) - end - - def create_post(attrs) do - %Schema.Post{} - |> changeset(attrs) - |> Repo.insert() - end - - def update_post(post, attrs) do - post - |> changeset(attrs) - |> Repo.update() - end - - def get_post!(id) do - Repo.get!(Schema.Post, id) - end - - def list_posts do - query = - from post in Schema.Post, - order_by: [desc: post.inserted_at] - - Repo.all(query) - end -end diff --git a/lib/core/statuses.ex b/lib/core/statuses.ex deleted file mode 100644 index bf9827c..0000000 --- a/lib/core/statuses.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Core.Statuses do - @moduledoc false - import Ecto.Changeset - import Ecto.Query - - alias Core.Repo - - def changeset(%Schema.Status{} = status, attrs \\ %{}) do - status - |> cast(attrs, [:body]) - |> validate_required([:body]) - end - - def create_status(attrs) do - %Schema.Status{} - |> changeset(attrs) - |> Repo.insert() - end - - def update_status(status, attrs) do - status - |> changeset(attrs) - |> Repo.update() - end - - def get_status!(id) do - Repo.get!(Schema.Status, id) - end - - def list_statuses do - query = - from status in Schema.Status, - order_by: [desc: status.inserted_at] - - Repo.all(query) - end -end diff --git a/lib/schema.ex b/lib/schema.ex index a23bc07..b55eea6 100644 --- a/lib/schema.ex +++ b/lib/schema.ex @@ -1,4 +1,4 @@ defmodule Schema do @moduledoc false - use Boundary, deps: [], exports: [Post, Status] + use Boundary, deps: [], exports: [] end diff --git a/lib/schema/post.ex b/lib/schema/post.ex deleted file mode 100644 index f61ca39..0000000 --- a/lib/schema/post.ex +++ /dev/null @@ -1,12 +0,0 @@ -defmodule Schema.Post do - @moduledoc false - use Ecto.Schema - - @primary_key {:id, :binary_id, autogenerate: true} - schema "posts" do - field :title, :string - field :body, :string - - timestamps() - end -end diff --git a/lib/schema/status.ex b/lib/schema/status.ex deleted file mode 100644 index b7d2feb..0000000 --- a/lib/schema/status.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Schema.Status do - @moduledoc false - use Ecto.Schema - - @primary_key {:id, :binary_id, autogenerate: true} - schema "statuses" do - field :body - - timestamps() - end -end diff --git a/lib/web/components/core_components.ex b/lib/web/components/core_components.ex index cfdd2a9..235e9ee 100644 --- a/lib/web/components/core_components.ex +++ b/lib/web/components/core_components.ex @@ -3,147 +3,4 @@ defmodule Web.CoreComponents do Provides core UI components. """ use Phoenix.Component - - attr :class, :string, default: nil - attr :global, :global - slot :inner_block - - def title(assigns) do - ~H""" - <h1 class={["font-bold text-xl mb-3", @class]} {@global}>{render_slot(@inner_block)}</h1> - """ - end - - attr :class, :string, default: nil - attr :global, :global - slot :inner_block - - def subtitle(assigns) do - ~H""" - <h2 class={["font-bold text-lg mb-2", @class]} {@global}>{render_slot(@inner_block)}</h2> - """ - end - - attr :class, :string, default: nil - - attr :global, :global, - include: ~w[navigate patch href replace method csrf_token download hreflang referrerpolicy rel target type] - - slot :inner_block - - def a(assigns) do - ~H""" - <.link class={["hover:underline", @class]} {@global}>{render_slot(@inner_block)}</.link> - """ - end - - attr :class, :string, default: nil - attr :type, :string, default: "button" - attr :global, :global - slot :inner_block - - def button(assigns) do - ~H""" - <button type={@type} class={["hover:underline", @class]} {@global}> - {render_slot(@inner_block)} - </button> - """ - end - - attr :class, :string, default: nil - attr :field, Phoenix.HTML.FormField, required: true - attr :type, :string, default: "text" - attr :global, :global, include: ~w[required placeholder] - - def input(%{type: "textarea"} = assigns) do - ~H""" - <textarea - class={["px-2 py-1 border border-gray-400 rounded", @class]} - id={@field.id} - name={@field.name} - {@global} - >{@field.value}</textarea> - """ - end - - def input(assigns) do - ~H""" - <input - class={["px-2 py-1 border border-gray-400 rounded", @class]} - type={@type} - id={@field.id} - name={@field.name} - value={@field.value} - {@global} - /> - """ - end - - @doc """ - Renders a [Heroicon](https://heroicons.com). - - Heroicons come in three styles – outline, solid, and mini. - By default, the outline style is used, but solid and mini may - be applied by using the `-solid` and `-mini` suffix. - - You can customize the size and colors of the icons by setting - width, height, and background color classes. - - Icons are extracted from the `deps/heroicons` directory and bundled within - your compiled app.css by the plugin in your `assets/tailwind.config.js`. - - ## Examples - - <.icon name="hero-x-mark-solid" /> - <.icon name="hero-arrow-path" class="ml-1 w-3 h-3 animate-spin" /> - """ - attr :name, :string, required: true - attr :class, :string, default: nil - - def icon(%{name: "hero-" <> _} = assigns) do - ~H""" - <span class={[@name, @class]} /> - """ - end - - attr :format, :string, required: true - attr :value, :any, default: nil - attr :formatter, :atom, default: :default - attr :timezone, :string, default: "America/New_York" - attr :global, :global - - def timex(%{value: nil} = assigns) do - ~H""" - <time datetime="">--</time> - """ - end - - def timex(%{value: value, timezone: timezone} = assigns) do - assigns = - assign_new(assigns, :local_value, fn -> - case value do - %DateTime{} = datetime -> - datetime - - %NaiveDateTime{} = naive -> - naive - |> DateTime.from_naive!("Etc/UTC") - |> DateTime.shift_zone!(timezone) - end - end) - - ~H""" - <time - datetime={Timex.format!(@local_value, "{ISO:Extended}")} - title={Timex.format!(@local_value, "{Mshort} {D}, {YYYY}, {h12}:{m} {AM} {Zabbr}")} - {@global} - > - {Timex.format!(@local_value, @format, timex_formatter(@formatter))} - </time> - """ - end - - defp timex_formatter(formatter) do - Module.concat(Timex.Format.DateTime.Formatters, :string.titlecase("#{formatter}")) - end end diff --git a/lib/web/components/layouts.ex b/lib/web/components/layouts.ex index 7ed0d51..55dce7d 100644 --- a/lib/web/components/layouts.ex +++ b/lib/web/components/layouts.ex @@ -10,10 +10,4 @@ defmodule Web.Layouts do use Web, :html embed_templates "layouts/*" - - defp post?("/writing/" <> _), do: true - defp post?(_), do: false - - defp status?("/microblog/" <> _), do: true - defp status?(_), do: false end diff --git a/lib/web/components/layouts/app.html.heex b/lib/web/components/layouts/app.html.heex index 0fffe7f..ccdf89c 100644 --- a/lib/web/components/layouts/app.html.heex +++ b/lib/web/components/layouts/app.html.heex @@ -1,53 +1 @@ -<div class="sticky top-0 z-50 flex flex-row bg-white justify-between py-1 md:mb-2 border-b border-gray-200"> - <div class="flex flex-col md:flex-row"> - <section class="flex flex-row gap-x-2 md:border-r border-gray-200 px-2"> - <.a href={~p"/"} class="font-bold">sloanelybutsurely.com</.a> - <nav> - <ul class="flex flex-row gap-x-2"> - <li> - <.a href={~p"/writing"}>writing</.a> - </li> - <li> - <.a href={~p"/microblog"}>microblog</.a> - </li> - </ul> - </nav> - </section> - <section :if={@admin?} class="flex flex-row gap-x-2 px-2"> - <nav> - <ul class="flex flex-row gap-x-2"> - <li> - <.a navigate={~p"/admin"}>admin</.a> - </li> - <li> - <.a href={~p"/admin/statuses/new"}>new status</.a> - </li> - <li> - <.a href={~p"/admin/posts/new"}>new post</.a> - </li> - <li :if={post?(@current_path)}> - <.a href={~p"/admin/posts/#{@post}"}>edit post</.a> - </li> - <li :if={status?(@current_path)}> - <.a href={~p"/admin/statuses/#{@status}"}>edit status</.a> - </li> - </ul> - </nav> - </section> - </div> - - <.a :if={@admin?} class="px-2" href={~p"/admin/session/destroy?return_to=#{@current_path}"}> - sign out - </.a> - <.a - :if={!@admin?} - class="px-2 text-transparent hover:text-current" - href={~p"/sign-in?return_to=#{@current_path}"} - > - sign in - </.a> -</div> - -<main class="p-2 max-w-2xl mx-auto"> - {@inner_content} -</main> +{@inner_content} diff --git a/lib/web/components/layouts/root.html.heex b/lib/web/components/layouts/root.html.heex index ab725f5..a147bdf 100644 --- a/lib/web/components/layouts/root.html.heex +++ b/lib/web/components/layouts/root.html.heex @@ -10,11 +10,6 @@ <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}> </script> - <%= if @load_trix? do %> - <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css" /> - <script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"> - </script> - <% end %> </head> <body class="bg-white"> {@inner_content} diff --git a/lib/web/controllers/admin_auth.ex b/lib/web/controllers/admin_auth.ex deleted file mode 100644 index 40da948..0000000 --- a/lib/web/controllers/admin_auth.ex +++ /dev/null @@ -1,62 +0,0 @@ -defmodule Web.AdminAuth do - @moduledoc false - use Web, :verified_routes - - import Phoenix.Controller - import Plug.Conn - - def log_in_admin(conn, params) do - conn - |> renew_session() - |> put_session(:admin?, true) - |> redirect(to: params["return_to"] || ~p"/admin") - end - - def log_out_admin(conn, params) do - if live_socket_id = get_session(conn, :live_socket_id) do - Web.Endpoint.broadcast(live_socket_id, "disconnect", %{}) - end - - conn - |> renew_session() - |> redirect(to: params["return_to"] || ~p"/") - end - - def mount_admin(%Plug.Conn{} = conn, _opts) do - assign(conn, :admin?, admin?(conn)) - end - - def require_admin(%Plug.Conn{assigns: %{admin?: true}} = conn, _opts) do - conn - end - - def require_admin(conn, _opts) do - redirect(conn, to: ~p"/sign-in?return_to=#{conn.request_path}") - end - - def correct_password?(password) do - password_hash = Application.fetch_env!(:sloanely_but_surely, :password_hash) - - Argon2.verify_pass(password, password_hash) - end - - def on_mount(:default, _params, session, socket) do - {:cont, Phoenix.Component.assign(socket, :admin?, admin?(session))} - end - - ## private - - defp renew_session(conn) do - delete_csrf_token() - - conn - |> configure_session(renew: true) - |> clear_session() - end - - defp admin?(%Plug.Conn{} = conn) do - Plug.Conn.get_session(conn, :admin?, false) == true - end - - defp admin?(%{} = session), do: Map.get(session, "admin?", false) == true -end diff --git a/lib/web/controllers/admin_session_controller.ex b/lib/web/controllers/admin_session_controller.ex deleted file mode 100644 index 1b5e913..0000000 --- a/lib/web/controllers/admin_session_controller.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule Web.AdminSessionController do - use Web, :controller - - alias Web.AdminAuth - - def create(conn, %{"password" => password} = params) do - if AdminAuth.correct_password?(password) do - AdminAuth.log_in_admin(conn, params) - else - redirect(conn, to: ~p"/sign-in") - end - end - - def create(conn, _params) do - redirect(conn, to: ~p"/sign-in") - end - - def destroy(conn, params) do - AdminAuth.log_out_admin(conn, params) - end -end diff --git a/lib/web/controllers/globals.ex b/lib/web/controllers/globals.ex deleted file mode 100644 index f45728a..0000000 --- a/lib/web/controllers/globals.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule Web.Globals do - @moduledoc false - use Web, :live_view - - def assign_globals(%Plug.Conn{} = conn, _opts) do - conn - |> Plug.Conn.assign(:current_path, conn.request_path) - |> Plug.Conn.assign(:load_trix?, false) - end - - def on_mount(:default, _params, _session, socket) do - socket = - socket - |> attach_hook(:assign_handle_params_globals, :handle_params, fn _params, uri, socket -> - %URI{path: current_path} = URI.parse(uri) - {:cont, assign(socket, :current_path, current_path)} - end) - |> assign(:load_trix?, false) - - {:cont, socket} - end -end diff --git a/lib/web/controllers/page_controller.ex b/lib/web/controllers/page_controller.ex deleted file mode 100644 index d35f827..0000000 --- a/lib/web/controllers/page_controller.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Web.PageController do - use Web, :controller - - def home(conn, _params) do - posts = Enum.take(Core.Posts.list_posts(), 5) - statuses = Enum.take(Core.Statuses.list_statuses(), 10) - - conn - |> assign(:posts, posts) - |> assign(:statuses, statuses) - |> render(:home) - end -end diff --git a/lib/web/controllers/page_html.ex b/lib/web/controllers/page_html.ex deleted file mode 100644 index 70589f4..0000000 --- a/lib/web/controllers/page_html.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule Web.PageHTML do - @moduledoc """ - This module contains pages rendered by PageController. - - See the `page_html` directory for all templates available. - """ - use Web, :html - - embed_templates "page_html/*" -end diff --git a/lib/web/controllers/page_html/home.html.heex b/lib/web/controllers/page_html/home.html.heex deleted file mode 100644 index 83270d6..0000000 --- a/lib/web/controllers/page_html/home.html.heex +++ /dev/null @@ -1,46 +0,0 @@ -<div class="flex flex-col gap-y-4"> - <section> - <.title> - <.a href={~p"/writing"}>writing</.a> - </.title> - <ul class="flex flex-col"> - <li :for={post <- @posts}> - <.link href={~p"/writing/#{post}"} class="flex flex-row justify-between group"> - <span class="group-hover:underline"> - <%= if post.title do %> - {post.title} - <% else %> - (no title) - <% end %> - </span> - <.timex - value={post.inserted_at} - format="{YYYY}-{0M}-{0D}" - class="text-gray-500 text-nowrap" - /> - </.link> - </li> - </ul> - </section> - - <section> - <.title> - <.a href={~p"/microblog"}>microblog</.a> - </.title> - <ul class="flex flex-col"> - <li :for={status <- @statuses}> - <.link href={~p"/microblog/#{status}"} class="flex flex-row justify-between group"> - <span class="group-hover:underline overflow-hidden text-ellipsis text-nowrap"> - {status.body} - </span> - <.timex - value={status.inserted_at} - format="{relative}" - formatter={:relative} - class="text-gray-500 text-nowrap" - /> - </.link> - </li> - </ul> - </section> -</div> diff --git a/lib/web/controllers/post_controller.ex b/lib/web/controllers/post_controller.ex deleted file mode 100644 index 181261b..0000000 --- a/lib/web/controllers/post_controller.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Web.PostController do - use Web, :controller - - def index(conn, _params) do - posts = Core.Posts.list_posts() - - conn - |> assign(:posts, posts) - |> render(:index) - end - - def show(conn, %{"post_id" => post_id}) do - post = Core.Posts.get_post!(post_id) - - conn - |> assign(:post, post) - |> render(:show) - end -end diff --git a/lib/web/controllers/post_html.ex b/lib/web/controllers/post_html.ex deleted file mode 100644 index c7cf197..0000000 --- a/lib/web/controllers/post_html.ex +++ /dev/null @@ -1,6 +0,0 @@ -defmodule Web.PostHTML do - @moduledoc false - use Web, :html - - embed_templates "post_html/*" -end diff --git a/lib/web/controllers/post_html/index.html.heex b/lib/web/controllers/post_html/index.html.heex deleted file mode 100644 index 7c6863b..0000000 --- a/lib/web/controllers/post_html/index.html.heex +++ /dev/null @@ -1,19 +0,0 @@ -<.title>writing</.title> -<ul class="flex flex-col"> - <li :for={post <- @posts}> - <.link href={~p"/writing/#{post}"} class="flex flex-row justify-between group"> - <span class="group-hover:underline"> - <%= if post.title do %> - {post.title} - <% else %> - (no title) - <% end %> - </span> - <.timex - value={post.inserted_at} - format="{YYYY}-{0M}-{0D}" - class="text-gray-500 text-nowrap" - /> - </.link> - </li> -</ul> diff --git a/lib/web/controllers/post_html/show.html.heex b/lib/web/controllers/post_html/show.html.heex deleted file mode 100644 index 991292c..0000000 --- a/lib/web/controllers/post_html/show.html.heex +++ /dev/null @@ -1,9 +0,0 @@ -<article> - <header class="flex flex-row"> - <h1 :if={@post.title} class="font-bold text-xl mb-3">{@post.title}</h1> - </header> - <section class="prose prose-cms max-w-none">{raw(@post.body)}</section> - <footer class="mt-5 py-1 border-t border-gray-200 relative"> - <.link :if={@admin?} class="absolute right-0" href={~p"/admin/posts/#{@post}"}>edit</.link> - </footer> -</article> diff --git a/lib/web/controllers/status_controller.ex b/lib/web/controllers/status_controller.ex deleted file mode 100644 index 7c8798e..0000000 --- a/lib/web/controllers/status_controller.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Web.StatusController do - use Web, :controller - - def index(conn, _params) do - statuses = Core.Statuses.list_statuses() - - conn - |> assign(:statuses, statuses) - |> render(:index) - end - - def show(conn, %{"status_id" => status_id}) do - status = Core.Statuses.get_status!(status_id) - - conn - |> assign(:status, status) - |> render(:show) - end -end diff --git a/lib/web/controllers/status_html.ex b/lib/web/controllers/status_html.ex deleted file mode 100644 index e99f601..0000000 --- a/lib/web/controllers/status_html.ex +++ /dev/null @@ -1,6 +0,0 @@ -defmodule Web.StatusHTML do - @moduledoc false - use Web, :html - - embed_templates "status_html/*" -end diff --git a/lib/web/controllers/status_html/index.html.heex b/lib/web/controllers/status_html/index.html.heex deleted file mode 100644 index 3e21c2a..0000000 --- a/lib/web/controllers/status_html/index.html.heex +++ /dev/null @@ -1,16 +0,0 @@ -<.title>microblog</.title> -<ul class="flex flex-col"> - <li :for={status <- @statuses}> - <.link href={~p"/microblog/#{status}"} class="flex flex-row justify-between group"> - <span class="group-hover:underline overflow-hidden text-ellipsis text-nowrap"> - {status.body} - </span> - <.timex - value={status.inserted_at} - format="{relative}" - formatter={:relative} - class="text-gray-500 text-nowrap" - /> - </.link> - </li> -</ul> diff --git a/lib/web/controllers/status_html/show.html.heex b/lib/web/controllers/status_html/show.html.heex deleted file mode 100644 index 4b8bdf6..0000000 --- a/lib/web/controllers/status_html/show.html.heex +++ /dev/null @@ -1,3 +0,0 @@ -<article> - <p>{@status.body}</p> -</article> diff --git a/lib/web/live/admin_live.ex b/lib/web/live/admin_live.ex deleted file mode 100644 index 3f6bc13..0000000 --- a/lib/web/live/admin_live.ex +++ /dev/null @@ -1,16 +0,0 @@ -defmodule Web.AdminLive do - @moduledoc false - use Web, :live_view - - @impl true - def mount(_params, _session, socket) do - {:ok, socket} - end - - @impl true - def render(assigns) do - ~H""" - <h1>AdminLive</h1> - """ - end -end diff --git a/lib/web/live/admin_login_live.ex b/lib/web/live/admin_login_live.ex deleted file mode 100644 index 1f94232..0000000 --- a/lib/web/live/admin_login_live.ex +++ /dev/null @@ -1,39 +0,0 @@ -defmodule Web.AdminLoginLive do - @moduledoc false - use Web, :live_view - - @impl true - def mount(params, _session, socket) do - socket = - assign( - socket, - form: to_form(%{"password" => "", "return_to" => params["return_to"]}), - return_to: params["return_to"] - ) - - {:ok, socket, layout: false} - end - - @impl true - def render(assigns) do - ~H""" - <main class="flex flex-col w-screen h-screen fixed justify-center items-center"> - <.form for={@form} action={~p"/admin/session/create"} class="flex flex-col gap-y-2"> - <.input type="hidden" field={@form[:return_to]} /> - <.input type="password" placeholder="password" field={@form[:password]} required /> - <div class="flex flex-col items-end"> - <button type="submit" class="font-bold hover:underline">sign in</button> - <.a href={cancel_href(@return_to)}> - cancel - </.a> - </div> - </.form> - </main> - """ - end - - defp cancel_href("/admin"), do: ~p"/" - defp cancel_href("/admin/" <> _), do: ~p"/" - defp cancel_href(nil), do: ~p"/" - defp cancel_href(return_to), do: return_to -end diff --git a/lib/web/live/post_live.ex b/lib/web/live/post_live.ex deleted file mode 100644 index dcb7bef..0000000 --- a/lib/web/live/post_live.ex +++ /dev/null @@ -1,76 +0,0 @@ -defmodule Web.PostLive do - @moduledoc false - use Web, :live_view - - @impl true - def mount(_params, _session, socket) do - socket = assign(socket, :load_trix?, true) - - {:ok, socket} - end - - @impl true - def handle_params(_params, _uri, %{assigns: %{live_action: :new}} = socket) do - post = %Schema.Post{} - changeset = Core.Posts.changeset(post, %{}) - - socket = assign(socket, post: post, form: to_form(changeset)) - - {:noreply, socket} - end - - def handle_params(%{"post_id" => post_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do - post = Core.Posts.get_post!(post_id) - - changeset = Core.Posts.changeset(post, %{}) - - socket = assign(socket, post: post, form: to_form(changeset)) - - {:noreply, socket} - end - - @impl true - def handle_event("save_post", %{"post" => attrs}, %{assigns: %{live_action: :new}} = socket) do - socket = - case Core.Posts.create_post(attrs) do - {:ok, post} -> push_navigate(socket, to: ~p"/admin/posts/#{post}") - {:error, changeset} -> assign(socket, form: to_form(changeset)) - end - - {:noreply, socket} - end - - def handle_event("save_post", %{"post" => attrs}, %{assigns: %{post: post, live_action: :edit}} = socket) do - socket = - case Core.Posts.update_post(post, attrs) do - {:ok, post} -> - assign(socket, - post: post, - form: - post - |> Core.Posts.changeset(%{}) - |> to_form() - ) - - {:error, changeset} -> - assign(socket, form: to_form(changeset)) - end - - {:noreply, socket} - end - - @impl true - def render(assigns) do - ~H""" - <.form for={@form} class="flex flex-col gap-y-2" phx-submit="save_post"> - <.input type="hidden" field={@form[:body]} /> - <.input class="text-lg" field={@form[:title]} placeholder="Title" /> - <div id="editor" phx-update="ignore"> - <trix-editor input={@form[:body].id} class="prose prose-cms max-w-none"></trix-editor> - </div> - - <button type="submit" class="self-end">save</button> - </.form> - """ - end -end diff --git a/lib/web/live/status_live.ex b/lib/web/live/status_live.ex deleted file mode 100644 index bbfc028..0000000 --- a/lib/web/live/status_live.ex +++ /dev/null @@ -1,70 +0,0 @@ -defmodule Web.StatusLive do - @moduledoc false - use Web, :live_view - - @impl true - def mount(_params, _session, socket) do - {:ok, socket} - end - - @impl true - def handle_params(_params, _uri, %{assigns: %{live_action: :new}} = socket) do - status = %Schema.Status{} - - changeset = Core.Statuses.changeset(status, %{}) - - socket = assign(socket, status: status, form: to_form(changeset)) - - {:noreply, socket} - end - - def handle_params(%{"status_id" => status_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do - status = Core.Statuses.get_status!(status_id) - - changeset = Core.Statuses.changeset(status, %{}) - - socket = assign(socket, status: status, form: to_form(changeset)) - - {:noreply, socket} - end - - @impl true - def handle_event("save_status", %{"status" => attrs}, %{assigns: %{live_action: :new}} = socket) do - socket = - case Core.Statuses.create_status(attrs) do - {:ok, status} -> push_navigate(socket, to: ~p"/admin/statuses/#{status}") - {:error, changeset} -> assign(socket, form: to_form(changeset)) - end - - {:noreply, socket} - end - - def handle_event("save_status", %{"status" => attrs}, %{assigns: %{status: status, live_action: :edit}} = socket) do - socket = - case Core.Statuses.update_status(status, attrs) do - {:ok, status} -> - assign(socket, - status: status, - form: - status - |> Core.Statuses.changeset(%{}) - |> to_form() - ) - - {:error, changeset} -> - assign(socket, form: to_form(changeset)) - end - - {:noreply, socket} - end - - @impl true - def render(assigns) do - ~H""" - <.form for={@form} class="flex flex-col gap-y-2" phx-submit="save_status"> - <.input type="textarea" field={@form[:body]} /> - <button type="submit" class="self-end">save</button> - </.form> - """ - end -end diff --git a/lib/web/router.ex b/lib/web/router.ex index 319c7db..debe208 100644 --- a/lib/web/router.ex +++ b/lib/web/router.ex @@ -1,12 +1,6 @@ defmodule Web.Router do use Web, :router - import Web.AdminAuth - import Web.Globals - - alias Web.AdminAuth - alias Web.Globals - pipeline :browser do plug :accepts, ["html"] plug :fetch_session @@ -14,47 +8,5 @@ defmodule Web.Router do plug :put_root_layout, html: {Web.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers - plug :assign_globals - end - - pipeline :supports_admin_action do - plug :mount_admin - end - - pipeline :requires_admin do - plug :mount_admin - plug :require_admin - end - - live_session :default, on_mount: [AdminAuth, Globals] do - scope "/", Web do - pipe_through :browser - pipe_through :supports_admin_action - - get "/", PageController, :home - - get "/writing", PostController, :index - get "/writing/:post_id", PostController, :show - - get "/microblog", StatusController, :index - get "/microblog/:status_id", StatusController, :show - - live "/sign-in", AdminLoginLive - post "/admin/session/create", AdminSessionController, :create - get "/admin/session/destroy", AdminSessionController, :destroy - end - - scope "/admin", Web do - pipe_through :browser - pipe_through :requires_admin - - live "/", AdminLive - - live "/posts/new", PostLive, :new - live "/posts/:post_id", PostLive, :edit - - live "/statuses/new", StatusLive, :new - live "/statuses/:status_id", StatusLive, :edit - end end end diff --git a/mix.exs b/mix.exs index 2d7c631..54a1b14 100644 --- a/mix.exs +++ b/mix.exs @@ -51,9 +51,6 @@ defmodule SlaonelyButSurely.MixProject do {:bandit, "~> 1.5"}, # Added dependencies - {:argon2_elixir, "~> 4.1"}, - {:timex, "~> 3.7"}, - {:typed_struct, "~> 0.3.0"}, {:boundary, "~> 0.10.4"}, # Added dev and/or test dependencies diff --git a/priv/repo/migrations/20250222164951_add_posts_table.exs b/priv/repo/migrations/20250222164951_add_posts_table.exs deleted file mode 100644 index 2ab8d7b..0000000 --- a/priv/repo/migrations/20250222164951_add_posts_table.exs +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Core.Repo.Migrations.AddPostsTable do - use Ecto.Migration - - def change do - create table(:posts, primary_key: false) do - add :id, :uuid, primary_key: true - add :title, :text - add :body, :text, null: false, default: "" - - timestamps() - end - end -end diff --git a/priv/repo/migrations/20250222201807_add_statuses_table.exs b/priv/repo/migrations/20250222201807_add_statuses_table.exs deleted file mode 100644 index 6b6d69a..0000000 --- a/priv/repo/migrations/20250222201807_add_statuses_table.exs +++ /dev/null @@ -1,14 +0,0 @@ -defmodule Core.Repo.Migrations.AddStatusesTable do - use Ecto.Migration - - def change do - create table(:statuses, primary_key: false) do - add :id, :uuid, primary_key: true - add :body, :text, null: false - - timestamps() - end - - create index(:statuses, [:inserted_at]) - end -end