Merge branch 'master' into master2

This commit is contained in:
edshot99 2025-02-05 16:54:44 -06:00
commit 77b01aa5f8
47 changed files with 1304 additions and 572 deletions

View File

@ -24,7 +24,7 @@ module ApplicationHelper
tag.li(id: id, class: klass) do tag.li(id: id, class: klass) do
link_to(url, id: "#{id}-link", **options) do link_to(url, id: "#{id}-link", **options) do
concat tag.i(class: icon) concat svg_icon(icon)
concat " " concat " "
concat tag.span(text) concat tag.span(text)
end end
@ -189,6 +189,21 @@ module ApplicationHelper
end end
end end
def simple_avatar(user, **options)
return "" if user.nil?
post_id = user.avatar_id
deferred_post_ids.add(post_id) if post_id
klass = options.delete(:class)
named = options.delete(:named)
tag.a href: user_path(user), class: "simple-avatar #{klass}", data: { id: post_id, name: user.name } do
tag.span(class: "simple-avatar-button") do
concat tag.span(user.pretty_name, class: "simple-avatar-name") if named
concat tag.span(class: "simple-avatar-image", data: { name: user.name[0].capitalize })
end
end
end
def user_banner(user) def user_banner(user)
return "" if user.nil? return "" if user.nil?
post_id = user.banner_id post_id = user.banner_id

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
module IconHelper
# https://lucide.dev/
PATHS = {
# Navigation
hamburger: %(<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>),
brush: %(<path d="m9.06 11.9 8.07-8.06a2.85 2.85 0 1 1 4.03 4.03l-8.06 8.08"/><path d="M7.07 14.94c-1.66 0-3 1.35-3 3.02 0 1.33-2.5 1.52-2 2.02 1.08 1.1 2.49 2.02 4 2.02 2.2 0 4-1.8 4-4.04a3.01 3.01 0 0 0-3-3.02z"/>),
images: %(<path d="M18 22H4a2 2 0 0 1-2-2V6"/><path d="m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18"/><circle cx="12" cy="8" r="2"/><rect width="16" height="16" x="6" y="2" rx="2"/>),
library: %(<path d="m16 6 4 14"/><path d="M12 6v14"/><path d="M8 8v12"/><path d="M4 4v16"/>),
group: %(<path d="M3 7V5c0-1.1.9-2 2-2h2"/><path d="M17 3h2c1.1 0 2 .9 2 2v2"/><path d="M21 17v2c0 1.1-.9 2-2 2h-2"/><path d="M7 21H5c-1.1 0-2-.9-2-2v-2"/><rect width="7" height="5" x="7" y="7" rx="1"/><rect width="7" height="5" x="10" y="12" rx="1"/>),
tags: %(<path d="m15 5 6.3 6.3a2.4 2.4 0 0 1 0 3.4L17 19"/><path d="M9.586 5.586A2 2 0 0 0 8.172 5H3a1 1 0 0 0-1 1v5.172a2 2 0 0 0 .586 1.414L8.29 18.29a2.426 2.426 0 0 0 3.42 0l3.58-3.58a2.426 2.426 0 0 0 0-3.42z"/><circle cx="6.5" cy="9.5" r=".5" fill="currentColor"/>),
megaphone: %(<path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/>),
message_square: %(<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>),
lectern: %(<path d="M16 12h3a2 2 0 0 0 1.902-1.38l1.056-3.333A1 1 0 0 0 21 6H3a1 1 0 0 0-.958 1.287l1.056 3.334A2 2 0 0 0 5 12h3"/><path d="M18 6V3a1 1 0 0 0-1-1h-3"/><rect width="8" height="12" x="8" y="10" rx="1"/>),
swatch: %(<path d="M11 17a4 4 0 0 1-8 0V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2Z"/><path d="M16.7 13H19a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H7"/><path d="M 7 17h.01"/><path d="m11 8 2.3-2.3a2.4 2.4 0 0 1 3.404.004L18.6 7.6a2.4 2.4 0 0 1 .026 3.434L9.9 19.8"/>),
settings: %(<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>),
log_in: %(<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" x2="3" y1="12" y2="12"/>),
# Pagination
chevron_left: %(<path d="m15 18-6-6 6-6"/>),
chevron_right: %(<path d="m9 18 6-6-6-6"/>),
ellipsis: %(<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>),
# Posts
fullscreen: %(<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><rect width="10" height="8" x="7" y="8" rx="1"/>),
}.freeze
def svg_icon(name, *args)
options = args.extract_options!
width = options[:width] || 24
height = options[:height] || 24
tag.svg(
"xmlns": "http://www.w3.org/2000/svg",
"width": width,
"height": height,
"viewbox": "0 0 24 24",
"fill": "none",
"stroke": "currentColor",
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-linejoin": "round",
) do
raw(PATHS[name]) # rubocop:disable Rails/OutputSafety
end
end
end

View File

@ -2,17 +2,23 @@
module PaginationHelper module PaginationHelper
def sequential_paginator(records) def sequential_paginator(records)
with_paginator_wrapper do tag.div(class: "paginator") do
return "" if records.try(:none?) return "" if records.try(:none?)
html = "".html_safe html = "".html_safe
unless records.is_first_page?
html << tag.li(link_to("< Previous", nav_params_for("a#{records[0].id}"), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" })) # Previous
html << link_to(records.is_first_page? ? "#" : nav_params_for("a#{records[0].id}"), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: records.is_first_page? }) do
concat svg_icon(:chevron_left)
concat tag.span("Prev")
end end
unless records.is_last_page? # Next
html << tag.li(link_to("Next >", nav_params_for("b#{records[-1].id}"), rel: "next", id: "paginator-next", data: { shortcut: "d right" })) html << link_to(records.is_last_page? ? "#" : nav_params_for("b#{records[-1].id}"), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: records.is_last_page? }) do
concat tag.span("Next")
concat svg_icon(:chevron_right)
end end
html html
end end
end end
@ -22,64 +28,73 @@ module PaginationHelper
return sequential_paginator(records) return sequential_paginator(records)
end end
with_paginator_wrapper do tag.div(class: "paginator", data: { total: [records.total_pages, records.max_numbered_pages].min, current: records.current_page }) do
html = "".html_safe html = "".html_safe
icon_left = tag.i(class: "fa-solid fa-chevron-left")
if records.current_page >= 2 # Previous
html << tag.li(class: "arrow") { link_to(icon_left, nav_params_for(records.current_page - 1), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" }) } has_prev = records.current_page < 2
else html << link_to(has_prev ? "#" : nav_params_for(records.current_page - 1), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: has_prev }) do
html << tag.li(class: "arrow") { tag.span(icon_left) } concat svg_icon(:chevron_left)
concat tag.span("Prev")
end end
paginator_pages(records).each do |page| # Break
html << numbered_paginator_item(page, records) html << tag.div(class: "break")
# Numbered
paginator_pages(records).each do |page, klass|
html << numbered_paginator_item(page, klass, records)
end end
icon_right = tag.i(class: "fa-solid fa-chevron-right") # Next
if records.current_page < records.total_pages has_next = records.current_page >= records.total_pages
html << tag.li(class: "arrow") { link_to(icon_right, nav_params_for(records.current_page + 1), rel: "next", id: "paginator-next", data: { shortcut: "d right" }) } html << link_to(has_next ? "#" : nav_params_for(records.current_page + 1), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: has_next }) do
else concat tag.span("Next")
html << tag.li(class: "arrow") { tag.span(icon_right) } concat svg_icon(:chevron_right)
end end
html html
end end
end end
private private
def with_paginator_wrapper(&)
tag.div(class: "paginator") do
tag.menu(&)
end
end
def paginator_pages(records) def paginator_pages(records)
window = 4 small_window = 2
large_window = 4
last_page = [records.total_pages, records.max_numbered_pages].min last_page = [records.total_pages, records.max_numbered_pages].min
left = [2, records.current_page - window].max left_sm = [2, records.current_page - small_window].max
right = [records.current_page + window, last_page - 1].min left_lg = [2, records.current_page - large_window].max
right_sm = [records.current_page + small_window, last_page - 1].min
right_lg = [records.current_page + large_window, last_page - 1].min
small_range = left_sm..right_sm
[ result = [
1, [1, "first"],
("..." unless left == 2), ]
(left..right).to_a, result.push([0, "spacer"]) unless left_lg == 2
("..." unless right == last_page - 1), (left_lg..right_lg).each do |page|
(last_page unless last_page <= 1), result.push([page, small_range.member?(page) ? "sm" : "lg"])
].flatten.compact end
result.push([0, "spacer"]) unless right_lg == last_page - 1
result.push([last_page, "last"]) unless last_page <= 1
result
end end
def numbered_paginator_item(page, records) def numbered_paginator_item(page, klass, records)
return "" if page.to_i > records.max_numbered_pages return "" if page.to_i > records.max_numbered_pages
html = "".html_safe html = "".html_safe
if page == "..." if page == 0
html << tag.li(class: "more") { tag.i(class: "fa-solid fa-ellipsis") } html << link_to(svg_icon(:ellipsis), nav_params_for(0), class: "spacer")
elsif page == records.current_page elsif page == records.current_page
html << tag.li(class: "current-page") { tag.span(page) } html << tag.span(page, class: "page current")
else else
html << tag.li(class: "numbered-page") { link_to(page, nav_params_for(page)) } html << link_to(page, nav_params_for(page), class: "page #{klass}")
end end
html html
end end

View File

@ -0,0 +1,43 @@
import Page from "./utility/page";
const Home = {};
Home.init = function () {
const $form = $("#home-search-form");
const $tags = $("#tags");
let isEmpty = !$tags.val();
let wasEmpty = isEmpty;
if (isEmpty) $form.addClass("empty");
$tags.on("input", () => {
wasEmpty = isEmpty;
isEmpty = !$tags.val();
if (isEmpty && !wasEmpty) $form.addClass("empty");
else if (!isEmpty && wasEmpty) $form.removeClass("empty");
});
$(".home-buttons a").on("click", (event) => {
if (isEmpty) return; // Act like regular links
event.preventDefault();
const extraTags = $(event.currentTarget).attr("tags");
if (extraTags) {
$tags.val((index, value) => {
return value + " " + extraTags;
});
}
$form.trigger("submit");
return false;
});
};
$(() => {
if (!Page.matches("static", "home")) return;
Home.init();
});
export default Home;

View File

@ -2,7 +2,7 @@ const Navigation = {};
Navigation.init = function () { Navigation.init = function () {
const wrapper = $("html"); const wrapper = $("html");
$("#nav-toggle, .nav-offset-left, .nav-offset-bottom").on("click", (event) => { $("#nav-toggle, .nav-offset-left, .nav-offset-bott").on("click", (event) => {
event.preventDefault(); event.preventDefault();
wrapper.toggleClass("nav-toggled"); wrapper.toggleClass("nav-toggled");

View File

@ -10,7 +10,6 @@ NewsUpdate.initialize = function () {
let newsOpen = false; let newsOpen = false;
$("#news-header, #news-show").on("click", (event) => { $("#news-header, #news-show").on("click", (event) => {
event.preventDefault(); event.preventDefault();
console.log("click");
newsOpen = !newsOpen; newsOpen = !newsOpen;
$("#news").toggleClass("open", newsOpen); $("#news").toggleClass("open", newsOpen);

View File

@ -0,0 +1,19 @@
const Paginator = {};
Paginator.init_fasttravel = function (button) {
button.on("click", (event) => {
event.preventDefault();
const value = prompt("Navigate to page");
if (!value) return;
window.location.replace(button.attr("href").replace("page=0", "page=" + value));
});
};
$(() => {
for (const one of $(".paginator a.spacer").get())
Paginator.init_fasttravel($(one));
});
export default Paginator;

View File

@ -35,12 +35,10 @@ PostModeMenu.change_tag_script = function (e) {
e.preventDefault(); e.preventDefault();
const newScriptID = Number(e.key); const newScriptID = Number(e.key);
console.log(newScriptID, LStorage.Posts.TagScript.ID);
if (!newScriptID || newScriptID == LStorage.Posts.TagScript.ID) if (!newScriptID || newScriptID == LStorage.Posts.TagScript.ID)
return; return;
LStorage.Posts.TagScript.ID = newScriptID; LStorage.Posts.TagScript.ID = newScriptID;
console.log("settings", LStorage.Posts.TagScript.ID, LStorage.Posts.TagScript.Content);
$("#tag-script-field").val(LStorage.Posts.TagScript.Content); $("#tag-script-field").val(LStorage.Posts.TagScript.Content);
PostModeMenu.show_notice(newScriptID); PostModeMenu.show_notice(newScriptID);
}; };

View File

@ -1,4 +1,5 @@
import LStorage from "./utility/storage"; import LStorage from "./utility/storage";
import Page from "./utility/page";
const PostSearch = {}; const PostSearch = {};
@ -10,6 +11,8 @@ PostSearch.init = function () {
$(".wiki-excerpt").each((index, element) => { $(".wiki-excerpt").each((index, element) => {
PostSearch.initialize_wiki_preview($(element)); PostSearch.initialize_wiki_preview($(element));
}); });
PostSearch.initialize_controls();
}; };
PostSearch.initialize_input = function ($form) { PostSearch.initialize_input = function ($form) {
@ -39,21 +42,35 @@ PostSearch.initialize_input = function ($form) {
PostSearch.initialize_wiki_preview = function ($preview) { PostSearch.initialize_wiki_preview = function ($preview) {
let visible = LStorage.Posts.WikiExcerpt; let visible = LStorage.Posts.WikiExcerpt;
if (visible) if (visible) $preview.addClass("open");
$preview.removeClass("hidden"); window.setTimeout(() => { // Disable the rollout on first load
$preview.removeClass("loading");
}, 250);
$($preview.find("a.wiki-excerpt-toggle")).on("click", (event) => { $($preview.find("h3.wiki-excerpt-toggle")).on("click", (event) => {
event.preventDefault(); event.preventDefault();
visible = !visible; visible = !visible;
$preview.toggleClass("hidden", !visible); $preview.toggleClass("open", visible);
LStorage.Posts.WikiExcerpt = visible; LStorage.Posts.WikiExcerpt = visible;
return false; return false;
}); });
}; };
PostSearch.initialize_controls = function () {
let fullscreen = LStorage.Posts.Fullscreen;
$("#search-fullscreen").on("click", () => {
fullscreen = !fullscreen;
$("body").attr("data-st-fullscreen", fullscreen);
LStorage.Posts.Fullscreen = fullscreen;
});
};
$(() => { $(() => {
if (!Page.matches("posts", "index") && !Page.matches("favorites"))
return;
PostSearch.init(); PostSearch.init();
}); });

View File

@ -28,6 +28,7 @@ Shortcuts.initialize_data_shortcuts = function () {
Shortcuts.keydown(keys, namespace, event => { Shortcuts.keydown(keys, namespace, event => {
const e = $(`[data-shortcut="${keys}"]`).get(0); const e = $(`[data-shortcut="${keys}"]`).get(0);
if ($e.data("disabled")) return;
if ($e.is("input, textarea")) { if ($e.is("input, textarea")) {
$e.trigger("focus").selectEnd(); $e.trigger("focus").selectEnd();
} else { } else {

View File

@ -3,7 +3,7 @@ import LStorage from "./utility/storage";
const Theme = {}; const Theme = {};
Theme.Values = ["Main", "Extra", "StickyHeader", "Palette", "Navbar", "Gestures"]; Theme.Values = ["Main", "Extra", "StickyHeader", "ForumNotif", "Palette", "Navbar", "Gestures"];
for (const one of Theme.Values) { for (const one of Theme.Values) {
Object.defineProperty(Theme, one, { Object.defineProperty(Theme, one, {

View File

@ -8,6 +8,23 @@ Thumbnails.initialize = function () {
const posts = $(".post-thumb.placeholder, .thumb-placeholder-link"); const posts = $(".post-thumb.placeholder, .thumb-placeholder-link");
const replacedPosts = []; const replacedPosts = [];
// Avatar special case
for (const post of $(".simple-avatar")) {
const $post = $(post);
const postID = $post.data("id");
if (!postID) continue;
const postData = postsData[postID];
if (!postData || !postData["preview_url"]) continue;
$("<img>")
.attr("src", postData["preview_url"])
.appendTo($post.find("span.simple-avatar-image"));
continue;
}
// Reset of the deferred posts
for (const post of posts) { for (const post of posts) {
const $post = $(post); const $post = $(post);

View File

@ -65,6 +65,9 @@ LStorage.Theme = {
/** @returns {boolean} True if the sticky header is enabled */ /** @returns {boolean} True if the sticky header is enabled */
StickyHeader: ["theme-sheader", false], StickyHeader: ["theme-sheader", false],
/** @returns {boolean} True if the forum notification dot is enabled */
ForumNotif: ["theme-forumnotif", false],
}; };
StorageUtils.bootstrapMany(LStorage.Theme); StorageUtils.bootstrapMany(LStorage.Theme);
@ -85,6 +88,9 @@ LStorage.Posts = {
/** @returns {boolean} True if the wiki excerpt should be visible */ /** @returns {boolean} True if the wiki excerpt should be visible */
WikiExcerpt: ["e6.posts.wiki", true], WikiExcerpt: ["e6.posts.wiki", true],
/** @returns {boolean} True if the search should be displayed in fullscreen */
Fullscreen: ["e6.posts.fusk", false],
}; };
StorageUtils.bootstrapMany(LStorage.Posts); StorageUtils.bootstrapMany(LStorage.Posts);

View File

@ -9,6 +9,9 @@
@import "base/links"; @import "base/links";
@import "base/fontawesome"; @import "base/fontawesome";
@import "common/standard_variables";
@import "common/standard_elements";
@import "common/footer"; @import "common/footer";
@import "common/helper_classes"; @import "common/helper_classes";
@import "common/helper_palette"; @import "common/helper_palette";

View File

@ -1,73 +1,84 @@
footer#page-footer { footer.footer-wrapper {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 0.5rem 0;
padding: 0.5rem 0 1rem;
margin: 1rem 0 0;
background: var(--color-foreground); background: var(--color-foreground);
margin-top: 1rem;
.footer-grid {
display: grid;
grid-template-columns: min-content min-content;
grid-template-areas:
"logo . "
"left right";
justify-items: center;
gap: 0.5rem 0;
width: min-content;
margin: 0 auto;
padding: 1rem;
}
.footer-logo {
grid-area: logo;
width: 100%;
text-align: right;
img {
width: 5rem;
height: auto;
// Aligning the logo with the line below
margin-right: -3.25rem;
background: themed("color-background") themed("image-background");
border-radius: 50%;
padding: 0.5rem;
}
}
.footer-left, .footer-right { .footer-left, .footer-right {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
gap: 0.25em; box-sizing: border-box;
padding: 0 0.5rem; width: 100%;
gap: 0.25rem;
font-size: 1.25em;
font-size: 1rem;
a, span {
line-height: 1.25em; a, span { white-space: nowrap; }
} span { line-height: 1.25em; }
} }
.footer-left { .footer-left {
align-items: end; grid-area: left;
border-right: 1px solid var(--color-section); padding-right: 1rem;
text-align: right;
border-right: 1px solid var(--color-section-lighten-5);
} }
.footer-right {
.footer-logo { grid-area: right;
grid-column: 1 / -1; padding-left: 1rem;
grid-row: 1;
text-align: center;
img {
width: 5rem;
height: 5rem;
}
} }
} }
// Desktop // Desktop-ish
footer#page-footer { footer.footer-wrapper {
@include window-larger-than(800px) { @include window-larger-than(450px) {
grid-template-columns: 1fr min-content 1fr; .footer-grid {
grid-template-areas: "logo left right";
.footer-left, .footer-right { padding: 1rem 0;
font-size: unset;
flex-flow: row;
align-items: center;
border: 0;
gap: 0;
a:not(:last-child)::after {
content: "";
width: 4px;
height: 4px;
background: white;
display: inline-block;
border-radius: 2px;
margin: 0.125rem 0.5rem;
}
span.footer-running { display: none; }
} }
.footer-left { justify-content: right; }
.footer-logo { .footer-logo {
grid-column: unset; display: flex;
grid-row: unset; align-items: center;
padding-right: 2rem;
box-sizing: border-box;
img {
width: 6rem;
margin-right: unset;
}
} }
} }
} }

View File

@ -0,0 +1,59 @@
// Standard button
// Could be applied to either a button or a link
// Semi-expected to have an icon
.st-button {
$button-font-size: st-value(100);
$button-background: themed("color-section-lighten-5");
$button-background-hover: themed("color-section-lighten-10");
$button-text-color: themed("color-text");
display: flex;
gap: st-value(100) / 4;
border-radius: radius(025);
// Button final size
// Font 1rem
// Padding 2 * 0.5rem
font-size: st-value(100);
line-height: st-value(100);
padding: st-value(100) / 2;
height: st-value(100) * 2;
// TODO What if button is on a light background
background: $button-background;
color: $button-text-color;
&:hover { background: $button-background-hover; }
& > svg {
// Icon should be slightly larger than text,
// with padding to fill the entire button height
height: st-value(100) * 1.5; // 1.5rem
width: st-value(100) * 1.5; // 1.5rem
margin: -#{st-value(100) / 2} 0; // 0.5rem
padding: st-value(100) / 4; // 0.25rem
border-radius: radius(025);
}
& > span {
text-align: left;
// Do not overflow text
overflow: hidden;
flex: 1;
}
// Full width button
&.w100 { width: 100%; }
&.stealth {
background: none;
padding: (st-value(100) / 2) 0;
svg { background: $button-background; }
span { color: themed("color-link"); }
&:hover {
svg { background: $button-background-hover; }
span { color: themed("color-link-hover"); }
}
}
}

View File

@ -0,0 +1,21 @@
@use "sass:map";
// Standard variables for typography and UI elements
$st-values: (
000: 0rem,
025: 0.25rem,
050: 0.50rem,
075: 0.75rem,
100: 1rem,
);
@function st-value($name) {
@return map-get($map: $st-values, $key: $name);
}
@function padding($value) { @return st-value(($value)); }
@mixin st-padding($value) { padding: padding(($value)); }
@function radius($value) { @return st-value($value); }
@mixin st-radius($value) { border-radius: radius($value); }

View File

@ -6,6 +6,7 @@ nav.navigation {
width: 100%; // otherwise narrow when fixed width: 100%; // otherwise narrow when fixed
z-index: 20; // otherwise post labels layered above z-index: 20; // otherwise post labels layered above
position: relative;
/* Top bar, always visible */ /* Top bar, always visible */
@ -13,16 +14,18 @@ nav.navigation {
grid-area: logo; grid-area: logo;
background-color: themed("color-background"); background-color: themed("color-background");
height: 3.75rem;
a.nav-logo-link { a.nav-logo-link {
display: flex; display: flex;
box-sizing: border-box;
// Height: 3.75rem // Height: 3.75rem
// - padding 0.25 * 2 = 0.5 // - padding 0.125 * 2 = 0.25
// - image 3.25 // - image 3.5
height: 3.25rem; height: 3.5rem;
width: 3.25rem; width: 4rem;
margin: 0.25rem; margin: 0.125rem;
background-image: url("main-logo.svg"); background-image: url("main-logo.svg");
background-repeat: no-repeat; background-repeat: no-repeat;
@ -37,160 +40,208 @@ nav.navigation {
display: flex; display: flex;
flex-flow: row-reverse; flex-flow: row-reverse;
align-items: center; align-items: center;
gap: 0.5em;
font-size: 1.15rem; font-size: 1.15rem;
padding-right: 0.5em;
background-color: themed("color-background"); background-color: themed("color-background");
padding-right: 0.25rem;
height: 3.75rem;
// Height: 3.75rem // Height: 3.75rem
// - wrapper padding 0.875 * 2 = 1.75 // - link padding 0.625 * 2 = 1.25
// - link padding 0.25 * 2 = 0.5 // - internal size 2.5
// - font size 1.5
padding: 0.875rem;
& > a { & > a {
display: flex; display: flex;
gap: 0.25em; padding: 0.625rem 0.5rem;
padding: 0.25rem 0.5rem; & > span {
background: themed("color-foreground"); display: flex;
border-radius: 6px; justify-content: center;
align-items: center;
box-sizing: border-box;
gap: 0.5rem;
height: 2.5rem;
min-width: 2.5rem;
line-height: 1.5rem;
padding: 0 0.5rem;
& > i { background: themed("color-foreground");
font-size: 1.5rem;
color: themed("color-link-active"); color: themed("color-link-active");
border-radius: 0.25rem;
white-space: nowrap;
}
&:hover > span, &:active > span { background: themed("color-section"); }
&:focus { outline: none; }
}
a.simple-avatar {
.simple-avatar-button {
padding: 0;
gap: 0;
.simple-avatar-name {
padding: 0.5rem;
@include window-smaller-than(32rem) {
display: none;
}
}
.simple-avatar-image {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 2.5rem;
height: 2.5rem;
box-sizing: border-box;
border-radius: 0.25rem;
background: themed("color-section");
img {
width: 2.5rem;
height: 2.5rem;
border-radius: 0.25rem;
object-fit: cover;
z-index: 1; // above the letter
}
&::before {
content: attr(data-name);
position: absolute;
font-size: 1.5rem;
}
}
@include window-smaller-than(32rem) {
&.sign-in .simple-avatar-image {
background: themed("color-foreground");
}
}
} }
} }
} }
/* Prevent toggled menus from being too wide */ /* Offset elements on the left and bottom */
.nav-offset-left { // Needed to track clicks outside the menu area
grid-area: offleft; @each $name in ("left", "bott") {
display: none; // flex .nav-offset-#{$name} {
background: #00000050; grid-area: off#{$name};
} display: none; // flex
background: #00000050;
.nav-offset-bottom { }
grid-area: offbott;
display: none; // flex
background: #00000050;
} }
/* Toggled menus, hidden by default */ /* Toggled menus, hidden by default */
.nav-primary { // Naming areas
grid-area: primary; @each $name in (primary, secondary, tools, help) {
display: none; // flex .nav-#{$name} {
flex-flow: column; grid-area: $name;
display: none;
background-color: themed("color-section"); li {
font-size: 1.5em; padding: 0;
li { & > a {
padding: 0; display: flex;
a { align-items: center;
display: block; gap: 0.5rem;
border-bottom: 1px solid themed("color-foreground");
padding: 0.5em;
// "Comments" is usually the longest and might wrap white-space: nowrap;
white-space: nowrap;
i { svg {
width: 1.5rem; margin: -0.25rem 0;
color: themed("color-link-active"); color: themed("color-link-active");
text-align: center; }
}
}
&.current a { background-color: themed("color-foreground"); }
&.forum-updated {
position: relative;
&::after {
content: "";
width: 6px;
height: 6px;
border-radius: 3px;
background: var(--palette-text-red);
position: absolute;
right: 0.2em;
top: 1em;
} }
} }
} }
} }
.nav-secondary { // Common top
grid-area: secondary; .nav-primary, .nav-secondary {
display: none; // flex
flex-flow: column; flex-flow: column;
background-color: themed("color-foreground");
font-size: 1.35em;
height: 440px;
// Prevent the tools / help buttons from being pushed // Prevent the tools / help buttons from being pushed
// way too low on pages with a lot of secondary links // way too low on pages with a lot of secondary links
overflow: scroll; height: 422px;
overflow-y: scroll;
li a {
justify-content: start;
}
}
.nav-primary {
background-color: themed("color-section");
font-size: 1.25rem;
li > a {
border-bottom: 1px solid themed("color-foreground");
line-height: 1.25rem;
padding: 1rem 0.5rem;
}
li.current a { background-color: themed("color-foreground"); }
}
.nav-secondary {
background-color: themed("color-foreground");
font-size: 1.2rem;
li { li {
padding: 0; & > a {
a {
display: block;
border-bottom: 1px solid themed("color-section"); border-bottom: 1px solid themed("color-section");
padding: 0.5em; line-height: 1.2rem;
padding: 0.7rem;
white-space: wrap; // forum menus are long
} }
&.divider { &.divider {
border-bottom: 1px solid themed("color-section"); border-bottom: 1px solid themed("color-section");
height: 0.25em; height: 0.25rem;
} }
form input[type="text"] { form input[type="text"] {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
// Reduced font size to make the search font-size: 1.25rem;
// box less claustrophobic padding: 0.5rem 0.5rem;
font-size: 1em;
padding: 0.25em 0.5em;
} }
} }
} }
.nav-tools { // Common bottom
grid-area: tools; .nav-tools, .nav-help {
display: none; // grid
grid-template-columns: 1fr 1fr;
grid-template-rows: min-content; grid-template-rows: min-content;
font-size: 1.1rem;
padding: 1rem;
gap: 1rem; gap: 1rem;
background-color: themed("color-section"); background-color: themed("color-section");
li > a {
justify-content: center;
border-radius: 0.25rem;
padding: 0.7rem 1rem;
line-height: 1.1rem;
}
}
.nav-tools {
grid-template-columns: 1fr 1fr;
border-top: 1px solid themed("color-foreground"); border-top: 1px solid themed("color-foreground");
padding: 1rem 1rem 0.5rem;
li { li {
padding: 0; a { background: themed("color-section-lighten-5"); }
&.nav-tools-login { grid-column: 1 / -1; } &.nav-tools-login { grid-column: 1 / -1; }
& > a {
display: block;
background: themed("color-section-lighten-5");
border-radius: 6px;
font-size: 125%;
padding: 0.5rem 1rem;
text-align: center;
i { color: themed("color-link-active"); }
}
} }
&.anonymous li.nav-tools-themes { &.anonymous li.nav-tools-themes {
@ -199,42 +250,19 @@ nav.navigation {
} }
.nav-help { .nav-help {
grid-area: help;
display: none; // grid
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: min-content; padding: 0.5rem 1rem 1rem;
padding: 1rem;
gap: 1rem;
background: themed("color-section");
li { li {
padding: 0; a { background: themed("color-section-darken-5"); }
&.nav-help-discord, &.nav-help-discord,
&.nav-help-subscribestar { &.nav-help-subscribestar {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
& > a { & > a img {
display: flex; height: 1.5rem;
margin: -0.2rem 0;
background: themed("color-section-darken-5");
border-radius: 6px;
font-size: 125%;
padding: 0.5rem 1rem;
justify-content: center;
align-items: center;
gap: 0.25em;
img {
height: 1.25em;
margin: -0.5em 0;
}
} }
// Hack to put the wiki/help links before discord/sstar on mobile // Hack to put the wiki/help links before discord/sstar on mobile
@ -255,6 +283,29 @@ body[data-th-sheader="true"] nav.navigation {
} }
// Forum notification
body[data-th-forumnotif="true"] nav.navigation .nav-primary li.forum-updated {
position: relative;
&::after {
content: "";
width: 6px;
height: 6px;
border-radius: 3px;
background: palette("text-red");
position: absolute;
right: 0.2rem;
top: 1.25rem;
@include window-larger-than(800px) {
top: 0.2rem;
}
}
}
// Mobile toggle // Mobile toggle
html.nav-toggled { html.nav-toggled {
@ -279,14 +330,14 @@ html.nav-toggled {
// Allow scrolling when the menu is too long // Allow scrolling when the menu is too long
overflow-y: scroll; overflow-y: scroll;
.nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bottom { .nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bott {
display: flex; display: flex;
} }
.nav-tools, .nav-help { .nav-tools, .nav-help {
display: grid; display: grid;
} }
.nav-primary, .nav-tools, .nav-help { .nav-primary, .nav-tools, .nav-help {
box-shadow: -1px 0 5px -1px var(--color-background); box-shadow: -3px 3px 5px -1px themed("color-background");
} }
} }
} }
@ -294,74 +345,68 @@ html.nav-toggled {
// Desktop // Desktop
nav.navigation, html.nav-toggled nav.navigation { nav.navigation, html.nav-toggled nav.navigation {
@include window-larger-than(800px) { @include window-larger-than(50rem) {
grid-template-areas: grid-template-areas:
"logo primary help tools " "logo primary help help controls"
"logo secondary secondary secondary" "logo secondary secondary tools controls";
; grid-template-columns: min-content min-content minmax(0, 1fr) min-content 3.25rem;
grid-template-columns: min-content min-content minmax(0, 1fr) min-content; grid-template-rows: 1.5rem 1.75rem;
grid-template-rows: 1.75em 2em;
padding: 0 1em 0.5em; padding: 0 1rem 0.5rem;
box-sizing: border-box; box-sizing: border-box;
height: unset; height: unset;
background: var(--color-background); background: themed("color-background");
overflow-y: hidden; // overrides mobile hack allowing the menu scrolling overflow-y: hidden; // overrides mobile hack allowing the menu scrolling
.nav-logo { .nav-logo a.nav-logo-link {
a.nav-logo-link { margin: 0.25rem 0.5rem 0 0; } height: 3.25rem;
width: 3.25rem;
margin: 0.25rem 0.5rem 0 0;
} }
.nav-offset-left, .nav-offset-bottom, .nav-controls { display: none; } .mobile { display: none; }
.nav-primary { // All link ribbons
.desktop {
display: flex; display: flex;
flex-flow: row; flex-flow: row;
background: unset; font-size: 0.875rem;
font-size: 1.05em; box-shadow: unset;
padding: 0 0.25em;
li { li a {
display: flex; display: flex;
align-items: center;
height: 100%;
a { padding: 0 0.625rem;
align-content: center; white-space: nowrap;
}
}
border-bottom: 0; .nav-primary {
padding: 0 0.75em; background: unset;
i { display: none; } height: unset;
} padding-left: 0.25rem;
&.forum-updated::after { li a {
top: 0.2em; border-bottom: 0;
} svg { display: none; }
} }
} }
.nav-secondary { .nav-secondary {
display: flex;
flex-flow: row;
height: unset; height: unset;
padding: 0 0.25em; padding: 0 0.25rem;
font-size: 1.05em; border-radius: 0.25rem 0 0 0.25rem;
border-radius: 6px;
// Silly fix for too many links overflow: hidden; // Silly fix for too many links
overflow: hidden; z-index: 1; // above the avatar
li { li {
display: flex; a { border-bottom: 0; }
a {
align-content: center;
border-bottom: 0;
padding: 0 0.75em;
white-space: nowrap;
}
&.divider { &.divider {
display: flex; display: flex;
@ -371,50 +416,46 @@ nav.navigation, html.nav-toggled nav.navigation {
&::after { content: "|"; } &::after { content: "|"; }
} }
form { form input[type="text"] {
display: flex; width: 12rem;
align-items: center; padding: 0.25rem 0.5rem;
font-size: 1rem;
input[type="text"] { min-width: 10em; } border-radius: 0.25rem;
} }
} }
} }
.nav-tools, .nav-help { .nav-tools, .nav-help {
display: flex;
padding: 0; padding: 0;
background: unset; background: unset;
border: none; border: none;
gap: 0; gap: 0;
li { li a {
display: flex; gap: 0.25rem;
a { background: unset;
align-content: center; text-align: unset;
border-radius: 0;
background: unset;
font-size: 1.05em;
padding: 0 0.75em;
text-align: unset;
white-space: nowrap;
border-radius: 0;
}
} }
} }
.nav-tools { .nav-tools {
background: themed("color-foreground");
// Otherwise help gets layered above it
// When the viewport is narrow (but not mobile)
z-index: 1; z-index: 1;
background: var(--color-background); border-radius: 0 0.25rem 0.25rem 0;
margin-right: 0.25rem;
li { li {
a { a {
i { color: themed("color-link"); } padding: 0 0.5rem;
&:hover i { color: themed("color-link-hover"); }
svg {
color: themed("color-link");
height: 1.25rem;
width: 1.25rem;
}
&:hover svg { color: themed("color-link-hover"); }
} }
&.nav-tools-themes, &.nav-tools-settings { &.nav-tools-themes, &.nav-tools-settings {
@ -422,26 +463,123 @@ nav.navigation, html.nav-toggled nav.navigation {
} }
} }
} }
.nav-help {
li a img { display: none; }
.nav-help {
// At small resolutions, overflow can
// cause scrollbars to appear
overflow: hidden;
li a img { display: none; }
li.current a { li.current a {
background-color: themed("color-foreground"); background-color: themed("color-foreground");
} }
} }
.nav-primary, .nav-tools, .nav-help { box-shadow: unset; } .nav-controls {
position: absolute;
right: 0;
top: 0;
height: 3.25rem;
box-sizing: border-box;
padding: 0.25rem 0 0;
#nav-toggle { display: none; }
a.simple-avatar {
padding: 0;
height: 100%;
.simple-avatar-button {
background: none;
color: inherit;
align-items: start;
font-size: 0.875rem;
line-height: 0.875rem;
.simple-avatar-name {
padding: 0 0.5rem;
}
.simple-avatar-image {
height: 3rem;
width: 3rem;
background: themed("color-foreground");
img {
width: 2.75rem;
height: 2.75rem;
}
}
}
}
}
// Icon collapse
// Stage 1: discord and subscribestar buttons
.collapse-1 { display: none; }
@include window-larger-than(77rem) {
.collapse-1 { display: flex; }
}
// Stage 2: account label
.collapse-2 .simple-avatar-name { display: none; }
@include window-larger-than(65rem) {
.collapse-2 .simple-avatar-name { display: unset; }
}
} }
} }
// Tweak for the secondary menu on desktop // Tweak for the secondary menu on desktop
body.c-static.a-home { body.c-static.a-home {
@include window-larger-than(800px) { @include window-larger-than(50rem) {
nav.navigation, menu.nav-logo, menu.nav-secondary {
nav.navigation {
// Center and align the navbar
grid-template-areas: "logo primary help controls";
grid-template-columns: repeat(4, min-content);
justify-content: center;
// Remove padding to prevent a scrollbar
// at low desktop resolutions
padding: 0 0 0.5rem;
#nav-subscribestar, #nav-discord, .nav-secondary, .nav-tools { display: none; }
}
// Match the background colors
nav.navigation, menu.nav-logo, menu.nav-secondary, menu.nav-controls {
background: unset; background: unset;
} }
menu.nav-tools { menu.nav-tools {
background: var(--bg-color); background: var(--bg-color);
} }
menu.nav-controls {
position: static;
height: unset;
padding: 0;
.simple-avatar-button {
height: unset;
align-items: center;
.simple-avatar-image { display: none; }
}
}
}
@include window-smaller-than(50rem) {
// Only show the primary navbar on mobile
// since the secondary is empty anyways
nav.navigation {
grid-template-areas:
"logo logo controls"
"offleft primary primary "
"offleft tools tools "
"offleft help help "
"offleft offbott offbott ";
.nav-secondary { display: none; }
}
} }
} }

View File

@ -17,6 +17,8 @@ div#page div#news {
&.open { &.open {
max-height: none; max-height: none;
#news-body { pointer-events: unset; }
} }
#news-header { #news-header {
@ -26,26 +28,27 @@ div#page div#news {
cursor: pointer; cursor: pointer;
width: 100%; width: 100%;
text-align: center; text-align: center;
padding: 0.5rem 0.5rem 0; padding: 0.5rem;
} }
#news-body { #news-body {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
margin-top: 5px;
max-width: 800px; max-width: 800px;
padding: 0 0.5rem 0.5rem; padding: 0 0.5rem 0.5rem;
pointer-events: none;
} }
#news-dismiss { #news-dismiss {
position: absolute; position: absolute;
right: 0.5rem; right: 0;
top: 0.25rem; top: 0;
padding: 0.25rem 1rem;
cursor: pointer; cursor: pointer;
font-size: 1.15rem; font-size: 1.25rem;
font-weight: bold; font-weight: bold;
color: white;
} }
} }

View File

@ -1,35 +1,101 @@
.paginator {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
background-color: themed("color-foreground");
border-radius: 0.25rem;
div.paginator { width: max-content;
display: block; margin: 0 auto;
padding: 2em 0 1em 0;
text-align: center;
clear: both;
menu { & > a, & > span {
display: flex; display: flex;
box-sizing: border-box;
justify-content: center; justify-content: center;
align-items: center;
min-width: 2.15rem;
font-size: 1rem;
line-height: 1rem;
padding: 0.75rem 0.3rem; // otherwise large page numbers wrap
border-radius: 0.25rem;
&:hover {
background: themed("color-section");
}
} }
li { & > a[data-disabled="true"] {
a { color: var(--color-text);
margin: 0 0.25em; pointer-events: none;
padding: 0.25em 0.75em; }
// Ordering
// Oh boy
.page {
order: 20;
&.lg { display: none; }
&.current { cursor: default; }
}
.prev {
order: 1;
margin-right: auto;
}
.spacer {
order: 20;
padding: 0;
&:last-child { display: none; }
svg {
height: 1rem;
transform: rotate(90deg);
}
}
.next {
order: 9;
margin-left: auto;
}
.break {
order: 10;
width: 100%;
}
// Tablet
@include window-larger-than(35rem) {
justify-content: center;
gap: 0.125rem;
a, span {
order: 0 !important;
min-width: 2.25rem;
font-size: 0.9rem;
} }
a:hover { .break { display: none; }
background: $paginator-hover-background; .spacer {
color: $paginator-hover-color; padding: inherit;
svg { transform: unset; }
} }
&.more { .prev { margin-right: 1rem; }
color: $paginator-more-color; .next { margin-left: 1rem; }
}
@include window-larger-than(50rem) {
a, span {
padding: 0.75rem 0.5rem;
font-size: 0.95rem;
} }
span { .prev, .next {
margin: 0 0.25em; span { display: none; }
padding: 0.25em 0.75em;
font-weight: bold;
} }
} }
@include window-larger-than(65rem) {
a.page.lg { display: flex; }
}
} }

View File

@ -59,14 +59,6 @@
} }
} }
.guest-warning {
.guest-warning-dialog {
top: 5vh;
height: 90vh;
width: 80vw;
}
}
div#page { div#page {
> div /* div#c-$controller */ > div /* div#c-$controller */

View File

@ -1,33 +1,55 @@
.guest-warning { .guest-warning {
position: fixed; position: fixed;
left: 0;
top: 0; top: 0;
height: 100vh; left: 0;
width: 100vw; right: 0;
z-index: 500; bottom: 0;
background-color: themed("color-background"); z-index: 1000;
display: flex;
justify-content: center;
background: themed("color-background");
.guest-warning-dialog { .guest-warning-dialog {
z-index: 500; margin: 20vh 0.5em 0;
position: relative;
top: 20vh; height: min-content;
margin-left: auto; max-width: 360px;
margin-right: auto;
height: 40vh;
width: 40vw;
overflow-x: scroll;
background-color: themed("color-section");
.dialog-header { .dialog-header {
padding: $padding-050 $base-padding; padding: 0 0.5em;
background-color: themed("color-section-lighten-5"); margin-bottom: 0.5em;
border-bottom: 1px solid themed("color-background");
} }
.dialog-content { .dialog-content {
padding: $padding-050 $base-padding; background: var(--color-foreground);
padding: 2em 1em 1em;
border-radius: 3px 3px 0 0;
p:last-child { margin-bottom: 0; }
} }
.dialog-footer { .dialog-footer {
padding: $padding-050 $base-padding; display: flex;
flex-wrap: wrap;
gap: 0.5em;
background: var(--color-foreground);
padding: 1em 1em 2em;
border-radius: 0 0 3px 3px;
button {
background: var(--color-section-lighten-5);
color: var(--color-link);
padding: 0.5em 1em;
border-radius: 3px;
&:hover {
background: var(--color-section-lighten-10);
color: var(--color-link-hover);
}
}
} }
} }
} }

View File

@ -3,6 +3,12 @@ body.c-static.a-home {
background-color: var(--bg-color); background-color: var(--bg-color);
background-image: var(--bg-image); background-image: var(--bg-image);
background-position-y: 3.75rem;
@include window-larger-than(800px) {
background-position-y: unset;
}
#page { #page {
background: none; background: none;
margin: 8rem auto 0; margin: 8rem auto 0;
@ -20,7 +26,7 @@ body.c-static.a-home {
padding: 1rem; padding: 1rem;
margin: 0.5rem; margin: 0.5rem;
border-radius: 0.5rem; border-radius: 0.25rem;
text-align: center; text-align: center;
} }
@ -73,6 +79,10 @@ body.c-static.a-home {
} }
} }
#home-search-form:not(.empty) .home-buttons a span::before {
content: "Search ";
}
// Footer // Footer
.home-footer-top { .home-footer-top {
@ -86,7 +96,7 @@ body.c-static.a-home {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
border-radius: 0.5rem 0.5rem 0 0; border-radius: 0.25rem 0.25rem 0 0;
@include window-larger-than(480px) { @include window-larger-than(480px) {
margin: 0.5rem 0.5rem 0; margin: 0.5rem 0.5rem 0;
@ -109,7 +119,7 @@ body.c-static.a-home {
margin: 0; margin: 0;
padding: 0.5rem; padding: 0.5rem;
border-radius: 0 0 0.5rem 0.5rem; border-radius: 0 0 0.25rem 0.25rem;
@include window-larger-than(480px) { @include window-larger-than(480px) {
margin: 0 0.5rem; margin: 0 0.5rem;

View File

@ -1,3 +1,22 @@
body.c-posts.a-index, body.c-favorites.a-index {
#page {
// Override the theme to instead
// project it upon the content area
background: none;
padding: 0;
}
// Exhibit A
// Makes the content area take up the
// full height of the page. Yes, really.
#page, #c-posts, #c-favorites, #a-index {
// I hate both this and myself
display: flex;
flex-flow: column;
flex: 1;
}
}
.post-index { .post-index {
display: grid; display: grid;
@ -7,24 +26,46 @@
"sidebar"; "sidebar";
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content 1fr min-content; grid-template-rows: min-content 1fr min-content;
gap: 1em;
flex: 1; // See Exhibit A
// 1. Searchbox // 1. Searchbox
.search { .search {
grid-area: search; grid-area: search;
padding: 0.5rem 0.25rem;
background-color: #152f56;
background-color: themed("color-foreground");
box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background");
h1 { h1 {
font-size: $h3-size; font-size: $h3-size;
} }
.search-controls {
display: none;
flex-flow: column;
}
} }
// 2. Content // 2. Content
.content { .content {
display: flex; // See Exhibit A
flex-flow: column;
grid-area: content; grid-area: content;
// Imported from #page
padding: 0.5rem 0.25rem themed("content-padding-bottom");
background-color: #152f56;
background-color: themed("color-foreground");
background-image: themed("image-foreground");
background-position: themed("image-foreground-position");
background-repeat: themed("image-foreground-repeat");
// Quick tag edit // Quick tag edit
#edit-dialog textarea { #edit-dialog textarea {
margin-bottom: 0.25em; margin-bottom: 0.25rem;
} }
// Actual content area: // Actual content area:
@ -32,86 +73,13 @@
.post-index-gallery { .post-index-gallery {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
gap: 1em; gap: 1rem;
.wiki-excerpt { flex: 1; // See Exhibit A
display: flex;
flex-flow: column;
position: relative;
padding: 1em 1em 0;
gap: 0.5em;
background: var(--color-section); .posts-container {
max-width: 60em; flex: 1; // See Exhibit A
grid-auto-rows: min-content;
.wiki-excerpt-toggle {
position: absolute;
top: 0;
right: 0;
padding: 1em;
outline: none;
transition: transform 0.25s;
&::after {
@include font-awesome-icon;
content: unicode("f0d8");
}
}
.styled-dtext {
background: linear-gradient(to top, var(--color-section), var(--color-text));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
max-height: 10em;
overflow: hidden;
transition: max-height 0.25s;
// Disable links
pointer-events: none;
cursor: unset;
a {
color: unset;
text-decoration: underline;
&::after { content: none; }
}
}
.wiki-excerpt-readmore {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3em;
// Makes the button appear in the middle of the animation
transition: visibility 0s 0.125s;
span {
padding: 0.5em 1em;
background: var(--color-section);
border-radius: 6px;
}
}
&.hidden{
.wiki-excerpt-toggle { transform: rotate(-90deg); }
.styled-dtext {
max-height: 0;
}
.wiki-excerpt-readmore { visibility: hidden; }
}
}
.paginator {
padding: 1em 0;
} }
} }
} }
@ -124,6 +92,11 @@
flex-flow: column; flex-flow: column;
gap: 1em; gap: 1em;
padding: 0.5rem 0.25rem;
background-color: #152f56;
background-color: themed("color-foreground");
box-shadow: inset 0px 0.25rem 0.25rem -0.25rem themed("color-background");
// Mode selection // Mode selection
#mode-box-mode, #mode-box #set-id { #mode-box-mode, #mode-box #set-id {
width: 100%; width: 100%;
@ -139,11 +112,170 @@
// Desktop // Desktop
.post-index { .post-index {
@include window-larger-than(800px) { @include window-larger-than(50rem) {
grid-template-areas: grid-template-areas:
"search content" "search content"
"sidebar content"; "sidebar content";
grid-template-columns: 15em 1fr; grid-template-columns: 14rem 1fr;
grid-template-rows: min-content 1fr; grid-template-rows: min-content 1fr;
.search {
box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background");
margin-top: 0.25rem;
border-top-left-radius: 0.25rem;
padding: 0.5rem;
.search-controls {
display: flex;
margin-top: 0.5rem;
}
}
.sidebar {
box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background");
margin-bottom: 0.25rem;
border-bottom-left-radius: 0.25rem;
padding: 0.5rem
}
.content {
border-radius: 0.25rem;
}
}
}
// Fullscreen
body.c-posts.a-index[data-st-fullscreen="true"] {
// Desktop-only, for obvious reasons
@include window-larger-than(50rem) {
.post-index {
grid-template-areas:
"search "
"content";
grid-template-columns: 1fr;
.search {
display: flex;
border-radius: 0.25rem 0.25rem 0 0;
box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background");
.post-search {
flex: 1;
}
.search-controls {
display: flex;
justify-content: right;
align-self: end;
margin: 0 0 0 0.5rem;
.st-button.w100 {
width: unset;
span { display: none; }
}
}
}
.sidebar { display: none; }
.content {
border-radius: 0 0 0.25rem 0.25rem;
}
}
}
}
// FEATURES
// Wiki Excerpt
.wiki-excerpt {
display: flex;
flex-flow: column;
position: relative;
background: themed("color-section");
border-radius: 0.25rem;
// header
h3 {
cursor: pointer;
padding: 0.5rem 1rem 0.5rem 1.5rem;
&::after {
@include font-awesome-icon;
content: unicode("f0da");
transition: transform 0.25s;
position: absolute;
top: 0;
left: 0;
padding: 0.5rem;
}
}
// body
.styled-dtext {
background: linear-gradient(to top, themed("color-section"), themed("color-text"));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
max-height: 0rem;
max-width: 50rem;
overflow: hidden;
padding: 0 0.5rem;
transition: max-height 0.25s;
// Disable links
pointer-events: none;
cursor: unset;
a {
color: unset;
text-decoration: underline;
&::after { content: none; }
}
p:last-child { margin-bottom: 0; }
}
// wiki link
.wiki-excerpt-readmore {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3rem;
max-width: 50rem;
box-sizing: border-box;
// Makes the button appear in the middle of the animation
transition: visibility 0s 0.125s;
visibility: hidden;
span {
padding: 0.5rem 1rem;
background: themed("color-section");
border-radius: 0.25rem;
}
}
&.open{
.wiki-excerpt-toggle::after { transform: rotate(90deg); }
.styled-dtext {
max-height: 10rem;
}
.wiki-excerpt-readmore { visibility: visible; }
}
&.loading {
h3::after, .styled-dtext { transition: none; }
} }
} }

View File

@ -20,7 +20,7 @@ $modes: (
); );
@each $mode, $color in $modes { @each $mode, $color in $modes {
#page[data-mode-menu="#{$mode}"] { #page[data-mode-menu="#{$mode}"] .content {
background-color: $color; background-color: $color;
} }
} }

View File

@ -2,6 +2,7 @@
#has-parent-relationship-preview, #has-children-relationship-preview { #has-parent-relationship-preview, #has-children-relationship-preview {
display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -1,20 +1,12 @@
div#c-static { div#c-static {
div#a-site-map { div#a-site-map {
width: 80em; display: flex;
flex-flow: row wrap;
max-width: 80em;
section { section {
width: 20em; width: 20em;
float: left; ul { margin-bottom: 1.5em; }
h1 {
font-size: $h3-size;
}
ul {
margin-bottom: 1.5em;
}
} }
} }
} }

View File

@ -249,8 +249,9 @@ div#c-users {
div.input { div.input {
input[type="text"], input[type="email"], input[type="password"], select { input[type="text"], input[type="email"], input[type="password"], select {
width: 100%; // z_responsive is the absolute worst
max-width: unset; width: 100% !important;
max-width: unset !important;
box-sizing: border-box; box-sizing: border-box;
} }
} }

View File

@ -3,18 +3,27 @@
module GitHelper module GitHelper
def self.init def self.init
if Rails.root.join("REVISION").exist? if Rails.root.join("REVISION").exist?
@hash = Rails.root.join("REVISION").read.strip @hash = @tag = Rails.root.join("REVISION").read.strip
elsif system("type git > /dev/null && git rev-parse --show-toplevel > /dev/null") elsif system("type git > /dev/null && git rev-parse --show-toplevel > /dev/null")
@hash = `git rev-parse HEAD`.strip @hash = `git rev-parse HEAD`.strip
@tag = `git describe --abbrev=0`
else else
@hash = "" @hash = @tag = ""
end end
end end
def self.tag
@tag
end
def self.hash def self.hash
@hash @hash
end end
def self.version
@tag.presence || short_hash
end
def self.short_hash def self.short_hash
@hash[0..8] @hash[0..8]
end end
@ -22,4 +31,13 @@ module GitHelper
def self.commit_url(commit_hash) def self.commit_url(commit_hash)
"#{Danbooru.config.source_code_url}/commit/#{commit_hash}" "#{Danbooru.config.source_code_url}/commit/#{commit_hash}"
end end
def self.release_url(tag_name)
"#{Danbooru.config.source_code_url}/releases/tag/#{tag_name}"
end
def self.version_url
return release_url(@tag) if @tag.present?
commit_url(@hash)
end
end end

View File

@ -38,9 +38,6 @@ class UserFeedback < ApplicationRecord
def log_destroy def log_destroy
ModAction.log(:user_feedback_destroy, { user_id: user_id, reason: body, type: category, record_id: id }) ModAction.log(:user_feedback_destroy, { user_id: user_id, reason: body, type: category, record_id: id })
deletion_user = "\"#{CurrentUser.name}\":/users/#{CurrentUser.id}"
creator_user = "\"#{creator.name}\":/users/#{creator.id}"
StaffNote.create(body: "#{deletion_user} deleted #{category} feedback, created #{created_at.to_date} by #{creator_user}: #{body}", user_id: user_id, creator: User.system)
end end
end end

View File

@ -1,4 +1,4 @@
<% if comment.should_see?(CurrentUser.user) || (current_page?(:controller => "comments", :action => "show") && CurrentUser.id == comment.creator_id) %> <% if comment.should_see?(CurrentUser.user) || (params[:controller] == "comments" && params[:action] == "show" && CurrentUser.id == comment.creator_id) %>
<article class="comment comment-post-grid <%= "below-threshold" if comment.below_threshold? %>" data-post-id="<%= comment.post_id %>" <article class="comment comment-post-grid <%= "below-threshold" if comment.below_threshold? %>" data-post-id="<%= comment.post_id %>"
data-comment-id="<%= comment.id %>" data-score="<%= comment.score %>" data-comment-id="<%= comment.id %>" data-score="<%= comment.score %>"
data-creator="<%= comment.creator&.name.downcase %>" data-is-sticky="<%= comment.is_sticky %>" data-creator-id="<%= comment.creator_id %>" data-creator="<%= comment.creator&.name.downcase %>" data-is-sticky="<%= comment.is_sticky %>" data-creator-id="<%= comment.creator_id %>"

View File

@ -1,7 +1,8 @@
<%= decorated_nav_link_to("Posts", "fas fa-images", posts_path) %> <%= decorated_nav_link_to("Artists", :brush, artists_path) %>
<%= decorated_nav_link_to("Pools", "fas fa-book", gallery_pools_path) %> <%= decorated_nav_link_to("Posts", :images, posts_path) %>
<%= decorated_nav_link_to("Sets", "fas fa-clone", post_sets_path) %> <%= decorated_nav_link_to("Pools", :library, gallery_pools_path) %>
<%= decorated_nav_link_to("Tags", "fas fa-tags", tags_path) %> <%= decorated_nav_link_to("Sets", :group, post_sets_path) %>
<%= decorated_nav_link_to("Blips", "fas fa-bullhorn", blips_path) %> <%= decorated_nav_link_to("Tags", :tags, tags_path) %>
<%= decorated_nav_link_to("Comments", "fas fa-comment-alt", comments_path(group_by: "post")) %> <%= decorated_nav_link_to("Blips", :megaphone, blips_path) %>
<%= decorated_nav_link_to("Forum", "fas fa-chalkboard", forum_topics_path, class: (CurrentUser.has_forum_been_updated? ? "forum-updated" : nil)) %> <%= decorated_nav_link_to("Comments", :message_square, comments_path(group_by: "post")) %>
<%= decorated_nav_link_to("Forum", :lectern, forum_topics_path, class: (CurrentUser.has_forum_been_updated? ? "forum-updated" : nil)) %>

View File

@ -3,45 +3,45 @@
<a href="/" class="nav-logo-link"></a> <a href="/" class="nav-logo-link"></a>
</menu> </menu>
<menu class="nav-primary"> <menu class="nav-primary desktop">
<%= render "layouts/main_links" %> <%= render "layouts/main_links" %>
</menu> </menu>
<menu class="nav-offset-left"></menu> <menu class="nav-offset-left mobile"></menu>
<menu class="nav-offset-bottom"></menu> <menu class="nav-offset-bott mobile"></menu>
<menu class="nav-secondary <%= "empty" unless content_for(:secondary_links) %>"> <menu class="nav-secondary desktop <%= "empty" unless content_for(:secondary_links) %>">
<%= yield :secondary_links %> <%= yield :secondary_links %>
</menu> </menu>
<menu class="nav-controls"> <menu class="nav-controls desktop">
<a href="" id="nav-toggle"> <a role="button" href="" id="nav-toggle" class="nav-controls-toggle">
<i class="fas fa-bars"></i> <span><%= svg_icon(:hamburger) %></span>
</a> </a>
<% if CurrentUser.is_anonymous? %> <% if CurrentUser.is_anonymous? %>
<%= link_to(new_session_path, class: "nav-tools-login") do %> <a href="<%= new_session_path %>" class="simple-avatar nav-controls-profile collapse-2">
<i class="fas fa-sign-in-alt"></i> <span class="simple-avatar-button sign-in">
Sign in <span class="simple-avatar-name">
<% end %> Sign In
</span>
<span class="simple-avatar-image">
<%= svg_icon(:log_in) %>
</span>
</span>
</a>
<% else %> <% else %>
<%= link_to(user_path(CurrentUser.user), class: "nav-tools-login") do %> <%= simple_avatar(CurrentUser.user, named: true, class: "nav-controls-profile collapse-2") %>
<i class="far fa-user-circle"></i>
Profile
<% end %>
<% end %> <% end %>
</menu> </menu>
<menu class="nav-tools <%= CurrentUser.is_anonymous? ? "anonymous" : "" %>"> <menu class="nav-tools desktop <%= CurrentUser.is_anonymous? ? "anonymous" : "" %>">
<%= decorated_nav_link_to("Themes", "fas fa-swatchbook", theme_path, class: "nav-tools-themes") %> <%= decorated_nav_link_to("Themes", :swatch, theme_path, class: "nav-tools-themes") %>
<% if CurrentUser.is_anonymous? %> <% unless CurrentUser.is_anonymous? %>
<%= decorated_nav_link_to("Sign in", "fas fa-sign-in-alt", new_session_path, class: "nav-tools-login") %> <%= decorated_nav_link_to("Settings", :settings, edit_user_path(CurrentUser.user), class: "nav-tools-settings") %>
<% else %>
<%= decorated_nav_link_to("Settings", "fas fa-cog", edit_user_path(CurrentUser.user), class: "nav-tools-settings") %>
<%= decorated_nav_link_to("Account #{unread_dmails(CurrentUser.user)}", "far fa-user-circle", home_users_path, class: "nav-tools-login") %>
<% end %> <% end %>
</menu> </menu>
<menu class="nav-help <%= CurrentUser.is_anonymous? ? "anonymous" : "" %>"> <menu class="nav-help desktop <%= CurrentUser.is_anonymous? ? "anonymous" : "" %>">
<%= nav_link_to("Wiki", wiki_pages_path(title: "help:home"), class: "nav-help-wiki") %> <%= nav_link_to("Wiki", wiki_pages_path(title: "help:home"), class: "nav-help-wiki") %>
<%= nav_link_to("Help", help_pages_path, class: "nav-help-help") %> <%= nav_link_to("Help", help_pages_path, class: "nav-help-help") %>
<%= nav_link_to("More", site_map_path, class: "nav-help-map") %> <%= nav_link_to("More", site_map_path, class: "nav-help-map") %>

View File

@ -1,17 +1,23 @@
<%= javascript_tag nonce: true do -%> <%= javascript_tag nonce: true do -%>
(function() { (function() {
try { try {
var theme = localStorage.getItem('theme') || 'hexagon'; const values = {
var extra = localStorage.getItem('theme-extra') || 'none'; // Theme
var sheader = localStorage.getItem('theme-sheader') || false; "th-main": localStorage.getItem("theme") || "hexagon",
var palette = localStorage.getItem('theme-palette') || 'default'; "th-extra": localStorage.getItem("theme-extra") || "none",
var nav = localStorage.getItem('theme-nav') || 'top'; "th-sheader": localStorage.getItem("theme-sheader") || false,
"th-forumnotif": localStorage.getItem("theme-forumnotif") || false,
"th-palette": localStorage.getItem("theme-palette") || "default",
"th-nav": localStorage.getItem("theme-nav") || "top",
// Settings
"st-fullscreen": localStorage.getItem("e6.posts.fusk") || false,
};
var b = document.body; var b = document.body;
b.setAttribute('data-th-main', theme); for (const [name, value] of Object.entries(values)) {
b.setAttribute('data-th-extra', extra); b.setAttribute("data-" + name, value);
b.setAttribute('data-th-sheader', sheader); }
b.setAttribute('data-th-palette', palette);
b.setAttribute('data-th-nav', nav);
} catch(e) {} } catch(e) {}
})(); })();
<% end -%> <% end -%>

View File

@ -10,8 +10,11 @@
<% if CurrentUser.is_anonymous? %> <% if CurrentUser.is_anonymous? %>
<%= render "static/guest_warning" %> <%= render "static/guest_warning" %>
<% end %> <% end %>
<div id="page"> <div id="page">
<%= yield :layout %> <%= yield :layout %>
</div> </div>
<%= render "static/deferred_posts" %>
<% end %> <% end %>
</html> </html>

View File

@ -1,8 +1,8 @@
<% if news_update.present? %> <% if news_update.present? %>
<div class="ui-state-highlight site-notice" style="display: none;" id="news" data-id="<%= news_update.id %>"> <div class="ui-state-highlight site-notice" style="display: none;" id="news" data-id="<%= news_update.id %>">
<a href="" id="news-dismiss" title="Dismiss"><i class="fas fa-times"></i></a> <a href="" role="button" id="news-dismiss" title="Dismiss"><i class="fas fa-times"></i></a>
<h6 id="news-header">News: <%= news_update.created_at.strftime("%b %d, %Y") %> <h6 id="news-header">News: <%= news_update.created_at.strftime("%b %d, %Y") %>
<a href="" id="news-show">Show</a> <a href="" role="button" id="news-show">Show</a>
</h6> </h6>
<div id="news-body" class="dtext-container"><%= format_text(news_update.message) %></div> <div id="news-body" class="dtext-container"><%= format_text(news_update.message) %></div>
</div> </div>

View File

@ -3,6 +3,7 @@
<div class="search"> <div class="search">
<%= render "posts/partials/common/search", title: "Posts", tags: params[:tags] %> <%= render "posts/partials/common/search", title: "Posts", tags: params[:tags] %>
<%= render "posts/partials/index/controls" %>
</div> </div>
<div class="sidebar"> <div class="sidebar">

View File

@ -0,0 +1,6 @@
<div class="search-controls">
<button id="search-fullscreen" class="st-button w100 stealth">
<%= svg_icon(:fullscreen) %>
<span>Fullscreen</span>
</button>
</div>

View File

@ -33,9 +33,7 @@
<% if CurrentUser.is_privileged? %> <% if CurrentUser.is_privileged? %>
<span id="tag-script-ui" style="display: none;"> <span id="tag-script-ui" style="display: none;">
<input id="tag-script-field" data-autocomplete="tag-edit" placeholder="Enter tag script" style="display: none;"/> <input id="tag-script-field" data-autocomplete="tag-edit" placeholder="Enter tag script" style="display: none;"/>
<% if CurrentUser.is_staff? %> <button id="tag-script-all">All</button>
<button id="tag-script-all">All</button>
<% end %>
</span> </span>
<input id="quick-mode-reason" placeholder="Reason" style="display: none;"/> <input id="quick-mode-reason" placeholder="Reason" style="display: none;"/>
<% end %> <% end %>

View File

@ -1,8 +1,7 @@
<div id="posts" class="post-index-gallery user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>"> <div id="posts" class="post-index-gallery user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>">
<% if @wiki_text.present? %> <% if @wiki_text.present? %>
<section class="wiki-excerpt hidden"> <section class="wiki-excerpt loading">
<h3><%= @wiki_page.pretty_title %></h3> <h3 role="button" class="wiki-excerpt-toggle"><%= @wiki_page.pretty_title %></h3>
<a href="" class="wiki-excerpt-toggle" role="button"></a>
<%= format_text(@wiki_text, allow_color: true, max_thumbs: 0) %> <%= format_text(@wiki_text, allow_color: true, max_thumbs: 0) %>
<a href="<%= wiki_page_path(@wiki_page) %>" class="wiki-excerpt-readmore"><span>Read More</span></a> <a href="<%= wiki_page_path(@wiki_page) %>" class="wiki-excerpt-readmore"><span>Read More</span></a>
</section> </section>

View File

@ -1,20 +1,29 @@
<footer id="page-footer"> <footer class="footer-wrapper">
<span class="footer-left"> <div class="footer-grid">
<%= link_to "Rules", terms_of_service_path %> <span class="footer-logo">
<%= link_to "Takedowns", takedown_static_path %> <a href="/"><%= image_pack_tag("main-logo.svg") %></a>
<%= link_to "Privacy", privacy_policy_path %> </span>
<%= link_to "Contact", contact_path %> <span class="footer-left">
<%= link_to "Advertising", help_page_path(id: "advertising") %> <%= link_to "Rules", terms_of_service_path %>
</span> <%= link_to "Takedowns", takedown_static_path %>
<span class="footer-logo"> <%= link_to "Privacy", privacy_policy_path %>
<a href="/"><%= image_pack_tag("main-logo.svg", class: "footer-logo", title: "Running #{Danbooru.config.software_name} #{GitHelper.short_hash}") %></a> <%= link_to "Contact", contact_path %>
</span> <%= link_to "Advertising", help_page_path(id: "advertising") %>
<span class="footer-right"> </span>
<span class="footer-running">Running <%= Danbooru.config.software_name %> <%= link_to GitHelper.short_hash.to_s, GitHelper.commit_url(GitHelper.hash) %></span> <span class="footer-right">
<%= link_to "Themes / Gestures", theme_path %> <span class="footer-running">Running e621ng</span>
<% if CurrentUser.user.enable_keyboard_navigation %> <span class="footer-version">
<%= link_to "Keyboard Shortcuts", keyboard_shortcuts_path %> <% if GitHelper.version.empty? %>
<% end %> v. unknown
<%= link_to disable_mobile_mode? ? "Mobile mode: OFF": "Mobile mode: ON", disable_mobile_mode_path, :rel => "nofollow" %> <% else %>
</span> v. <%= link_to GitHelper.version, GitHelper.version_url %>
<% end %>
</span>
<%= link_to "Themes / Gestures", theme_path %>
<% if CurrentUser.user.enable_keyboard_navigation %>
<%= link_to "Keyboard Shortcuts", keyboard_shortcuts_path %>
<% end %>
<%= link_to disable_mobile_mode? ? "Mobile mode: OFF": "Mobile mode: ON", disable_mobile_mode_path, :rel => "nofollow" %>
</span>
</div>
</footer> </footer>

View File

@ -4,12 +4,11 @@
<h1 class="dialog-header">Over 18?</h1> <h1 class="dialog-header">Over 18?</h1>
<div class="dialog-content"> <div class="dialog-content">
<p> <p>
You must be over the age of 18 and agree You must be <b>18 years or older</b> and agree to the <%= link_to "terms of service", terms_of_service_path, target: "_blank" %> to access this website.
to <%= link_to "the terms of service", terms_of_service_path, target: "_blank" %> to access this page.
</p> </p>
<p> <p>
By default a limited blacklist has been applied hiding content that is commonly objected to. You may remove Content that is commonly considered objectionable is blacklisted by default.
items from this blacklist by using the blacklist menu item. You may remove tags from this blacklist using the corresponding menu item.
</p> </p>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">

View File

@ -4,24 +4,27 @@
<% end -%> <% end -%>
<section class="home-section"> <section class="home-section">
<h1><%= Danbooru.config.app_name %></h1> <h1><%= Danbooru.config.app_name %></h1><br/>
<br/> <%= form_tag(posts_path, method: "get", id: "home-search-form") do %>
<%= form_tag(posts_path, method: "get", class: "home-search") do %> <!-- Primary Searchbar -->
<%= text_field_tag("tags", "", autofocus: "autofocus", placeholder: "Search posts by tag (title:\"video name\")", data: { shortcut: "q", autocomplete: "tag-query" }) %> <div class="home-search">
<%= tag.button(tag.i(class: "fa-solid fa-magnifying-glass"), type: "submit") %> <%= text_field_tag("tags", "", autofocus: "autofocus", placeholder: "Search posts by tag (title:\"video name\")", data: { shortcut: "q", autocomplete: "tag-query" }) %>
<%= tag.button(tag.i(class: "fa-solid fa-magnifying-glass"), type: "submit") %>
</div>
<!-- Secondary search buttons -->
<div class="home-buttons">
<a href="<%= posts_path %>" tags="">
<i class="fas fa-splotch"></i>
<span>Latest</span>
</a>
<a href="<%= posts_path(tags: "order:rank") %>" tags="order:rank">
<i class="fas fa-fire-alt"></i>
<span>Popular</span>
</a>
</div>
<% end %> <% end %>
<div class="home-buttons">
<a href="/posts">
<i class="fas fa-splotch"></i>
Latest
</a>
<a href="/popular">
<i class="fas fa-fire-alt"></i>
Popular
</a>
</div>
</section> </section>
<% if news_update = NewsUpdate.recent %> <% if news_update = NewsUpdate.recent %>

View File

@ -2,7 +2,7 @@
<div id="a-site-map"> <div id="a-site-map">
<section> <section>
<ul> <ul>
<li><h1>Posts</h1></li> <li><h3>Posts</h3></li>
<li><%= link_to("Listing", posts_path) %></li> <li><%= link_to("Listing", posts_path) %></li>
<li><%= link_to("Upload", new_upload_path) %></li> <li><%= link_to("Upload", new_upload_path) %></li>
<% if CurrentUser.is_janitor? %> <% if CurrentUser.is_janitor? %>
@ -17,7 +17,7 @@
<li><%= link_to("Help", help_page_path(id: "posts")) %></li> <li><%= link_to("Help", help_page_path(id: "posts")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Post Events</h1></li> <li><h3>Post Events</h3></li>
<li><%= link_to("Listing", post_events_path) %></li> <li><%= link_to("Listing", post_events_path) %></li>
<li><%= link_to("Tag Changes", post_versions_path) %></li> <li><%= link_to("Tag Changes", post_versions_path) %></li>
<li><%= link_to("Approvals", post_approvals_path) %></li> <li><%= link_to("Approvals", post_approvals_path) %></li>
@ -25,7 +25,7 @@
<li><%= link_to("Replacements", post_replacements_path) %></li> <li><%= link_to("Replacements", post_replacements_path) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Tools</h1></li> <li><h3>Tools</h3></li>
<li><%= link_to("News Updates", news_updates_path) %></li> <li><%= link_to("News Updates", news_updates_path) %></li>
<li><%= link_to("Mascots", mascots_path) %></li> <li><%= link_to("Mascots", mascots_path) %></li>
<li><%= link_to("Source Code", Danbooru.config.source_code_url) %></li> <li><%= link_to("Source Code", Danbooru.config.source_code_url) %></li>
@ -35,7 +35,7 @@
<li><%= link_to("DB Export", "/db_export/") %></li> <li><%= link_to("DB Export", "/db_export/") %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Artists</h1></li> <li><h3>Artists</h3></li>
<li><%= link_to("Listing", artists_path) %></li> <li><%= link_to("Listing", artists_path) %></li>
<li><%= link_to("Avoid Posting Entries", avoid_postings_path) %></li> <li><%= link_to("Avoid Posting Entries", avoid_postings_path) %></li>
<li><%= link_to("Avoid Posting List", avoid_posting_static_path) %></li> <li><%= link_to("Avoid Posting List", avoid_posting_static_path) %></li>
@ -45,7 +45,7 @@
</section> </section>
<section> <section>
<ul> <ul>
<li><h1>Tags</h1></li> <li><h3>Tags</h3></li>
<li><%= link_to("Listing", tags_path) %></li> <li><%= link_to("Listing", tags_path) %></li>
<li><%= link_to("Aliases", tag_aliases_path) %></li> <li><%= link_to("Aliases", tag_aliases_path) %></li>
<li><%= link_to("Implications", tag_implications_path) %></li> <li><%= link_to("Implications", tag_implications_path) %></li>
@ -53,13 +53,13 @@
<li><%= link_to("Help", help_page_path(id: "tags")) %></li> <li><%= link_to("Help", help_page_path(id: "tags")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Notes</h1></li> <li><h3>Notes</h3></li>
<li><%= link_to("Listing", notes_path) %></li> <li><%= link_to("Listing", notes_path) %></li>
<li><%= link_to("Changes", note_versions_path) %></li> <li><%= link_to("Changes", note_versions_path) %></li>
<li><%= link_to("Help", help_page_path(id: "notes")) %></li> <li><%= link_to("Help", help_page_path(id: "notes")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Pools</h1></li> <li><h3>Pools</h3></li>
<li><%= link_to("Listing", gallery_pools_path) %></li> <li><%= link_to("Listing", gallery_pools_path) %></li>
<li><%= link_to("Changes", pool_versions_path) %></li> <li><%= link_to("Changes", pool_versions_path) %></li>
<li><%= link_to("Help", help_page_path(id: "pools")) %></li> <li><%= link_to("Help", help_page_path(id: "pools")) %></li>
@ -67,30 +67,30 @@
</section> </section>
<section> <section>
<ul> <ul>
<li><h1>Comments</h1></li> <li><h3>Comments</h3></li>
<li><%= link_to("Listing", comments_path) %></li> <li><%= link_to("Listing", comments_path) %></li>
<li><%= link_to("Help", help_page_path(id: "comments")) %></li> <li><%= link_to("Help", help_page_path(id: "comments")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Forum</h1></li> <li><h3>Forum</h3></li>
<li><%= link_to("Listing", forum_topics_path) %></li> <li><%= link_to("Listing", forum_topics_path) %></li>
<li><%= link_to("Help", help_page_path(id: "forum")) %></li> <li><%= link_to("Help", help_page_path(id: "forum")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Wiki</h1></li> <li><h3>Wiki</h3></li>
<li><%= link_to("Listing", wiki_pages_path) %></li> <li><%= link_to("Listing", wiki_pages_path) %></li>
<li><%= link_to("Changes", wiki_page_versions_path) %></li> <li><%= link_to("Changes", wiki_page_versions_path) %></li>
<li><%= link_to("Help", help_page_path(id: "wiki")) %></li> <li><%= link_to("Help", help_page_path(id: "wiki")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Blips</h1></li> <li><h3>Blips</h3></li>
<li><%= link_to("Listing", blips_path) %></li> <li><%= link_to("Listing", blips_path) %></li>
<li><%= link_to("Help", help_page_path(id: "blips")) %></li> <li><%= link_to("Help", help_page_path(id: "blips")) %></li>
</ul> </ul>
</section> </section>
<section> <section>
<ul> <ul>
<li><h1>Users</h1></li> <li><h3>Users</h3></li>
<li><%= link_to("Listing", users_path) %></li> <li><%= link_to("Listing", users_path) %></li>
<li><%= link_to("Bans", bans_path) %></li> <li><%= link_to("Bans", bans_path) %></li>
<% if CurrentUser.is_anonymous? %> <% if CurrentUser.is_anonymous? %>
@ -105,7 +105,7 @@
<li><%= link_to("Help", help_page_path(id: "accounts")) %></li> <li><%= link_to("Help", help_page_path(id: "accounts")) %></li>
</ul> </ul>
<ul> <ul>
<li><h1>Admin</h1></li> <li><h3>Admin</h3></li>
<% if CurrentUser.is_moderator? %> <% if CurrentUser.is_moderator? %>
<li><%= link_to("Mod Dashboard", moderator_dashboard_path) %></li> <li><%= link_to("Mod Dashboard", moderator_dashboard_path) %></li>
<% end %> <% end %>

View File

@ -43,6 +43,12 @@
<option value="true">Enabled</option> <option value="true">Enabled</option>
</select> </select>
<label for="theme_forumnotif">Forum Activity Dot</label>
<select id="theme_forumnotif">
<option value="false">Disabled</option>
<option value="true">Enabled</option>
</select>
<h3>Accessibility</h3> <h3>Accessibility</h3>
<label for="theme_palette">Palette</label> <label for="theme_palette">Palette</label>
<select id="theme_palette"> <select id="theme_palette">

View File

@ -0,0 +1,56 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "environment"))
destroyed_feedback_ids = []
CurrentUser.as_system do
ModAction.where(action: "user_feedback_destroy")
# On July 24, 2024, we deployed the ability to soft-delete feedback records.
# We only care about restoring destroyed feedbacks that were destroyed before this date.
# Any entries after this are "real" destructions, that do not need to be restored.
.where("created_at < ?", CUTOFF_DATE = Date.new(2024, 8, 1))
.find_in_batches(batch_size: 10_000) do |batch|
feedback_data = batch.map do |mod_action|
record_id = mod_action.values["record_id"].to_i
destroyed_feedback_ids << record_id
{
id: record_id,
user_id: mod_action.values["user_id"].to_i,
creator_id: User.system.id, # placeholder
category: mod_action.values["type"],
body: mod_action.values["reason"]&.strip,
created_at: Date.new(1970, 1, 1), # placeholder
updated_at: mod_action.created_at,
updater_id: mod_action.creator_id,
is_deleted: true,
}
end
UserFeedback.insert_all(feedback_data) if feedback_data.any?
end
end
CurrentUser.as_system do
ModAction.where(action: "user_feedback_create")
.where("values->>'record_id' IN (?)", destroyed_feedback_ids.map(&:to_s))
.find_in_batches(batch_size: 10_000) do |batch|
batch.each do |mod_action|
record_id = mod_action.values["record_id"].to_i
next unless destroyed_feedback_ids.include?(record_id)
UserFeedback.where(id: record_id).update_all(
creator_id: mod_action.creator_id,
created_at: mod_action.created_at,
)
end
end
end
StaffNote.where(creator: User.system)
.where("body LIKE ?", "%deleted%feedback%")
.find_in_batches(batch_size: 10_000) do |batch|
StaffNote.where(id: batch.map(&:id)).delete_all
end

View File

@ -93,7 +93,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
end end
should "delete a feedback" do should "delete a feedback" do
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
delete_auth user_feedback_path(@user_feedback), @critic delete_auth user_feedback_path(@user_feedback), @critic
end end
end end
@ -101,7 +101,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
context "by a moderator" do context "by a moderator" do
should "allow destroying feedbacks they created" do should "allow destroying feedbacks they created" do
as(@mod) { @user_feedback = create(:user_feedback, user: @user) } as(@mod) { @user_feedback = create(:user_feedback, user: @user) }
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
delete_auth user_feedback_path(@user_feedback), @mod delete_auth user_feedback_path(@user_feedback), @mod
end end
end end
@ -126,13 +126,13 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
context "by an admin" do context "by an admin" do
should "allow destroying feedbacks they created" do should "allow destroying feedbacks they created" do
as(@admin) { @user_feedback = create(:user_feedback, user: @user) } as(@admin) { @user_feedback = create(:user_feedback, user: @user) }
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
delete_auth user_feedback_path(@user_feedback), @admin delete_auth user_feedback_path(@user_feedback), @admin
end end
end end
should "allow destroying feedbacks they did not create" do should "allow destroying feedbacks they did not create" do
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
delete_auth user_feedback_path(@user_feedback, format: :json), @admin delete_auth user_feedback_path(@user_feedback, format: :json), @admin
end end
end end