Add RSS subscribe links with icon across blog pages, footer, and head
- Add <.rss_icon> inline SVG component in core_components.ex - Inject RSS <link> tags into <head> on blog pages for auto-discovery - Show RSS icon next to blog title on index and tag pages - Show RSS icon on post pages next to back-link - Add Subscribe section in footer with links to both feeds - Wire feed URL through blog controller (index, show, tag actions) 174 tests pass, credo clean.
This commit is contained in:
parent
9238ecb7f3
commit
855b6cb590
@ -420,6 +420,31 @@ defmodule FirehoseWeb.CoreComponents do
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders an RSS feed icon (inline SVG).
|
||||
|
||||
## Examples
|
||||
|
||||
<.rss_icon class="size-5" />
|
||||
"""
|
||||
attr :class, :string, default: "size-4"
|
||||
attr :rest, :global
|
||||
|
||||
def rss_icon(assigns) do
|
||||
~H"""
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class={[@class]}
|
||||
{@rest}
|
||||
>
|
||||
<path d="M3.75 3.75v3c0 7.454 6.046 13.5 13.5 13.5h3v-5.25a.75.75 0 0 0-.75-.75h-3a.75.75 0 0 0-.75.75v1.5a8.25 8.25 0 0 1-8.25-8.25h1.5a.75.75 0 0 0 .75-.75v-3a.75.75 0 0 0-.75-.75h-5.25a.75.75 0 0 0-.75.75Z" />
|
||||
<circle cx="5.25" cy="18.75" r="2.25" />
|
||||
</svg>
|
||||
"""
|
||||
end
|
||||
|
||||
## JS Commands
|
||||
|
||||
def show(js \\ %JS{}, selector) do
|
||||
|
||||
@ -39,4 +39,30 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="border-t border-base-200 px-4 py-8 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-2xl space-y-3">
|
||||
<h3 class="text-sm font-semibold tracking-wide text-base-content/60 uppercase">Subscribe</h3>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<a
|
||||
href="/api/blog/engineering/feed.xml"
|
||||
class="inline-flex items-center gap-2 text-sm text-base-content/70 hover:text-primary transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<.rss_icon class="size-4 text-orange-500" />
|
||||
Engineering Blog
|
||||
</a>
|
||||
<a
|
||||
href="/api/blog/releases/feed.xml"
|
||||
class="inline-flex items-center gap-2 text-sm text-base-content/70 hover:text-primary transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<.rss_icon class="size-4 text-orange-500" />
|
||||
Release Notes
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<.flash_group flash={@flash} />
|
||||
|
||||
@ -41,6 +41,9 @@
|
||||
<% end %>
|
||||
<meta name="twitter:card" content={meta.twitter_card} />
|
||||
<% end %>
|
||||
<%= for feed <- get_content_for(assigns, :feed_links) do %>
|
||||
<link rel="alternate" type="application/rss+xml" title={feed.title} href={feed.rss_url} />
|
||||
<% end %>
|
||||
</head>
|
||||
<body>
|
||||
{@inner_content}
|
||||
|
||||
@ -9,9 +9,11 @@ defmodule FirehoseWeb.BlogController do
|
||||
result = blog.paginate(page)
|
||||
|
||||
meta = Blogex.SEO.meta_tags_for_blog(blog, FirehoseWeb.Endpoint.url())
|
||||
rss_url = rss_feed_url(blog)
|
||||
|
||||
conn
|
||||
|> FirehoseWeb.Layouts.put_content_for(:meta_tags, meta)
|
||||
|> FirehoseWeb.Layouts.put_content_for(:feed_links, %{rss_url: rss_url, title: blog.title()})
|
||||
|> render(:index,
|
||||
page_title: blog.title(),
|
||||
blog_title: blog.title(),
|
||||
@ -19,7 +21,8 @@ defmodule FirehoseWeb.BlogController do
|
||||
posts: result.entries,
|
||||
base_path: blog.base_path(),
|
||||
page: result.page,
|
||||
total_pages: result.total_pages
|
||||
total_pages: result.total_pages,
|
||||
rss_feed_url: rss_url
|
||||
)
|
||||
end
|
||||
|
||||
@ -27,31 +30,40 @@ defmodule FirehoseWeb.BlogController do
|
||||
blog = conn.assigns.blog
|
||||
post = blog.get_post!(slug)
|
||||
visibility = Blogex.Post.visibility(post)
|
||||
rss_url = rss_feed_url(blog)
|
||||
|
||||
meta = Blogex.SEO.meta_tags(post, FirehoseWeb.Endpoint.url(), blog)
|
||||
|
||||
conn
|
||||
|> FirehoseWeb.Layouts.put_content_for(:meta_tags, meta)
|
||||
|> FirehoseWeb.Layouts.put_content_for(:feed_links, %{rss_url: rss_url, title: blog.title()})
|
||||
|> render(:show,
|
||||
page_title: post.title,
|
||||
post: post,
|
||||
meta: meta,
|
||||
base_path: blog.base_path(),
|
||||
visibility: visibility,
|
||||
authenticated: !!(conn.assigns[:current_scope] && conn.assigns.current_scope.user)
|
||||
authenticated: !!(conn.assigns[:current_scope] && conn.assigns.current_scope.user),
|
||||
rss_feed_url: rss_url
|
||||
)
|
||||
end
|
||||
|
||||
def tag(conn, %{"tag" => tag}) do
|
||||
blog = conn.assigns.blog
|
||||
posts = blog.posts_by_tag(tag)
|
||||
rss_url = rss_feed_url(blog)
|
||||
|
||||
render(conn, :tag,
|
||||
rss_link = %{rss_url: rss_url, title: blog.title()}
|
||||
|
||||
conn
|
||||
|> FirehoseWeb.Layouts.put_content_for(:feed_links, rss_link)
|
||||
|> render(:tag,
|
||||
page_title: "#{blog.title()} — #{tag}",
|
||||
blog_title: blog.title(),
|
||||
tag: tag,
|
||||
posts: posts,
|
||||
base_path: blog.base_path()
|
||||
base_path: blog.base_path(),
|
||||
rss_feed_url: rss_url
|
||||
)
|
||||
end
|
||||
|
||||
@ -77,4 +89,9 @@ defmodule FirehoseWeb.BlogController do
|
||||
_ -> 1
|
||||
end
|
||||
end
|
||||
|
||||
defp rss_feed_url(blog) do
|
||||
base_id = blog.base_path() |> String.trim_leading("/blog/")
|
||||
FirehoseWeb.Endpoint.url() <> "/api/blog/#{base_id}/feed.xml"
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,17 @@
|
||||
<div class="space-y-8">
|
||||
<header>
|
||||
<div class="flex items-center gap-3">
|
||||
<h1 class="text-3xl font-bold font-display">{@blog_title}</h1>
|
||||
<a
|
||||
href={@rss_feed_url}
|
||||
class="link link-hover text-orange-500 hover:text-orange-600 transition-colors"
|
||||
title="Subscribe to {@blog_title} RSS feed"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<.rss_icon class="size-5" />
|
||||
</a>
|
||||
</div>
|
||||
<p :if={@blog_description} class="mt-2 text-base-content/70">{@blog_description}</p>
|
||||
</header>
|
||||
|
||||
|
||||
@ -1,5 +1,16 @@
|
||||
<div class="space-y-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<a href={@base_path} class="text-sm text-primary hover:underline">← Back to posts</a>
|
||||
<a
|
||||
href={@rss_feed_url}
|
||||
class="link link-hover text-orange-500 hover:text-orange-600 transition-colors"
|
||||
title="Subscribe to RSS feed"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<.rss_icon class="size-5" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<%= if @authenticated and @visibility == :draft do %>
|
||||
<div
|
||||
|
||||
@ -1,6 +1,17 @@
|
||||
<div class="space-y-8">
|
||||
<header>
|
||||
<div class="flex items-center gap-3">
|
||||
<h1 class="text-3xl font-bold font-display">{@blog_title}</h1>
|
||||
<a
|
||||
href={@rss_feed_url}
|
||||
class="link link-hover text-orange-500 hover:text-orange-600 transition-colors"
|
||||
title="Subscribe to {@blog_title} RSS feed"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<.rss_icon class="size-5" />
|
||||
</a>
|
||||
</div>
|
||||
<p class="mt-2 text-base-content/70">Posts tagged "{@tag}"</p>
|
||||
</header>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user