notification display, general web ui polish around the whole place

This commit is contained in:
Jill 2023-01-18 21:03:03 +03:00
parent 12ae7504a0
commit c2eb375b7e
44 changed files with 124 additions and 28 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -21,7 +21,10 @@
accent-color: var(--accent-color);
--text-color: #111;
--text-color-dark: #444;
--text-color-darker: #555;
--background-color: #fff;
--background-color-2: #eee;
background-color: var(--background-color);
color: var(--text-color);
@ -45,17 +48,16 @@ a:hover {
}
.dim {
color: #444;
color: var(--text-color-dark);
}
@media (prefers-color-scheme: dark) {
:root {
--text-color: #fff;
--text-color-dark: #aaa;
--text-color-darker: #888;
--background-color: #111;
}
.dim {
color: #aaa;
--background-color-2: #161616;
}
}
@ -84,8 +86,8 @@ pre {
}
.dir-header {
background-color: rgba(150, 150, 150, 0.15);
color: #999;
background-color: var(--background-color-2);
color: var(--text-color-dark);
padding: 0.5em;
border-radius: 12px;
line-height: 1.2;

View File

@ -15,7 +15,7 @@
form {
margin: auto;
max-width: 700px;
background-color: rgba(150, 150, 150, 0.15);
background-color: var(--background-color-2);
padding: 1em;
border-radius: 1em;
line-height: 1.5;

View File

@ -11,7 +11,7 @@
.level {
width: 100%;
height: 4em;
background-color: rgba(0, 0, 0, 0.2);
background-color: var(--background-color-2);
border-radius: 2em;
padding: 1em;
display: flex;
@ -61,21 +61,12 @@
</head>
<body style="display: flex; flex-direction: column; align-items: center; gap: 1em">
<div class="levels">
<%- levels.each do |id, name, username, difficulty_community, difficulty_set, featured, epic| -%>
<%- levels.each do |id, name, username, difficulty_community, difficulty_set, demon_difficulty_int, featured, epic| -%>
<div class="level">
<%=
difficulties = StaticArray[
"auto",
"easy",
"normal",
"hard",
"harder",
"insane",
"demon-hard"
]
difficulty_int = difficulty_set || difficulty_community
difficulty_img = difficulty_int ? difficulties[difficulty_int] : "unrated"
"<img src='https://gdbrowser.com/assets/difficulties/#{difficulty_img + (epic ? "-epic" : (featured ? "-featured" : ""))}.png' class='level-img'>"
difficulty = (difficulty_set || difficulty_community).try { |n| LevelDifficulty.new(n) }
demon_difficulty = demon_difficulty_int.try { |n| DemonDifficulty.new(n) }
"<img src='#{Templates.get_difficulty_icon(difficulty, featured, epic, demon_difficulty)}' class='level-img'>"
%>
<div class="level-right">
<span class="line"><span class="name"><%= name %></span><span class="id dim">#<%= id %></span></span>

View File

@ -5,7 +5,7 @@
<link rel="icon" type="image/png" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/style.css" />
<title>Account Management</title>
<title>Notifications</title>
<style>
body {
max-width: 800px;
@ -28,6 +28,53 @@
gap: 0.5em;
align-items: center;
}
.notification {
margin: 0.5em;
border-radius: 20px;
height: 5em;
background-color: var(--background-color-2);
display: flex;
flex-direction: row;
}
.notification.unread {
outline: 1px solid var(--accent-color);
}
.notification > .notif-left, .notification > .notif-right {
padding: 1em;
}
.notif-left {
flex: 0 0 auto;
max-width: 100%;
width: 5em;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.notif-left > img, .notif-left > svg {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
.notif-right {
flex: 1 1 0px;
min-width: 0;
border-left: 3px solid var(--background-color);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.timestamp {
text-align: right;
color: var(--text-color-dark)
}
</style>
</head>
<body>
@ -44,5 +91,28 @@
File.read("public/assets/icons/bell.svg") %>
</a>
</div>
<%- notifications.each() do |notif| -%>
<div class="notification <%= notif[:read_at] == nil ? "unread" : "" %>">
<div class="notif-left">
<%=
case notif[:type]
when "authored_level_featured", "authored_level_rated"
difficulty = notif[:details]["difficulty"]?.as?(Int64).try { |n| LevelDifficulty.new(n.to_i32) }
"<img src='#{Templates.get_difficulty_icon(difficulty, notif[:type] == "authored_level_featured")}' class='notif-icon'>"
end
%>
</div>
<div class="notif-right">
<div>
<%= Notifications.format_notification(notif[:type], notif[:target], notif[:details], html_safe: true) %>
</div>
<div class="timestamp">
<time datetime="<%= notif[:created_at] %>Z">
<%= Format.fmt_value(Time.parse(notif[:created_at], Format::TIME_FORMAT, Time::Location::UTC)) %> ago
</time>
</div>
</div>
</div>
<%- end -%>
</body>
</html>

View File

@ -20,7 +20,7 @@ module CrystalGauntlet::Notifications
"authored_level_rated" => %(Your level <b>%{level_name}</b> has been rated!)
}
def format_notification(type : String, target : Int32, details : NotificationDetails? = nil, html_safe : Bool = false)
def format_notification(type : String, target : Int32?, details : NotificationDetails? = nil, html_safe : Bool = false)
details = details || {} of String => String | Int64 | Bool | Float64 | Nil
string = NOTIFICATION_STRINGS[type]
@ -30,7 +30,7 @@ module CrystalGauntlet::Notifications
#end
if html_safe
string % details.transform_values ->HTML.escape
string % details.transform_values { |v| HTML.escape v.to_s }
else
string % details
end

View File

@ -35,6 +35,29 @@ module CrystalGauntlet::Templates
return
end
end
DIFFICULTIES = StaticArray[
"auto",
"easy",
"normal",
"hard",
"harder",
"insane",
"demon"
]
DEMON_DIFFICULTIES = StaticArray[
"easy",
"medium",
"hard",
"insane",
"extreme"
]
def get_difficulty_icon(difficulty : LevelDifficulty?, featured : Bool = false, epic : Bool = false, demon_difficulty : DemonDifficulty? = DemonDifficulty::Hard)
"/assets/difficulties/#{DIFFICULTIES[difficulty.try &.to_i || -1]? || "na"}#{difficulty.try &.demon? ? "-#{DEMON_DIFFICULTIES[demon_difficulty.try &.to_i || -1]? || "hard"}" : ""}#{(featured && !epic) ? "-featured" : ""}#{epic ? "-epic" : ""}.png"
end
end
module CrystalGauntlet

View File

@ -8,6 +8,6 @@ CrystalGauntlet.template_endpoints["/tools/levels"] = ->(context : HTTP::Server:
context.response.content_type = "text/html"
page = (context.request.query_params["page"]? || "0").to_i? || 0
total_levels = DATABASE.scalar("select count(*) from levels").as(Int64)
levels = DATABASE.query_all("select levels.id, name, users.username, levels.community_difficulty, levels.difficulty, levels.featured, levels.epic from levels left join users on levels.user_id = users.id order by levels.id desc limit #{levels_per_page} offset #{page * levels_per_page}", as: {Int32, String, String, Int32?, Int32?, Bool, Bool})
levels = DATABASE.query_all("select levels.id, name, users.username, levels.community_difficulty, levels.difficulty, levels.demon_difficulty, levels.featured, levels.epic from levels left join users on levels.user_id = users.id order by levels.id desc limit #{levels_per_page} offset #{page * levels_per_page}", as: {Int32, String, String, Int32?, Int32?, Int32?, Bool, Bool})
ECR.embed("./public/template/levels.ecr", context.response)
}

View File

@ -13,8 +13,18 @@ CrystalGauntlet.template_endpoints["/accounts/notifications"] = ->(context : HTT
icon_value = [cube, ship, ball, ufo, wave, robot, spider][icon_type]
type_str = ["cube", "ship", "ball", "ufo", "wave", "robot", "spider"][icon_type]
notification_count = DATABASE.scalar("select count(*) from notifications where account_id = ? and read_at is null", account_id).as(Int64)
unread_notifications = notification_count > 0
notifications = DATABASE.query_all("select type, target, details, read_at, created_at from notifications where account_id = ? order by created_at desc", account_id, as: {String, Int32?, String, String?, String})
.map {|type, target, details, read_at, created_at| {
type: type,
target: target,
details: Notifications::NotificationDetails.from_json(details),
read_at: read_at,
created_at: created_at
} }
# mark all as read
DATABASE.exec("update notifications set read_at = ? where read_at is null and account_id = ?", Time.utc.to_s(Format::TIME_FORMAT), account_id)
unread_notifications = false
ECR.embed("./public/template/notifications.ecr", context.response)
}