Skip to content

Commit 7ee19c7

Browse files
committed
Add Hackerspub integration for post fetching and display
- Introduce `HackerspubPostsBuilder` to fetch posts from Hackers.pub and store data. - Create layout `hackerspub_post.erb` for rendering individual posts. - Update navbar to include a link to Hackers.pub posts. - Create an index page to list and view posts from Hackers.pub.
1 parent b2f121f commit 7ee19c7

5 files changed

Lines changed: 175 additions & 0 deletions

File tree

bridgetown.config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ collections:
3535
future: true
3636
sort_by: date
3737
sort_direction: descending
38+
hackerspub_posts:
39+
output: true
40+
sort_by: date
41+
sort_direction: descending
3842

3943
feed:
4044
path: rss.xml
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
require_relative "../utils/hackerspub/client"
2+
3+
module Builders
4+
class HackerspubPostsBuilder < SiteBuilder
5+
def build
6+
return if ENV["BRIDGETOWN__DISABLE_BUILDERS"] == "true"
7+
return if ENV["HACKERSPUB_DISABLE"].to_s == "1"
8+
9+
handle = ENV.fetch("HACKERSPUB_HANDLE", "@kodingwarrior")
10+
endpoint = ENV.fetch(
11+
"HACKERSPUB_GRAPHQL_URL",
12+
Utils::Hackerspub::Client::DEFAULT_ENDPOINT
13+
)
14+
base_url = ENV.fetch("HACKERSPUB_BASE_URL", "https://hackers.pub")
15+
16+
client = Utils::Hackerspub::Client.new(
17+
handle:,
18+
endpoint:,
19+
base_url:,
20+
)
21+
22+
posts = client.fetch_posts
23+
actor = client.fetch_actor_bio
24+
25+
site.data["hackerspub_actor"] = actor if actor
26+
site.data["hackerspub_handle"] = handle
27+
28+
visible_posts = posts.select do |post|
29+
visibility = post[:visibility].to_s.downcase
30+
visibility.empty? || visibility == "public"
31+
end
32+
33+
unique_posts = visible_posts.uniq { |post| [post[:year], post[:encoded_slug] || post[:slug]] }
34+
35+
unique_posts.sort_by { |post| post[:published_at] || Time.at(0) }.each do |post|
36+
path_slug = post[:encoded_slug] || post[:slug]
37+
add_resource :hackerspub_posts, "#{post[:year]}/#{path_slug}.html" do
38+
layout "hackerspub_post"
39+
title post[:name].to_s.strip.empty? ? post[:slug] : post[:name]
40+
date post[:published_at] if post[:published_at]
41+
published post[:published_raw]
42+
year post[:year]
43+
slug post[:slug]
44+
encoded_slug post[:encoded_slug]
45+
language post[:language]
46+
summary post[:summary]
47+
original_url post[:url]
48+
visibility post[:visibility]
49+
content post[:content]
50+
permalink "/hackerspub/#{post[:year]}/#{path_slug}/"
51+
end
52+
end
53+
rescue Utils::Hackerspub::Error => e
54+
Bridgetown.logger.error("HackerspubPostsBuilder:", e.message)
55+
return
56+
rescue StandardError => e
57+
Bridgetown.logger.error(
58+
"HackerspubPostsBuilder:",
59+
"Unexpected error while building Hackerspub posts — #{e.class}: #{e.message}"
60+
)
61+
Bridgetown.logger.debug(e.backtrace.join("\n")) if Bridgetown.logger.debug?
62+
return
63+
end
64+
end
65+
end

