starting to get public pages working
This commit is contained in:
parent
f8859e8070
commit
a7a270101d
11 changed files with 209 additions and 22 deletions
|
@ -31,6 +31,20 @@ defmodule Core.Posts do
|
||||||
|> Core.Repo.all()
|
|> Core.Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_published_recent_posts(kind) do
|
||||||
|
Post.Query.recent_posts(kind, @recent_posts_count)
|
||||||
|
|> Post.Query.published()
|
||||||
|
|> Core.Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_date(%Schema.Post{published_at: nil}), do: nil
|
||||||
|
|
||||||
|
def publish_date(%Schema.Post{published_at: published_at}) do
|
||||||
|
published_at
|
||||||
|
|> DateTime.shift_zone!("America/New_York")
|
||||||
|
|> DateTime.to_date()
|
||||||
|
end
|
||||||
|
|
||||||
def change_post(%Schema.Post{} = post \\ %Schema.Post{}, attrs) do
|
def change_post(%Schema.Post{} = post \\ %Schema.Post{}, attrs) do
|
||||||
Post.content_changeset(post, attrs)
|
Post.content_changeset(post, attrs)
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,6 +110,13 @@ defmodule Core.Posts.Post do
|
||||||
|> limit(^count)
|
|> limit(^count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recent_posts(query \\ base(), kind, count) do
|
||||||
|
query
|
||||||
|
|> where_kind(kind)
|
||||||
|
|> default_order()
|
||||||
|
|> limit(^count)
|
||||||
|
end
|
||||||
|
|
||||||
def where_publish_date_and_slug(query \\ current(), publish_date, slug) do
|
def where_publish_date_and_slug(query \\ current(), publish_date, slug) do
|
||||||
where(
|
where(
|
||||||
query,
|
query,
|
||||||
|
|
|
@ -112,4 +112,85 @@ defmodule Web.CoreComponents do
|
||||||
</button>
|
</button>
|
||||||
"""
|
"""
|
||||||
end
|
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
|
||||||
|
|
||||||
|
attr :id, :string, required: true
|
||||||
|
attr :posts, :any, required: true
|
||||||
|
|
||||||
|
slot :inner_block, required: true
|
||||||
|
|
||||||
|
def post_list(assigns) do
|
||||||
|
~H"""
|
||||||
|
<ol id={@id} phx-update={if is_struct(@posts, Phoenix.LiveView.LiveStream), do: "phx-update"}>
|
||||||
|
<li
|
||||||
|
:for={{dom_id, item} <- normalize_posts(@posts)}
|
||||||
|
id={dom_id}
|
||||||
|
class="flex flex-row justify-between"
|
||||||
|
>
|
||||||
|
<span>{render_slot(@inner_block, item)}</span>
|
||||||
|
<span>
|
||||||
|
<%= if item.deleted_at do %>
|
||||||
|
deleted
|
||||||
|
<% else %>
|
||||||
|
<%= if item.published_at do %>
|
||||||
|
<%= case item.kind do %>
|
||||||
|
<% :blog -> %>
|
||||||
|
<.timex value={item.published_at} format="{YYYY}-{0M}-{0D}" />
|
||||||
|
<% :status -> %>
|
||||||
|
<.timex value={item.published_at} format="{relative}" formatter={:relative} />
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
draft
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_posts(%Phoenix.LiveView.LiveStream{} = stream), do: stream
|
||||||
|
|
||||||
|
defp normalize_posts(posts) when is_list(posts),
|
||||||
|
do: Enum.with_index(posts, &{"#{&1.kind}-#{&2}", &1})
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
</.link>
|
</.link>
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="flex flex-row gap-x-2">
|
<ul class="flex flex-row gap-x-2">
|
||||||
<li><.link href="#">writing</.link></li>
|
<li><.link href={~p"/blog"}>writing</.link></li>
|
||||||
<li><.link href="#">microblog</.link></li>
|
<li><.link href={~p"/microblog"}>microblog</.link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -22,6 +22,12 @@ defmodule Web.PostController do
|
||||||
|> render_post(status)
|
|> render_post(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def index(%{assigns: %{kind: kind}} = conn, _params) when kind in ~w[blog status]a do
|
||||||
|
posts = Core.Posts.get_published_recent_posts(kind)
|
||||||
|
|
||||||
|
render(conn, :index, posts: posts)
|
||||||
|
end
|
||||||
|
|
||||||
defp render_post(conn, %Schema.Post{kind: :blog} = blog) do
|
defp render_post(conn, %Schema.Post{kind: :blog} = blog) do
|
||||||
render(conn, :show_blog, blog: blog)
|
render(conn, :show_blog, blog: blog)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,4 +2,10 @@ defmodule Web.PostHTML do
|
||||||
use Web, :html
|
use Web, :html
|
||||||
|
|
||||||
embed_templates "post_html/*"
|
embed_templates "post_html/*"
|
||||||
|
|
||||||
|
def blog_path(%Schema.Post{} = blog) do
|
||||||
|
if date = Core.Posts.publish_date(blog) do
|
||||||
|
~p"/blog/#{date.year}/#{date.month}/#{date.day}/#{blog.slug}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
8
lib/web/controllers/post_html/index.html.heex
Normal file
8
lib/web/controllers/post_html/index.html.heex
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<.post_list :let={post} id="recent-posts" posts={@posts}>
|
||||||
|
<%= case post.kind do %>
|
||||||
|
<% :blog -> %>
|
||||||
|
<.link navigate={blog_path(post)}>{post.title}</.link>
|
||||||
|
<% :status -> %>
|
||||||
|
{post.body}
|
||||||
|
<% end %>
|
||||||
|
</.post_list>
|
|
@ -23,7 +23,7 @@ defmodule Web.AdminDashboardLive do
|
||||||
<h2 class="font-bold text-xl">recent statuses</h2>
|
<h2 class="font-bold text-xl">recent statuses</h2>
|
||||||
<.link navigate={~p"/admin/posts/new?kind=status"}>new status</.link>
|
<.link navigate={~p"/admin/posts/new?kind=status"}>new status</.link>
|
||||||
</header>
|
</header>
|
||||||
<.post_list :let={status} id="recent-statuses" stream={@streams.statuses}>
|
<.post_list :let={status} id="recent-statuses" posts={@streams.statuses}>
|
||||||
<.link navigate={~p"/admin/posts/#{status}"}>{status.body}</.link>
|
<.link navigate={~p"/admin/posts/#{status}"}>{status.body}</.link>
|
||||||
</.post_list>
|
</.post_list>
|
||||||
</section>
|
</section>
|
||||||
|
@ -33,26 +33,11 @@ defmodule Web.AdminDashboardLive do
|
||||||
<h2 class="font-bold text-xl">recent blogs</h2>
|
<h2 class="font-bold text-xl">recent blogs</h2>
|
||||||
<.link navigate={~p"/admin/posts/new?kind=blog"}>new blog</.link>
|
<.link navigate={~p"/admin/posts/new?kind=blog"}>new blog</.link>
|
||||||
</header>
|
</header>
|
||||||
<.post_list :let={blog} id="recent-blogs" stream={@streams.blogs}>
|
<.post_list :let={blog} id="recent-blogs" posts={@streams.blogs}>
|
||||||
<.link navigate={~p"/admin/posts/#{blog}"}>{blog.title}</.link>
|
<.link navigate={~p"/admin/posts/#{blog}"}>{blog.title}</.link>
|
||||||
</.post_list>
|
</.post_list>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
attr :id, :string, required: true
|
|
||||||
attr :stream, :any, required: true
|
|
||||||
|
|
||||||
slot :inner_block, required: true
|
|
||||||
|
|
||||||
def post_list(assigns) do
|
|
||||||
~H"""
|
|
||||||
<ol id={@id} phx-update="stream">
|
|
||||||
<li :for={{dom_id, item} <- @stream} id={dom_id}>
|
|
||||||
{render_slot(@inner_block, item)}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,12 @@ defmodule Web.AdminPostLive do
|
||||||
post = Core.Posts.get!(post_id)
|
post = Core.Posts.get!(post_id)
|
||||||
form = Core.Posts.change_post(post, %{}) |> to_form()
|
form = Core.Posts.change_post(post, %{}) |> to_form()
|
||||||
|
|
||||||
socket = assign(socket, post: post, form: form)
|
socket =
|
||||||
|
assign(socket,
|
||||||
|
post: post,
|
||||||
|
form: form,
|
||||||
|
page_title: page_title(post, socket.assigns.live_action)
|
||||||
|
)
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
@ -19,7 +24,12 @@ defmodule Web.AdminPostLive do
|
||||||
post = %Schema.Post{kind: String.to_existing_atom(kind)}
|
post = %Schema.Post{kind: String.to_existing_atom(kind)}
|
||||||
form = Core.Posts.change_post(post, %{}) |> to_form()
|
form = Core.Posts.change_post(post, %{}) |> to_form()
|
||||||
|
|
||||||
socket = assign(socket, post: post, form: form)
|
socket =
|
||||||
|
assign(socket,
|
||||||
|
post: post,
|
||||||
|
form: form,
|
||||||
|
page_title: page_title(post, socket.assigns.live_action)
|
||||||
|
)
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
@ -54,6 +64,58 @@ defmodule Web.AdminPostLive do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("publish", _, %{assigns: %{post: post}} = socket) do
|
||||||
|
socket =
|
||||||
|
case Core.Posts.publish_post(post) do
|
||||||
|
{:ok, post} ->
|
||||||
|
assign(socket, post: post)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("unpublish", _, %{assigns: %{post: post}} = socket) do
|
||||||
|
socket =
|
||||||
|
case Core.Posts.unpublish_post(post) do
|
||||||
|
{:ok, post} ->
|
||||||
|
assign(socket, post: post)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("delete", _, %{assigns: %{post: post}} = socket) do
|
||||||
|
socket =
|
||||||
|
case Core.Posts.delete_post(post) do
|
||||||
|
{:ok, post} ->
|
||||||
|
assign(socket, post: post)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("undelete", _, %{assigns: %{post: post}} = socket) do
|
||||||
|
socket =
|
||||||
|
case Core.Posts.undelete_post(post) do
|
||||||
|
{:ok, post} ->
|
||||||
|
assign(socket, post: post)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<main>
|
<main>
|
||||||
|
@ -73,6 +135,21 @@ defmodule Web.AdminPostLive do
|
||||||
|
|
||||||
<.button type="submit">save</.button>
|
<.button type="submit">save</.button>
|
||||||
</.form>
|
</.form>
|
||||||
|
|
||||||
|
<%= if @live_action == :edit do %>
|
||||||
|
<div>
|
||||||
|
<%= if @post.published_at do %>
|
||||||
|
<.button phx-click="unpublish">unpublish</.button>
|
||||||
|
<% else %>
|
||||||
|
<.button phx-click="publish">publish</.button>
|
||||||
|
<% end %>
|
||||||
|
<%= if @post.deleted_at do %>
|
||||||
|
<.button phx-click="undelete">undelete</.button>
|
||||||
|
<% else %>
|
||||||
|
<.button phx-click="delete">delete</.button>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</main>
|
</main>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,7 +45,9 @@ defmodule Web.Router do
|
||||||
|
|
||||||
delete "/admin/users/log_out", UserSessionController, :delete
|
delete "/admin/users/log_out", UserSessionController, :delete
|
||||||
|
|
||||||
get "/:year/:month/:day/:slug", PostController, :show
|
get "/blog", PostController, :index, assigns: %{kind: :blog}
|
||||||
|
get "/blog/:year/:month/:day/:slug", PostController, :show
|
||||||
|
get "/microblog", PostController, :index, assigns: %{kind: :status}
|
||||||
get "/status/:status_id", PostController, :show
|
get "/status/:status_id", PostController, :show
|
||||||
|
|
||||||
# live_session :current_user, on_mount: [{Web.UserAuth, :mount_current_user}] do
|
# live_session :current_user, on_mount: [{Web.UserAuth, :mount_current_user}] do
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -60,6 +60,7 @@ defmodule SlaonelyButSurely.MixProject do
|
||||||
{:boundary, "~> 0.10.4"},
|
{:boundary, "~> 0.10.4"},
|
||||||
{:tzdata, "~> 1.1"},
|
{:tzdata, "~> 1.1"},
|
||||||
{:slugify, "~> 1.3"},
|
{:slugify, "~> 1.3"},
|
||||||
|
{:timex, "~> 3.7"},
|
||||||
|
|
||||||
# Added dev and/or test dependencies
|
# Added dev and/or test dependencies
|
||||||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||||
|
|
Loading…
Reference in a new issue