160 lines
4.7 kB
1
defmodule BlogWeb.MirrorLive do
2
use BlogWeb, :live_view
3
require Logger
4
5
@source_url "https://raw.githubusercontent.com/notactuallytreyanastasio/blog/main/lib/blog_web/live/mirror_live.ex"
6
7
def mount(_params, _session, socket) do
8
if connected?(socket) do
9
# Fetch source code asynchronously when client connects
10
Task.async(fn -> fetch_source_code() end)
11
end
12
13
{:ok,
14
assign(socket,
15
source_code: "Loading source code...",
16
page_title: "I am looking at myself",
17
meta_attrs: [
18
%{name: "title", content: "Mirror Mirror on the wall"},
19
%{name: "description", content: "A page that shows its own source code"},
20
%{property: "og:title", content: "Mirror Mirror on the wall"},
21
%{property: "og:description", content: "A page that shows its own source code"},
22
%{property: "og:type", content: "website"}
23
]
24
)}
25
end
26
27
def handle_info({ref, source_code}, socket) when is_reference(ref) do
28
# Flush the DOWN message
29
Process.demonitor(ref, [:flush])
30
31
# Process the source code
32
characters = process_source_code(source_code)
33
34
{:noreply, assign(socket, lines: characters)}
35
end
36
37
def handle_info({:DOWN, _, :process, _, reason}, socket) do
38
Logger.error("Failed to fetch source code: #{inspect(reason)}")
39
40
# Create a fallback source code
41
fallback = fallback_source_code()
42
characters = process_source_code(fallback)
43
44
{:noreply, assign(socket, lines: characters)}
45
end
46
47
defp process_source_code(source) do
48
# Handle both string and error tuple cases
49
source_str = case source do
50
{:error, reason} ->
51
Logger.error("Error decompiling source: #{inspect(reason)}")
52
fallback_source_code()
53
str when is_binary(str) ->
54
str
55
other ->
56
Logger.error("Unexpected source format: #{inspect(other)}")
57
fallback_source_code()
58
end
59
60
# Split into lines first, then characters
61
source_str
62
|> String.split("\n")
63
|> Enum.map(fn line ->
64
line
65
|> String.graphemes()
66
|> Enum.map(fn char ->
67
%{
68
char: char,
69
duration: :rand.uniform(10) + 5,
70
delay: :rand.uniform(5000),
71
direction: if(:rand.uniform() > 0.5, do: 1, else: -1)
72
}
73
end)
74
end)
75
end
76
77
defp fallback_source_code do
78
"""
79
defmodule BlogWeb.MirrorLive do
80
use BlogWeb, :live_view
81
82
# Source code could not be loaded
83
# This is a fallback representation
84
85
def render(assigns) do
86
~H\"\"\"
87
<div>
88
<h1>Mirror Mirror on the wall</h1>
89
<p>Source code could not be loaded</p>
90
</div>
91
\"\"\"
92
end
93
end
94
"""
95
end
96
97
defp fetch_source_code do
98
try do
99
# Try to decompile first
100
result = CodeDecompiler.decompile_to_string(__MODULE__)
101
102
# If result is an error tuple, try fetching from GitHub
103
case result do
104
{:error, _} ->
105
fetch_from_github()
106
_ ->
107
result
108
end
109
rescue
110
e ->
111
Logger.error("Exception decompiling: #{inspect(e)}")
112
fetch_from_github()
113
end
114
end
115
116
defp fetch_from_github do
117
try do
118
case Req.get(@source_url) do
119
{:ok, %{status: 200, body: body}} ->
120
body
121
error ->
122
Logger.error("Failed to fetch from GitHub: #{inspect(error)}")
123
fallback_source_code()
124
end
125
rescue
126
e ->
127
Logger.error("Exception fetching from GitHub: #{inspect(e)}")
128
fallback_source_code()
129
end
130
end
131
132
def render(assigns) do
133
~H"""
134
<div class="min-h-screen bg-gray-900 text-white p-8">
135
<div class="max-w-4xl mx-auto">
136
<h1 class="text-3xl font-bold mb-8">
137
Mirror Mirror on the wall, who's the most meta of them all?
138
</h1>
139
<div class="bg-gray-800 rounded-lg p-6 shadow-lg">
140
<pre class="text-sm font-mono" style="tab-size: 2;"><code class="language-elixir"><%= if assigns[:lines] do %><%= for line <- @lines do %><%= for char <- line do %><span style={"display: inline-block; animation: spin#{char.duration} #{char.duration}s linear #{char.delay}ms infinite;"}><%= char.char %></span><% end %>
141
<% end %><% else %>Loading source code...<% end %></code></pre>
142
</div>
143
</div>
144
</div>
145
146
<style>
147
<%= for duration <- 5..15 do %>
148
@keyframes spin<%= duration %> {
149
from { transform: rotate(0deg); }
150
to { transform: rotate(<%= if rem(duration, 2) == 0, do: "360", else: "-360" %>deg); }
151
}
152
<% end %>
153
</style>
154
"""
155
end
156
157
@doc """
158
Source code available at: #{@source_url}
159
"""
160
end
161