Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.

Commit 0d922d2

Browse files
committed
improve html page
1 parent 4278ae7 commit 0d922d2

File tree

4 files changed

+283
-32
lines changed

4 files changed

+283
-32
lines changed

.sqlx/query-4243e9da66d68fb7891b87614acf07d42f19dd3af5b46604049018c58ec9beec.json

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-ac5ff4d9b9767e3e46f4226641aa3f0fc1f99b2aa5c1b1d62f8736b13d48c05c.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/endpoints/image.rs

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub async fn get_raw(
4949
State(ApiState { database, .. }): State<ApiState>,
5050
Path(id): Path<Id>,
5151
) -> Result<Vec<u8>, ApiError> {
52-
let image = query!("SELECT player, filename, file, timestamp FROM images WHERE id = $1", id as _)
52+
let image = query!("SELECT file FROM images WHERE id = $1", id as _)
5353
.fetch_optional(&database)
5454
.await?
5555
.ok_or(StatusCode::NOT_FOUND)?;
@@ -91,43 +91,37 @@ pub async fn get_view(
9191
State(ApiState { database, .. }): State<ApiState>,
9292
Path(id): Path<Id>,
9393
) -> Result<Html<String>, ApiError> {
94-
let image = query!("SELECT player, filename, file, timestamp FROM images WHERE id = $1", id as _)
94+
let image = query!("SELECT filename, player, timestamp, file FROM images WHERE id = $1", id as _)
9595
.fetch_optional(&database)
9696
.await?
9797
.ok_or(StatusCode::NOT_FOUND)?;
9898

9999
let filename = String::from_utf8(image.filename).unwrap();
100100
let base_url = "https://api.axolotlclient.com/v1/";
101-
let image_url = base_url.to_string() + "image/" + &id.to_string() + "/";
102-
103-
Ok(Html(format!(
104-
r#"
105-
<html>
106-
<head>
107-
<title>{filename}</title>
108-
<link rel="alternate" type="application/json+oembed"
109-
href="{}oembed?format=json"
110-
title="{filename}" />
111-
<style>
112-
.title {{
113-
text-align: center;
114-
}}
115-
img {{
116-
width: 100%;
117-
max-height: 85%;
118-
}}
119-
</style>
120-
</head>
121-
<body>
122-
<div class="title">
123-
<h2>{filename}</h2>
124-
</div>
125-
<img src="{}raw">
126-
</body>
127-
</html>
128-
"#,
129-
&image_url, &image_url
130-
)))
101+
let image_url = base_url.to_string() + "image/" + &id.to_string();
102+
103+
let username = query!("SELECT username FROM players WHERE uuid = $1", image.player)
104+
.fetch_one(&database)
105+
.await?
106+
.username;
107+
108+
let time = image.timestamp.and_utc().format("%Y/%m/%d %H:%M").to_string();
109+
110+
Ok(Html(
111+
include_str!("image_view.html")
112+
.replace("{filename}", &filename)
113+
.replace("{image_data}", &("data:image/png;base64,".to_string() + &STANDARD_NO_PAD.encode(image.file)))
114+
.replace("{image_url}", &image_url)
115+
.replace("{username}", &username)
116+
.replace(
117+
"{time}",
118+
&image
119+
.timestamp
120+
.and_utc()
121+
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
122+
)
123+
.replace("{time_formatted}", &time),
124+
))
131125
}
132126

133127
#[derive(Serialize)]

src/endpoints/image_view.html

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<!doctype html>
2+
<html lang="en" data-theme="light">
3+
<head>
4+
<title>{filename} | AxolotlClient</title>
5+
<meta og:title="{filename}">
6+
<link rel="icon" href="https://axolotlclient.com/images/icon.png" />
7+
<link rel="alternate" type="application/json+oembed"
8+
href="{image_url}/oembed?format=json"
9+
title="{filename}" />
10+
<link href="https://fonts.bunny.net/css2?family=Fira+Sans&display=swap"
11+
rel="stylesheet"/>
12+
<meta charset="UTF-8" />
13+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
14+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
15+
<style>
16+
:root[data-theme="dark"] {
17+
--main-bg: #121212;
18+
--navbar-text: #ffffff;
19+
--main-text: #f2f2f2;
20+
--navbar-bg: #0000005e;
21+
--navbar-hover-bg: #ffffff90;
22+
--navbar-hover-text: #000;
23+
--about-text: var(--main-text);
24+
--switcher-color: invert(100%) sepia(2%) saturate(122%) hue-rotate(243deg)
25+
brightness(113%) contrast(90%);
26+
--bg-brightness: 0.5;
27+
}
28+
29+
:root[data-theme="light"] {
30+
--main-bg: #fff;
31+
--navbar-text: #303030;
32+
--navbar-bg: #f5f5f75e;
33+
--navbar-hover-bg: #00000090;
34+
--navbar-hover-text: #fff;
35+
--about-text: #2a2a2a;
36+
--switcher-color: invert(6%) sepia(1%) saturate(751%) hue-rotate(314deg)
37+
brightness(101%) contrast(71%);
38+
--bg-brightness: 0.8;
39+
}
40+
:root {
41+
--default-transition: color 0.3s ease-in-out,
42+
background-color 0.4s ease-in-out, border-color 0.3s ease-in-out,
43+
fill 0.3s ease-in-out, transform 0.3s ease-in-out;
44+
}
45+
46+
.navbar-left,
47+
.navbar-right {
48+
display: flex;
49+
align-items: center;
50+
justify-content: space-between;
51+
}
52+
53+
.navbar {
54+
transition: var(--default-transition);
55+
justify-content: space-between;
56+
display: flex;
57+
align-items: center;
58+
display: flex;
59+
align-items: center;
60+
backdrop-filter: blur(5px);
61+
z-index: 1;
62+
overflow: hidden;
63+
background-color: var(--navbar-bg);
64+
position: fixed;
65+
top: 0;
66+
width: 100%;
67+
left: 0;
68+
}
69+
70+
.navbar a {
71+
transition: 1s 5ms;
72+
float: left;
73+
display: block;
74+
color: var(--navbar-text);
75+
text-align: center;
76+
text-decoration: none;
77+
}
78+
79+
.navbar .text {
80+
padding: 20px;
81+
}
82+
83+
.navbar img {
84+
padding: 8px 10px 8px 10px;
85+
height: 40px;
86+
width: 40px;
87+
}
88+
89+
.navbar a:hover {
90+
background: var(--navbar-hover-bg);
91+
color: var(--navbar-hover-text);
92+
}
93+
#switcher {
94+
transition: 0.3s 5ms;
95+
filter: var(--switcher-color);
96+
float: right;
97+
width: 25px;
98+
margin: 2px;
99+
cursor: pointer;
100+
}
101+
.title {
102+
padding-top: 45px;
103+
text-align: center;
104+
color: var(--about-text);
105+
}
106+
.content {
107+
height: calc(100vh - 170px);
108+
max-width: 100%;
109+
padding: 0;
110+
margin: 0;
111+
}
112+
.content img {
113+
max-width: 100%;
114+
max-height: 100%;
115+
box-shadow: rgba(0, 0, 0, 0.5) 0 4px 8px;
116+
}
117+
body {
118+
overflow: hidden;
119+
background-color: var(--main-bg);
120+
height: 100%;
121+
font-family: "Fira Sans";
122+
text-align: center;
123+
}
124+
html {
125+
height: 100%;
126+
}
127+
.bg {
128+
background-image: url({image_data});
129+
backdrop-filter: blur(5px);
130+
filter: blur(8px) contrast(0.9) brightness(var(--bg-brightness));
131+
background-repeat: no-repeat;
132+
background-position: center;
133+
background-size: cover;
134+
position: fixed;
135+
top: 0;
136+
left: 0;
137+
height: 100%;
138+
min-width: 100%;
139+
z-index: -2;
140+
align-items: center;
141+
}
142+
</style>
143+
</head>
144+
<body>
145+
<div class="bg"></div>
146+
<div class="navbar">
147+
<div class="navbar-left">
148+
<a href="https://axolotlclient.com">
149+
<img src="https://axolotlclient.com/images/icon.png" alt="Logo"/>
150+
</a>
151+
<a class="text" href="https://modrinth.com/mod/axolotlclient">Modrinth</a>
152+
<a class="text" href="https://github.com/AxolotlClient/">GitHub</a>
153+
<a class="text" href="https://discord.gg/WyMjeX3vka">Discord</a>
154+
</div>
155+
<div class="navbar-right">
156+
<img src="https://axolotlclient.com/images/moon.svg" alt="" id="switcher" />
157+
</div>
158+
</div>
159+
<div class="title">
160+
<h2>{filename}</h2>
161+
<h3>Shared by {username} at <span id="upload_date">{time_formatted} (UTC)</span></h3>
162+
</div>
163+
<div class="content">
164+
<img src="{image_data}" alt="Screenshot {filename}" width="">
165+
</div>
166+
</body>
167+
<script>
168+
const rootElem = document.documentElement;
169+
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
170+
if (darkThemeMq.matches) {
171+
rootElem.setAttribute("data-theme", "dark");
172+
document.getElementById("switcher").src = "https://axolotlclient.com/images/sun.svg";
173+
} else {
174+
rootElem.setAttribute("data-theme", "light");
175+
document.getElementById("switcher").src = "https://axolotlclient.com/images/moon.svg";
176+
}
177+
178+
const switchTheme = () => {
179+
let dataTheme = rootElem.getAttribute("data-theme"),
180+
newtheme;
181+
newTheme = dataTheme === "dark" ? "light" : "dark";
182+
switchIcon = document.getElementById("switcher");
183+
rootElem.setAttribute("data-theme", newTheme);
184+
if (dataTheme === "dark") {
185+
switcher.src = "https://axolotlclient.com/images/moon.svg";
186+
} else {
187+
switcher.src = "https://axolotlclient.com/images/sun.svg";
188+
}
189+
};
190+
191+
document.querySelector("#switcher").addEventListener("click", switchTheme);
192+
193+
document.getElementById("upload_date").innerHTML = new Date("{time}").toLocaleString()
194+
</script>
195+
</html>

0 commit comments

Comments
 (0)