142 lines
4.8 kB
1
defmodule BlogWeb.RedditLinksLive do
2
use BlogWeb, :live_view
3
require Logger
4
5
@max_skeets 50
6
7
def mount(_params, _session, socket) do
8
# Load existing skeets from ETS
9
initial_skeets =
10
BlueskyHose.get_reddit_links(@max_skeets)
11
12
if connected?(socket) do
13
# Subscribe to the reddit links feed
14
Phoenix.PubSub.subscribe(Blog.PubSub, "reddit_links")
15
end
16
17
{:ok,
18
assign(socket,
19
page_title: "Reddit Links from Bluesky",
20
skeets: initial_skeets,
21
filtered_skeets: filter_nsfw_skeets(initial_skeets, true),
22
max_skeets: @max_skeets,
23
filter_nsfw: true,
24
meta_attrs: [
25
%{name: "description", content: "Bluesky posts containing Reddit links"},
26
%{property: "og:title", content: "Reddit Links from Bluesky"},
27
%{property: "og:description", content: "Bluesky posts containing Reddit links"},
28
%{property: "og:type", content: "website"}
29
]
30
)}
31
end
32
33
def handle_info({:reddit_link, skeet}, socket) do
34
# Add the new skeet to the list and trim to max size
35
updated_skeets =
36
[skeet | socket.assigns.skeets]
37
|> Enum.take(socket.assigns.max_skeets)
38
39
# Apply NSFW filter if enabled
40
filtered_skeets = filter_nsfw_skeets(updated_skeets, socket.assigns.filter_nsfw)
41
42
{:noreply, assign(socket, skeets: updated_skeets, filtered_skeets: filtered_skeets)}
43
end
44
45
def handle_event("toggle_nsfw_filter", _params, socket) do
46
# Toggle the filter state
47
filter_nsfw = !socket.assigns.filter_nsfw
48
49
# Apply the filter to the current skeets
50
filtered_skeets = filter_nsfw_skeets(socket.assigns.skeets, filter_nsfw)
51
52
{:noreply, assign(socket, filter_nsfw: filter_nsfw, filtered_skeets: filtered_skeets)}
53
end
54
55
# Filter out NSFW skeets if the filter is enabled
56
defp filter_nsfw_skeets(skeets, true) do
57
Enum.reject(skeets, fn skeet ->
58
is_nsfw?(skeet.original_text)
59
end)
60
end
61
62
# Return all skeets if the filter is disabled
63
defp filter_nsfw_skeets(skeets, false), do: skeets
64
65
# Check if a skeet contains NSFW indicators
66
defp is_nsfw?(text) when is_binary(text) do
67
nsfw_patterns = [
68
~r/\bNSFW\b/i,
69
~r/\bNSFL\b/i,
70
~r/\b18\+\b/,
71
~r/\bXXX\b/i
72
]
73
74
Enum.any?(nsfw_patterns, fn pattern ->
75
Regex.match?(pattern, text)
76
end)
77
end
78
79
defp is_nsfw?(_), do: false
80
81
def render(assigns) do
82
~H"""
83
<div class="max-w-4xl mx-auto py-8">
84
<h1 class="text-3xl font-bold mb-6">Reddit Links from Bluesky</h1>
85
86
<div class="mb-4 flex items-center">
87
<div class="form-control">
88
<label class="cursor-pointer flex items-center">
89
<input
90
type="checkbox"
91
checked={@filter_nsfw}
92
class="checkbox checkbox-primary mr-2"
93
phx-click="toggle_nsfw_filter"
94
/>
95
<span class="label-text text-gray-700">Filter NSFW content</span>
96
</label>
97
</div>
98
</div>
99
100
<div class="mb-8 p-4 bg-blue-50 rounded-lg border border-blue-200">
101
<p class="text-blue-800">
102
This page shows Bluesky posts that contain Reddit links. New posts will appear automatically.
103
</p>
104
</div>
105
106
<div class="space-y-6">
107
<%= if Enum.empty?(@filtered_skeets) do %>
108
<div class="p-8 text-center bg-gray-50 rounded-lg border border-gray-200">
109
<p class="text-gray-500">Waiting for posts with Reddit links to appear...</p>
110
<p class="text-sm text-gray-400 mt-2">
111
This could take some time depending on Bluesky activity.
112
</p>
113
</div>
114
<% end %>
115
116
<%= for skeet <- @filtered_skeets do %>
117
<div class="p-4 bg-white rounded-lg shadow-md border border-gray-200 transition-all hover:shadow-lg">
118
<div class="prose prose-sm max-w-none">
119
<div class="text-sm text-gray-600 mb-2 italic">
120
Original post: {String.slice(skeet.original_text, 0, 300)}{if String.length(
121
skeet.original_text
122
) > 300,
123
do: "...",
124
else: ""}
125
</div>
126
<ul class="list-disc pl-5">
127
<%= for link <- skeet.links do %>
128
<li class="mb-1">
129
<a href={link} class="text-blue-600 hover:underline break-all" target="_blank">
130
{link}
131
</a>
132
</li>
133
<% end %>
134
</ul>
135
</div>
136
</div>
137
<% end %>
138
</div>
139
</div>
140
"""
141
end
142
end
143