src/_layouts/hackerspub_post.erb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
layout: default
3+
---
4+
5+
<% title = resource.data.title || resource.data.slug %>
6+
<% published_at = resource.data.date %>
7+
<% language = resource.data.language %>
8+
<% formatted_date = published_at&.strftime("%B %d, %Y") %>
9+
10+
<article class="">
11+
<h1 class="text-4xl font-bold pt-5 pb-3"><%= title %></h1>
12+
13+
<div class="flex items-center text-sm text-gray-600 pb-5 border-b mb-8">
14+
<% if formatted_date %>
15+
<span><%= formatted_date %></span>
16+
<% end %>
17+
<% if language %>
18+
<span class="mx-3 block font-bold text-slate-500">.</span>
19+
<span><%= language %></span>
20+
<% end %>
21+
</div>
22+
23+
<% if resource.content %>
24+
<div class="prose prose-lg max-w-none">
25+
<%= helpers.raw resource.content %>
26+
</div>
27+
<% end %>
28+
29+
<div class="mt-8 pt-6 border-t">
30+
<a
31+
href="<%= resource.data.original_url %>"
32+
target="_blank"
33+
rel="noopener noreferrer"
34+
class="inline-block bg-purple-600 hover:bg-purple-700 text-white font-semibold px-6 py-3 rounded-lg transition-colors"
35+
>
36+
hackers.pub에서 보기 ->
37+
</a>
38+
</div>
39+
</article>

src/_partials/_navbar.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
<li><a href="/about">/about</a></li>
66
<li><a href="/posts">/posts</a></li>
77
<li><a href="/wiki">/wiki</a></li>
8+
<li><a href="/hackerspub">/hackerspub</a></li>
89
</ul>
910
</nav>

src/hackerspub/index.erb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
layout: page
3+
title: Posts from Hackers' Pub
4+
permalink: /hackerspub/
5+
---
6+
7+
<% actor = site.data["hackerspub_actor"] %>
8+
<% posts = collections.hackerspub_posts.resources.sort_by { |resource| resource.data.date || Time.at(0) }.reverse %>
9+
10+
<% if actor %>
11+
<div class="bg-slate-100 border border-slate-200 rounded-lg p-5 mb-8 flex items-start gap-4">
12+
<% if actor["avatarUrl"] %>
13+
<img src="<%= actor["avatarUrl"] %>" alt="Hackers.pub avatar" class="w-16 h-16 rounded-full shadow" loading="lazy" />
14+
<% end %>
15+
<div>
16+
<h2 class="text-xl font-semibold mb-1"><%= actor["bio"] ? "About" : "Hackers.pub" %></h2>
17+
<% if actor["bio"] %>
18+
<p class="text-sm text-slate-600 whitespace-pre-line"><%= actor["bio"] %></p>
19+
<% elsif site.data["hackerspub_handle"] %>
20+
<p class="text-sm text-slate-600">
21+
Posts syndicated from <code><%= site.data["hackerspub_handle"] %></code>.
22+
</p>
23+
<% else %>
24+
<p class="text-sm text-slate-600">
25+
Posts syndicated from Hackers.pub.
26+
</p>
27+
<% end %>
28+
</div>
29+
</div>
30+
<% end %>
31+
32+
<% if posts.empty? %>
33+
<p>No Hackers.pub posts are available. Run the build with network access enabled to fetch articles.</p>
34+
<% else %>
35+
<ul class="space-y-6 list-none">
36+
<% posts.each do |post| %>
37+
<li class="border-b border-slate-200 pb-6 list-none">
38+
<h2 class="text-2xl font-semibold">
39+
<a href="<%= post.relative_url %>" class="hover:text-purple-600 transition-colors">
40+
<%= post.data.title %>
41+
</a>
42+
</h2>
43+
<div class="text-sm text-slate-500 mt-1 flex items-center gap-2">
44+
<% if post.data.date %>
45+
<span><%= post.data.date.strftime("%B %d, %Y") %></span>
46+
<% end %>
47+
<% if post.data.language %>
48+
<span aria-hidden="true"></span>
49+
<span><%= post.data.language %></span>
50+
<% end %>
51+
</div>
52+
<% if post.data.summary %>
53+
<p class="text-slate-600 mt-3"><%= post.data.summary %></p>
54+
<% end %>
55+
<a
56+
href="<%= post.data.original_url %>"
57+
class="inline-block text-purple-700 hover:text-purple-900 text-sm font-medium mt-3"
58+
target="_blank"
59+
rel="noopener noreferrer"
60+
>
61+
View on Hackers.pub ->
62+
</a>
63+
</li>
64+
<% end %>
65+
</ul>
66+
<% end %>

0 commit comments

Comments
 (0)