diff --git a/README.md b/README.md index 2011388..9284788 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ AlchemyPub is originally brought to you by [j4nk.dev](https://j4nk.dev). It is p - [x] Write tests - [x] Write documentation - [x] Publish on Github -- [ ] Live page add/remove support +- [x] Live page add/remove support - [ ] Legal stuff - [ ] Deployment - [ ] Keyboard navigation diff --git a/lib/alchemy_pub/engine.ex b/lib/alchemy_pub/engine.ex index a6e176c..ecb3868 100644 --- a/lib/alchemy_pub/engine.ex +++ b/lib/alchemy_pub/engine.ex @@ -97,6 +97,12 @@ defmodule AlchemyPub.Engine do |> Enum.sort(fn [_, a, b | _], [_, c, d | _] -> {a, b} >= {c, d} end) end + defp remove_post(path) do + title = path |> Path.basename() |> Path.rootname() |> urlify() + :ets.delete(@ets, title) + title + end + defp create_or_update_post(path) do {frontmatter, md} = case YamlFrontMatter.parse_file(path) do @@ -121,9 +127,10 @@ defmodule AlchemyPub.Engine do title = title(filename, h1, frontmatter) meta = - Map.put(frontmatter, "title", title) + frontmatter + |> Map.put("title", title) |> Map.put("date", date) - |> Map.update("tags", [], & &1) + |> Map.update("tags", [], fn t -> t || [] end) post = {urlify(filename), rank, date, meta, content} :ets.insert(@ets, post) @@ -146,11 +153,21 @@ defmodule AlchemyPub.Engine do end def handle_info( - {:file_event, _watcher_pid, {path, _events}}, + {:file_event, _watcher_pid, {path, events}}, state ) do - {title, _rank, _date, frontmatter, content} = create_or_update_post(path) - PubSub.broadcast(AlchemyPub.PubSub, "title:#{title}", {frontmatter, content}) + for e <- events |> Enum.filter(fn e -> e != :closed end) |> Enum.dedup() do + case e do + :moved_from -> + title = remove_post(path) + PubSub.broadcast(AlchemyPub.PubSub, "page_update", {:remove, title}) + + value when value in [:created, :moved_to, :modified] -> + title = create_or_update_post(path) |> elem(0) + PubSub.broadcast(AlchemyPub.PubSub, "page_update", {:add, title}) + end + end + {:noreply, state} end diff --git a/lib/alchemy_pub_web/page_live.ex b/lib/alchemy_pub_web/page_live.ex index de4122d..f12db28 100644 --- a/lib/alchemy_pub_web/page_live.ex +++ b/lib/alchemy_pub_web/page_live.ex @@ -77,27 +77,25 @@ defmodule AlchemyPubWeb.PageLive do end def mount(_params, _session, socket) do - socket = - socket - |> assign(post_title: "Loading") + if connected?(socket) do + PubSub.subscribe(AlchemyPub.PubSub, "page_update") + end - {:ok, socket} + {:ok, socket |> assign(post_title: "Loading")} end def handle_params(params, _url, socket) do - title = Map.get(socket.assigns, :title, nil) - - if title do - PubSub.unsubscribe(AlchemyPub.PubSub, "title:#{title}") - end + {:noreply, rebuild(socket, params)} + end + defp rebuild(socket, params) do all = Engine.find_sorted() {pages, posts} = all |> Enum.split_with(fn [_, rank | _] -> rank != nil end) pages = pages |> Enum.reverse() tags = for [_, _, _, meta] <- all do - Map.get(meta, "tags", []) + Map.get(meta, "tags") end |> List.flatten() |> Enum.map(fn t -> {Engine.urlify(t), t} end) @@ -131,12 +129,6 @@ defmodule AlchemyPubWeb.PageLive do socket = case page do {title, _rank, _date, meta, content} -> - sub = "title:#{title}" - - if connected?(socket) do - PubSub.subscribe(AlchemyPub.PubSub, sub) - end - socket |> assign( page_title: meta |> Map.get("title"), @@ -189,9 +181,7 @@ defmodule AlchemyPubWeb.PageLive do false -> {pages |> filter_robot, posts |> filter_robot} end - socket = socket |> assign(posts: posts, pages: pages, tags: tags) - - {:noreply, socket} + socket |> assign(posts: posts, pages: pages, tags: tags, params: params) end defp filter_hidden(pages) do @@ -205,7 +195,17 @@ defmodule AlchemyPubWeb.PageLive do end) end - def handle_info({_meta, content}, socket) do - {:noreply, socket |> assign(content: content)} + def handle_info({:add, _title}, socket) do + {:noreply, socket |> rebuild(socket.assigns.params)} + end + + def handle_info({:remove, title}, socket) do + socket = + cond do + title == socket.assigns.title -> push_patch(socket, to: "/") + true -> socket |> rebuild(socket.assigns.params) + end + + {:noreply, socket} end end