From 97dfa98e93ed86ca126c5abcf9368ec2c164038c Mon Sep 17 00:00:00 2001 From: Cinder Date: Fri, 31 Jan 2025 07:15:10 -0800 Subject: [PATCH 01/23] [Themes] Add a toggle for the forum activity dot (#869) --- app/javascript/src/javascripts/themes.js | 2 +- .../src/javascripts/utility/storage.js | 3 ++ .../src/styles/common/navigation.scss | 42 ++++++++++--------- app/views/layouts/_theme_include.html.erb | 2 + app/views/static/theme.html.erb | 6 +++ 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/javascript/src/javascripts/themes.js b/app/javascript/src/javascripts/themes.js index 83d7f9da6..8ee283cfc 100644 --- a/app/javascript/src/javascripts/themes.js +++ b/app/javascript/src/javascripts/themes.js @@ -3,7 +3,7 @@ import LStorage from "./utility/storage"; 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) { Object.defineProperty(Theme, one, { diff --git a/app/javascript/src/javascripts/utility/storage.js b/app/javascript/src/javascripts/utility/storage.js index f796134d4..1f1c45d7a 100644 --- a/app/javascript/src/javascripts/utility/storage.js +++ b/app/javascript/src/javascripts/utility/storage.js @@ -65,6 +65,9 @@ LStorage.Theme = { /** @returns {boolean} True if the sticky header is enabled */ StickyHeader: ["theme-sheader", false], + + /** @returns {boolean} True if the forum notification dot is enabled */ + ForumNotif: ["theme-forumnotif", false], }; StorageUtils.bootstrapMany(LStorage.Theme); diff --git a/app/javascript/src/styles/common/navigation.scss b/app/javascript/src/styles/common/navigation.scss index 3a732f95d..b42b0d4d9 100644 --- a/app/javascript/src/styles/common/navigation.scss +++ b/app/javascript/src/styles/common/navigation.scss @@ -104,22 +104,6 @@ nav.navigation { } &.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; - } - } } } @@ -255,6 +239,28 @@ 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: var(--palette-text-red); + + position: absolute; + right: 0.2em; + top: 1em; + + @include window-larger-than(800px) { + top: 0.2em; + } + } +} + // Mobile toggle html.nav-toggled { @@ -333,10 +339,6 @@ nav.navigation, html.nav-toggled nav.navigation { padding: 0 0.75em; i { display: none; } } - - &.forum-updated::after { - top: 0.2em; - } } } diff --git a/app/views/layouts/_theme_include.html.erb b/app/views/layouts/_theme_include.html.erb index 21657ba33..cf3791804 100644 --- a/app/views/layouts/_theme_include.html.erb +++ b/app/views/layouts/_theme_include.html.erb @@ -4,12 +4,14 @@ var theme = localStorage.getItem('theme') || 'hexagon'; var extra = localStorage.getItem('theme-extra') || 'hexagon'; var sheader = localStorage.getItem('theme-sheader') || false; + var forumnotif = localStorage.getItem('theme-forumnotif') || false; var palette = localStorage.getItem('theme-palette') || 'default'; var nav = localStorage.getItem('theme-nav') || 'top'; var b = document.body; b.setAttribute('data-th-main', theme); b.setAttribute('data-th-extra', extra); b.setAttribute('data-th-sheader', sheader); + b.setAttribute('data-th-forumnotif', forumnotif); b.setAttribute('data-th-palette', palette); b.setAttribute('data-th-nav', nav); } catch(e) {} diff --git a/app/views/static/theme.html.erb b/app/views/static/theme.html.erb index ac8a268ec..eeb601027 100644 --- a/app/views/static/theme.html.erb +++ b/app/views/static/theme.html.erb @@ -43,6 +43,12 @@ + + +

Accessibility

- <% if CurrentUser.is_staff? %> - - <% end %> + <% end %> From 813679954651a4be0bd59742d66b29b092325665 Mon Sep 17 00:00:00 2001 From: clragon Date: Mon, 3 Feb 2025 18:00:20 +0100 Subject: [PATCH 12/23] [UserFeedbacks] Restore deleted user feedbacks from staff notes (#859) --- app/models/user_feedback.rb | 3 - ...3_convert_staffnote_to_deleted_feedback.rb | 56 +++++++++++++++++++ .../user_feedbacks_controller_test.rb | 8 +-- 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100755 db/fixes/123_convert_staffnote_to_deleted_feedback.rb diff --git a/app/models/user_feedback.rb b/app/models/user_feedback.rb index 811994906..4c21ccd74 100644 --- a/app/models/user_feedback.rb +++ b/app/models/user_feedback.rb @@ -38,9 +38,6 @@ class UserFeedback < ApplicationRecord def log_destroy 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 diff --git a/db/fixes/123_convert_staffnote_to_deleted_feedback.rb b/db/fixes/123_convert_staffnote_to_deleted_feedback.rb new file mode 100755 index 000000000..fe4f56196 --- /dev/null +++ b/db/fixes/123_convert_staffnote_to_deleted_feedback.rb @@ -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 diff --git a/test/functional/user_feedbacks_controller_test.rb b/test/functional/user_feedbacks_controller_test.rb index b60e38725..0d6b1feac 100644 --- a/test/functional/user_feedbacks_controller_test.rb +++ b/test/functional/user_feedbacks_controller_test.rb @@ -93,7 +93,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest end 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 end end @@ -101,7 +101,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest context "by a moderator" do should "allow destroying feedbacks they created" do 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 end end @@ -126,13 +126,13 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest context "by an admin" do should "allow destroying feedbacks they created" do 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 end end 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 end end From f6e9f44aef2e9950a98519883052758f818f070c Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 09:00:41 -0800 Subject: [PATCH 13/23] [Home] Rework the search form to use the extra buttons (#881) --- app/javascript/src/javascripts/home.js | 46 ++++++++++++++++++++ app/javascript/src/styles/specific/home.scss | 4 ++ app/views/static/home.html.erb | 33 ++++++++------ 3 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 app/javascript/src/javascripts/home.js diff --git a/app/javascript/src/javascripts/home.js b/app/javascript/src/javascripts/home.js new file mode 100644 index 000000000..ff4010cbe --- /dev/null +++ b/app/javascript/src/javascripts/home.js @@ -0,0 +1,46 @@ +import Page from "./utility/page"; + +const Home = {}; + +Home.init = function () { + + const $form = $("#home-search-form"); + const $tags = $("#tags"); + console.log("init"); + + let isEmpty = !$tags.val(); + let wasEmpty = isEmpty; + if (isEmpty) $form.addClass("empty"); + console.log("input", isEmpty, wasEmpty); + + $tags.on("input", () => { + wasEmpty = isEmpty; + isEmpty = !$tags.val(); + console.log("input", isEmpty, wasEmpty); + + 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; diff --git a/app/javascript/src/styles/specific/home.scss b/app/javascript/src/styles/specific/home.scss index 8ea1846b6..94dd52ea2 100644 --- a/app/javascript/src/styles/specific/home.scss +++ b/app/javascript/src/styles/specific/home.scss @@ -75,6 +75,10 @@ body.c-static.a-home { } } + #home-search-form:not(.empty) .home-buttons a span::before { + content: "Search "; + } + // Footer .home-footer-top { diff --git a/app/views/static/home.html.erb b/app/views/static/home.html.erb index 492239927..244aed43f 100644 --- a/app/views/static/home.html.erb +++ b/app/views/static/home.html.erb @@ -4,21 +4,26 @@ <% end -%>
- <%= form_tag(posts_path, method: "get", class: "home-search") do %> - <%= text_field_tag("tags", "", autofocus: "autofocus", placeholder: "Search posts by tag", data: { shortcut: "q", autocomplete: "tag-query" }) %> - <%= tag.button(tag.i(class: "fa-solid fa-magnifying-glass"), type: "submit") %> - <% end %> + <%= form_tag(posts_path, method: "get", id: "home-search-form") do %> - + + + + + + <% end %>
<% if news_update = NewsUpdate.recent %> From b690d82adc6a3297c13b33991198466986a9ae8c Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 09:56:21 -0800 Subject: [PATCH 14/23] [UI] Rework the navbar to include the user avatar (#884) --- app/helpers/application_helper.rb | 15 + app/javascript/src/javascripts/navigation.js | 2 +- app/javascript/src/javascripts/thumbnails.js | 22 + .../src/styles/common/navigation.scss | 494 ++++++++++-------- app/views/layouts/_nav.html.erb | 48 +- app/views/layouts/blank.html.erb | 3 + 6 files changed, 348 insertions(+), 236 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8b37699a7..ba6aba93a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -189,6 +189,21 @@ module ApplicationHelper 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 unread_dmails(user) if user.has_mail? "(#{user.unread_dmail_count})" diff --git a/app/javascript/src/javascripts/navigation.js b/app/javascript/src/javascripts/navigation.js index 3d5fd84ad..beae52ac3 100644 --- a/app/javascript/src/javascripts/navigation.js +++ b/app/javascript/src/javascripts/navigation.js @@ -2,7 +2,7 @@ const Navigation = {}; Navigation.init = function () { 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(); wrapper.toggleClass("nav-toggled"); diff --git a/app/javascript/src/javascripts/thumbnails.js b/app/javascript/src/javascripts/thumbnails.js index 65e62559a..0d8be6576 100644 --- a/app/javascript/src/javascripts/thumbnails.js +++ b/app/javascript/src/javascripts/thumbnails.js @@ -8,6 +8,28 @@ Thumbnails.initialize = function () { const posts = $(".post-thumb.placeholder, .thumb-placeholder-link"); const replacedPosts = []; + // Avatar special case + for (const post of $(".simple-avatar")) { + const $post = $(post); + console.log(1, $post); + + const postID = $post.data("id"); + if (!postID) continue; + console.log(2, postID); + + const postData = postsData[postID]; + if (!postData || !postData["preview_url"]) continue; + console.log(3, postData); + + console.log(3.55, $post.data("name")); + $("") + .attr("src", postData["preview_url"]) + .appendTo($post.find("span.simple-avatar-image")); + console.log(4, "done"); + continue; + } + + // Reset of the deferred posts for (const post of posts) { const $post = $(post); diff --git a/app/javascript/src/styles/common/navigation.scss b/app/javascript/src/styles/common/navigation.scss index 5997692c5..cf1e76fb9 100644 --- a/app/javascript/src/styles/common/navigation.scss +++ b/app/javascript/src/styles/common/navigation.scss @@ -6,6 +6,7 @@ nav.navigation { width: 100%; // otherwise narrow when fixed z-index: 20; // otherwise post labels layered above + position: relative; /* Top bar, always visible */ @@ -13,16 +14,18 @@ nav.navigation { grid-area: logo; background-color: themed("color-background"); + height: 3.75rem; a.nav-logo-link { display: flex; + box-sizing: border-box; // Height: 3.75rem - // - padding 0.25 * 2 = 0.5 - // - image 3.25 - height: 3.25rem; - width: 3.25rem; - margin: 0.25rem; + // - padding 0.125 * 2 = 0.25 + // - image 3.5 + height: 3.5rem; + width: 4rem; + margin: 0.125rem; background-image: url("main-logo.svg"); background-repeat: no-repeat; @@ -37,148 +40,205 @@ nav.navigation { display: flex; flex-flow: row-reverse; align-items: center; - gap: 0.5em; font-size: 1.15rem; - padding-right: 0.5em; background-color: themed("color-background"); + padding-right: 0.25rem; + height: 3.75rem; // Height: 3.75rem - // - wrapper padding 0.875 * 2 = 1.75 - // - link padding 0.25 * 2 = 0.5 - // - font size 1.5 - padding: 0.875rem; - + // - link padding 0.625 * 2 = 1.25 + // - internal size 2.5 & > a { display: flex; - gap: 0.25em; + padding: 0.625rem 0.5rem; - padding: 0.25rem 0.5rem; - background: themed("color-foreground"); - border-radius: 6px; + & > span { + display: flex; + 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; - white-space: nowrap; - - & > i { - font-size: 1.5rem; + background: themed("color-foreground"); color: themed("color-link-active"); + border-radius: 0.25rem; + white-space: nowrap; + + i { font-size: 1.5rem; } + } + + &: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; + } + } } } } - /* Prevent toggled menus from being too wide */ - .nav-offset-left { - grid-area: offleft; - display: none; // flex - background: #00000050; - } - - .nav-offset-bottom { - grid-area: offbott; - display: none; // flex - background: #00000050; + /* Offset elements on the left and bottom */ + // Needed to track clicks outside the menu area + @each $name in ("left", "bott") { + .nav-offset-#{$name} { + grid-area: off#{$name}; + display: none; // flex + background: #00000050; + } } /* Toggled menus, hidden by default */ - .nav-primary { - grid-area: primary; - display: none; // flex - flex-flow: column; + // Naming areas + @each $name in (primary, secondary, tools, help) { + .nav-#{$name} { + grid-area: $name; + display: none; - background-color: themed("color-section"); - font-size: 1.5em; + li { + padding: 0; - li { - padding: 0; - a { - display: block; - border-bottom: 1px solid themed("color-foreground"); - padding: 0.5em; + & > a { + display: flex; + align-items: center; + gap: 0.5rem; - // "Comments" is usually the longest and might wrap - white-space: nowrap; + white-space: nowrap; - i { - width: 1.5rem; - color: themed("color-link-active"); - text-align: center; + i { + width: 1.5rem; + color: themed("color-link-active"); + text-align: center; + } } } - - &.current a { background-color: themed("color-foreground"); } } } - .nav-secondary { - grid-area: secondary; - display: none; // flex + // Common top + .nav-primary, .nav-secondary { flex-flow: column; - background-color: themed("color-foreground"); - font-size: 1.35em; - height: 440px; - // Prevent the tools / help buttons from being pushed // way too low on pages with a lot of secondary links + 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 { - padding: 0; - a { - display: block; + & > a { 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 { border-bottom: 1px solid themed("color-section"); - height: 0.25em; + height: 0.25rem; } form input[type="text"] { width: 100%; box-sizing: border-box; - // Reduced font size to make the search - // box less claustrophobic - font-size: 1em; - padding: 0.25em 0.5em; + font-size: 1.25rem; + padding: 0.5rem 0.5rem; } } } - .nav-tools { - grid-area: tools; - - display: none; // grid - grid-template-columns: 1fr 1fr; + // Common bottom + .nav-tools, .nav-help { grid-template-rows: min-content; - - padding: 1rem; + font-size: 1.1rem; gap: 1rem; 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"); + padding: 1rem 1rem 0.5rem; li { - padding: 0; - + a { background: themed("color-section-lighten-5"); } &.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; - - white-space: nowrap; - - i { color: themed("color-link-active"); } - } } &.anonymous li.nav-tools-themes { @@ -187,42 +247,19 @@ nav.navigation { } .nav-help { - grid-area: help; - - display: none; // grid grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: min-content; - - padding: 1rem; - gap: 1rem; - - background: themed("color-section"); + padding: 0.5rem 1rem 1rem; li { - padding: 0; - + a { background: themed("color-section-darken-5"); } &.nav-help-discord, &.nav-help-subscribestar { grid-column: 1 / -1; } - & > a { - display: flex; - - 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; - } + & > a img { + height: 1.5rem; + margin: -0.2rem 0; } // Hack to put the wiki/help links before discord/sstar on mobile @@ -253,18 +290,19 @@ body[data-th-forumnotif="true"] nav.navigation .nav-primary li.forum-updated { height: 6px; border-radius: 3px; - background: var(--palette-text-red); + background: palette("text-red"); position: absolute; - right: 0.2em; - top: 1em; + right: 0.2rem; + top: 1.25rem; @include window-larger-than(800px) { - top: 0.2em; + top: 0.2rem; } } } + // Mobile toggle html.nav-toggled { @@ -289,14 +327,14 @@ html.nav-toggled { // Allow scrolling when the menu is too long 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; } .nav-tools, .nav-help { display: grid; } .nav-primary, .nav-tools, .nav-help { - box-shadow: -1px 0 5px -1px var(--color-background); + box-shadow: -3px 3px 5px -1px themed("color-background"); } } } @@ -304,70 +342,68 @@ html.nav-toggled { // Desktop nav.navigation, html.nav-toggled nav.navigation { - @include window-larger-than(800px) { + @include window-larger-than(50rem) { grid-template-areas: - "logo primary help tools " - "logo secondary secondary secondary" - ; - grid-template-columns: min-content min-content minmax(0, 1fr) min-content; - grid-template-rows: 1.75em 2em; + "logo primary help help controls" + "logo secondary secondary tools controls"; + grid-template-columns: min-content min-content minmax(0, 1fr) min-content 3.25rem; + grid-template-rows: 1.5rem 1.75rem; - padding: 0 1em 0.5em; + padding: 0 1rem 0.5rem; box-sizing: border-box; height: unset; - background: var(--color-background); + background: themed("color-background"); overflow-y: hidden; // overrides mobile hack allowing the menu scrolling - .nav-logo { - a.nav-logo-link { margin: 0.25rem 0.5rem 0 0; } + .nav-logo a.nav-logo-link { + 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; flex-flow: row; + font-size: 0.875rem; + box-shadow: unset; + + li a { + display: flex; + align-items: center; + height: 100%; + + padding: 0 0.625rem; + white-space: nowrap; + } + } + + .nav-primary { background: unset; - font-size: 1.05em; - padding: 0 0.25em; + height: unset; + padding-left: 0.25rem; - li { - a { - display: flex; - align-items: center; - height: 100%; - - border-bottom: 0; - padding: 0 0.75em; - i { display: none; } - } + li a { + border-bottom: 0; + i { display: none; } } } .nav-secondary { - display: flex; - flex-flow: row; height: unset; - padding: 0 0.25em; - font-size: 1.05em; - border-radius: 6px; + padding: 0 0.25rem; + border-radius: 0.25rem 0 0 0.25rem; - // Silly fix for too many links - overflow: hidden; + overflow: hidden; // Silly fix for too many links + z-index: 1; // above the avatar li { - a { - display: flex; - align-items: center; - height: 100%; - - border-bottom: 0; - padding: 0 0.75em; - white-space: nowrap; - } + a { border-bottom: 0; } &.divider { display: flex; @@ -377,49 +413,40 @@ nav.navigation, html.nav-toggled nav.navigation { &::after { content: "|"; } } - form { - display: flex; - align-items: center; - - input[type="text"] { min-width: 10em; } + form input[type="text"] { + width: 12rem; + padding: 0.25rem 0.5rem; + font-size: 1rem; + border-radius: 0.25rem; } } } .nav-tools, .nav-help { - display: flex; - padding: 0; background: unset; border: none; gap: 0; - li { - a { - display: flex; - align-items: center; - height: 100%; - gap: 0.25em; + li a { + gap: 0.25rem; - background: unset; - font-size: 1.05em; - padding: 0 0.75em; - text-align: unset; - white-space: nowrap; - border-radius: 0; - } + background: unset; + text-align: unset; + border-radius: 0; } } .nav-tools { - - // Otherwise help gets layered above it - // When the viewport is narrow (but not mobile) + background: themed("color-foreground"); z-index: 1; - background: var(--color-background); + border-radius: 0 0.25rem 0.25rem 0; + margin-right: 0.25rem; li { a { + padding: 0 0.5rem; + i { color: themed("color-link"); } &:hover i { color: themed("color-link-hover"); } } @@ -428,17 +455,8 @@ nav.navigation, html.nav-toggled nav.navigation { a span { display: none; } } } - - #nav-themes, #nav-settings { display: none; } - @include window-larger-than(875px) { - #nav-themes, #nav-settings { display: flex; } - } - - #nav-account span { display: none; } - @include window-larger-than(930px) { - #nav-account span { display: unset; } - } } + .nav-help { // At small resolutions, overflow can @@ -446,50 +464,104 @@ nav.navigation, html.nav-toggled nav.navigation { overflow: hidden; li a img { display: none; } - li.current a { background-color: themed("color-foreground"); } + } - #nav-discord, #nav-subscribestar { display: none; } - @include window-larger-than(1150px) { - #nav-discord, #nav-subscribestar { display: flex; } + .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; + } + } + } } } - .nav-primary, .nav-tools, .nav-help { box-shadow: unset; } + // 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 body.c-static.a-home { - @include window-larger-than(800px) { + @include window-larger-than(50rem) { 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.5em; + padding: 0 0 0.5rem; - #nav-subscribestar, #nav-discord, #nav-themes, #nav-settings { - display: none; - } + #nav-subscribestar, #nav-discord, .nav-secondary, .nav-tools { display: none; } } // Match the background colors - nav.navigation, menu.nav-logo, menu.nav-secondary { + nav.navigation, menu.nav-logo, menu.nav-secondary, menu.nav-controls { background: unset; } menu.nav-tools { 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(800px) { + @include window-smaller-than(50rem) { // Only show the primary navbar on mobile // since the secondary is empty anyways nav.navigation { diff --git a/app/views/layouts/_nav.html.erb b/app/views/layouts/_nav.html.erb index e259b36d6..ea0e84561 100644 --- a/app/views/layouts/_nav.html.erb +++ b/app/views/layouts/_nav.html.erb @@ -3,51 +3,51 @@ - + <%= render "layouts/main_links" %> - - + + - "> + "> <%= yield :secondary_links %> - - - + + + <% if CurrentUser.is_anonymous? %> - <%= link_to(new_session_path, class: "nav-tools-login") do %> - - Sign in - <% end %> + + + <% else %> - <%= link_to(user_path(CurrentUser.user), class: "nav-tools-login") do %> - - Profile - <% end %> + <%= simple_avatar(CurrentUser.user, named: true, class: "nav-controls-profile collapse-2") %> <% end %> - "> + "> <%= decorated_nav_link_to("Themes", "fas fa-swatchbook", theme_path, class: "nav-tools-themes") %> - <% if CurrentUser.is_anonymous? %> - <%= decorated_nav_link_to("Sign in", "fas fa-sign-in-alt", new_session_path, class: "nav-tools-login") %> - <% else %> + <% unless CurrentUser.is_anonymous? %> <%= 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", user_path(CurrentUser.user), class: "nav-tools-login") %> <% end %> - "> + "> <%= 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") %> - <% if !CurrentUser.is_anonymous? %> - <%= custom_image_nav_link_to("Discord", "discord.com.png", discord_get_path, class: "nav-help-discord") %> + <% unless CurrentUser.is_anonymous? %> + <%= custom_image_nav_link_to("Discord", "discord.com.png", discord_get_path, class: "nav-help-discord collapse-1") %> <% end %> - <%= custom_image_nav_link_to("SubscribeStar", "subscribestar.adult.png", subscribestar_path, class: "nav-help-subscribestar") %> + <%= custom_image_nav_link_to("SubscribeStar", "subscribestar.adult.png", subscribestar_path, class: "nav-help-subscribestar collapse-1") %> <%= nav_link_to("More", site_map_path, class: "nav-help-map") %> diff --git a/app/views/layouts/blank.html.erb b/app/views/layouts/blank.html.erb index 40b65f21f..2ac1ca296 100644 --- a/app/views/layouts/blank.html.erb +++ b/app/views/layouts/blank.html.erb @@ -10,8 +10,11 @@ <% if CurrentUser.is_anonymous? %> <%= render "static/guest_warning" %> <% end %> +
<%= yield :layout %>
+ + <%= render "static/deferred_posts" %> <% end %> From 09b46b0766318bfb3a34178e79d34056fed376e2 Mon Sep 17 00:00:00 2001 From: Tarrgon <61888458+Tarrgon@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:26:44 -0500 Subject: [PATCH 15/23] [Comments] Fix an error on the index page caused by hidden comments (#837) --- app/views/comments/partials/show/_comment.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/comments/partials/show/_comment.html.erb b/app/views/comments/partials/show/_comment.html.erb index 5c1db74cd..86aff0df3 100644 --- a/app/views/comments/partials/show/_comment.html.erb +++ b/app/views/comments/partials/show/_comment.html.erb @@ -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) %>
" data-post-id="<%= comment.post_id %>" 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 %>" From af6ab947d13cbcd3fdb5ee8d2dcfffdecec2bdbd Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 21:42:12 -0800 Subject: [PATCH 16/23] [Pagination] Rework the pagination styles (#886) Now with mobile layouts! --- app/helpers/icon_helper.rb | 29 +++++ app/helpers/pagination_helper.rb | 93 +++++++++------- app/javascript/src/javascripts/paginator.js | 2 +- app/javascript/src/javascripts/shortcuts.js | 1 + .../src/styles/common/paginator.scss | 103 ++++++++++++++---- .../src/styles/specific/post_index.scss | 4 - 6 files changed, 168 insertions(+), 64 deletions(-) create mode 100644 app/helpers/icon_helper.rb diff --git a/app/helpers/icon_helper.rb b/app/helpers/icon_helper.rb new file mode 100644 index 000000000..1b9b7e7dc --- /dev/null +++ b/app/helpers/icon_helper.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module IconHelper + PATHS = { + chevron_left: %(), + chevron_right: %(), + ellipsis: %(), + }.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 diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index 8cec434ea..2e65d042f 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -2,17 +2,23 @@ module PaginationHelper def sequential_paginator(records) - with_paginator_wrapper do + tag.div(class: "paginator") do return "" if records.try(:none?) 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 - unless records.is_last_page? - html << tag.li(link_to("Next >", nav_params_for("b#{records[-1].id}"), rel: "next", id: "paginator-next", data: { shortcut: "d right" })) + # Next + 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 + html end end @@ -22,64 +28,73 @@ module PaginationHelper return sequential_paginator(records) 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 - icon_left = tag.i(class: "fa-solid fa-chevron-left") - if records.current_page >= 2 - 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" }) } - else - html << tag.li(class: "arrow") { tag.span(icon_left) } + + # Previous + has_prev = records.current_page < 2 + 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 + concat svg_icon(:chevron_left) + concat tag.span("Prev") end - paginator_pages(records).each do |page| - html << numbered_paginator_item(page, records) + # Break + html << tag.div(class: "break") + + # Numbered + paginator_pages(records).each do |page, klass| + html << numbered_paginator_item(page, klass, records) end - icon_right = tag.i(class: "fa-solid fa-chevron-right") - if 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" }) } - else - html << tag.li(class: "arrow") { tag.span(icon_right) } + # Next + has_next = records.current_page >= records.total_pages + 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 + concat tag.span("Next") + concat svg_icon(:chevron_right) end + html end end private - def with_paginator_wrapper(&) - tag.div(class: "paginator") do - tag.menu(&) - end - end - def paginator_pages(records) - window = 4 + small_window = 2 + large_window = 4 last_page = [records.total_pages, records.max_numbered_pages].min - left = [2, records.current_page - window].max - right = [records.current_page + window, last_page - 1].min + left_sm = [2, records.current_page - small_window].max + 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 - [ - 1, - ("..." unless left == 2), - (left..right).to_a, - ("..." unless right == last_page - 1), - (last_page unless last_page <= 1), - ].flatten.compact + result = [ + [1, "first"], + ] + result.push([0, "spacer"]) unless left_lg == 2 + (left_lg..right_lg).each do |page| + result.push([page, small_range.member?(page) ? "sm" : "lg"]) + end + result.push([0, "spacer"]) unless right_lg == last_page - 1 + result.push([last_page, "last"]) unless last_page <= 1 + + result end - def numbered_paginator_item(page, records) + def numbered_paginator_item(page, klass, records) return "" if page.to_i > records.max_numbered_pages html = "".html_safe - if page == "..." - html << tag.li(class: "more") { link_to(tag.i(class: "fa-solid fa-ellipsis"), nav_params_for(0)) } + if page == 0 + html << link_to(svg_icon(:ellipsis), nav_params_for(0), class: "spacer") elsif page == records.current_page - html << tag.li(class: "current-page") { tag.span(page) } + html << tag.span(page, class: "page current") 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 + html end diff --git a/app/javascript/src/javascripts/paginator.js b/app/javascript/src/javascripts/paginator.js index 4abe73237..434750a2c 100644 --- a/app/javascript/src/javascripts/paginator.js +++ b/app/javascript/src/javascripts/paginator.js @@ -12,7 +12,7 @@ Paginator.init_fasttravel = function (button) { }; $(() => { - for (const one of $(".paginator li.more a").get()) + for (const one of $(".paginator a.spacer").get()) Paginator.init_fasttravel($(one)); }); diff --git a/app/javascript/src/javascripts/shortcuts.js b/app/javascript/src/javascripts/shortcuts.js index 4e3aa6e56..869c2f136 100644 --- a/app/javascript/src/javascripts/shortcuts.js +++ b/app/javascript/src/javascripts/shortcuts.js @@ -28,6 +28,7 @@ Shortcuts.initialize_data_shortcuts = function () { Shortcuts.keydown(keys, namespace, event => { const e = $(`[data-shortcut="${keys}"]`).get(0); + if ($e.data("disabled")) return; if ($e.is("input, textarea")) { $e.trigger("focus").selectEnd(); } else { diff --git a/app/javascript/src/styles/common/paginator.scss b/app/javascript/src/styles/common/paginator.scss index c006084b6..a3d74c076 100644 --- a/app/javascript/src/styles/common/paginator.scss +++ b/app/javascript/src/styles/common/paginator.scss @@ -1,35 +1,98 @@ +.paginator { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + background-color: themed("color-foreground"); + border-radius: 0.25rem; -div.paginator { - display: block; - padding: 2em 0 1em 0; - text-align: center; - clear: both; - - menu { + & > a, & > span { display: flex; + box-sizing: border-box; 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 { - margin: 0 0.25em; - padding: 0.25em 0.75em; + & > a[data-disabled="true"] { + color: var(--color-text); + pointer-events: none; + } + + // 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 { - background: $paginator-hover-background; - color: $paginator-hover-color; + .break { display: none; } + .spacer { + padding: inherit; + svg { transform: unset; } } - &.more { - color: $paginator-more-color; + .prev { margin-right: 1rem; } + .next { margin-left: 1rem; } + } + + @include window-larger-than(50rem) { + a, span { + padding: 0.75rem 0.5rem; + font-size: 0.95rem; } - span { - margin: 0 0.25em; - padding: 0.25em 0.75em; - font-weight: bold; + .prev, .next { + span { display: none; } } } + + @include window-larger-than(65rem) { + a.page.lg { display: flex; } + } } diff --git a/app/javascript/src/styles/specific/post_index.scss b/app/javascript/src/styles/specific/post_index.scss index da8caad4f..5859ae3de 100644 --- a/app/javascript/src/styles/specific/post_index.scss +++ b/app/javascript/src/styles/specific/post_index.scss @@ -33,10 +33,6 @@ display: flex; flex-flow: column; gap: 1em; - - .paginator { - padding: 1em 0; - } } } From 200f064e2d1bfc282e8260b1e84bb600cf3a425e Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 21:43:52 -0800 Subject: [PATCH 17/23] [Misc] Remove debug output (#885) --- app/javascript/src/javascripts/home.js | 3 --- app/javascript/src/javascripts/news_updates.js | 1 - app/javascript/src/javascripts/post_mode_menu.js | 2 -- app/javascript/src/javascripts/thumbnails.js | 5 ----- 4 files changed, 11 deletions(-) diff --git a/app/javascript/src/javascripts/home.js b/app/javascript/src/javascripts/home.js index ff4010cbe..612c451a0 100644 --- a/app/javascript/src/javascripts/home.js +++ b/app/javascript/src/javascripts/home.js @@ -6,17 +6,14 @@ Home.init = function () { const $form = $("#home-search-form"); const $tags = $("#tags"); - console.log("init"); let isEmpty = !$tags.val(); let wasEmpty = isEmpty; if (isEmpty) $form.addClass("empty"); - console.log("input", isEmpty, wasEmpty); $tags.on("input", () => { wasEmpty = isEmpty; isEmpty = !$tags.val(); - console.log("input", isEmpty, wasEmpty); if (isEmpty && !wasEmpty) $form.addClass("empty"); else if (!isEmpty && wasEmpty) $form.removeClass("empty"); diff --git a/app/javascript/src/javascripts/news_updates.js b/app/javascript/src/javascripts/news_updates.js index aae44cbd1..aabe7912c 100644 --- a/app/javascript/src/javascripts/news_updates.js +++ b/app/javascript/src/javascripts/news_updates.js @@ -10,7 +10,6 @@ NewsUpdate.initialize = function () { let newsOpen = false; $("#news-header, #news-show").on("click", (event) => { event.preventDefault(); - console.log("click"); newsOpen = !newsOpen; $("#news").toggleClass("open", newsOpen); diff --git a/app/javascript/src/javascripts/post_mode_menu.js b/app/javascript/src/javascripts/post_mode_menu.js index a94151fdf..31415a3a8 100644 --- a/app/javascript/src/javascripts/post_mode_menu.js +++ b/app/javascript/src/javascripts/post_mode_menu.js @@ -35,12 +35,10 @@ PostModeMenu.change_tag_script = function (e) { e.preventDefault(); const newScriptID = Number(e.key); - console.log(newScriptID, LStorage.Posts.TagScript.ID); if (!newScriptID || newScriptID == LStorage.Posts.TagScript.ID) return; LStorage.Posts.TagScript.ID = newScriptID; - console.log("settings", LStorage.Posts.TagScript.ID, LStorage.Posts.TagScript.Content); $("#tag-script-field").val(LStorage.Posts.TagScript.Content); PostModeMenu.show_notice(newScriptID); }; diff --git a/app/javascript/src/javascripts/thumbnails.js b/app/javascript/src/javascripts/thumbnails.js index 0d8be6576..e1ffeed0b 100644 --- a/app/javascript/src/javascripts/thumbnails.js +++ b/app/javascript/src/javascripts/thumbnails.js @@ -11,21 +11,16 @@ Thumbnails.initialize = function () { // Avatar special case for (const post of $(".simple-avatar")) { const $post = $(post); - console.log(1, $post); const postID = $post.data("id"); if (!postID) continue; - console.log(2, postID); const postData = postsData[postID]; if (!postData || !postData["preview_url"]) continue; - console.log(3, postData); - console.log(3.55, $post.data("name")); $("") .attr("src", postData["preview_url"]) .appendTo($post.find("span.simple-avatar-image")); - console.log(4, "done"); continue; } From 76f64b2229b102ca731e9720c7386c74e0613153 Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 21:57:46 -0800 Subject: [PATCH 18/23] [Pagination] Fix spacing on desktop (#887) --- app/javascript/src/styles/common/paginator.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/javascript/src/styles/common/paginator.scss b/app/javascript/src/styles/common/paginator.scss index a3d74c076..6ffd9a446 100644 --- a/app/javascript/src/styles/common/paginator.scss +++ b/app/javascript/src/styles/common/paginator.scss @@ -6,6 +6,9 @@ background-color: themed("color-foreground"); border-radius: 0.25rem; + width: max-content; + margin: 0 auto; + & > a, & > span { display: flex; box-sizing: border-box; From 17bac8c93f9defffbefdf9c7132f92fee1d58afa Mon Sep 17 00:00:00 2001 From: Cinder Date: Mon, 3 Feb 2025 22:46:17 -0800 Subject: [PATCH 19/23] [UI] Fix an overflow issue in the footer (#888) --- app/javascript/src/styles/common/_footer.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/src/styles/common/_footer.scss b/app/javascript/src/styles/common/_footer.scss index 53fe677e4..31c2c6935 100644 --- a/app/javascript/src/styles/common/_footer.scss +++ b/app/javascript/src/styles/common/_footer.scss @@ -66,6 +66,7 @@ footer.footer-wrapper { @include window-larger-than(450px) { .footer-grid { grid-template-areas: "logo left right"; + padding: 1rem 0; } .footer-logo { From 7df12ce4436a80047a3e68f1ccd7c70df129ea30 Mon Sep 17 00:00:00 2001 From: Cinder Date: Tue, 4 Feb 2025 20:59:00 -0800 Subject: [PATCH 20/23] [Misc] Fix a sitemap overflow issue (#889) --- .../src/styles/specific/site_map.scss | 16 +++--------- app/views/static/site_map.html.erb | 26 +++++++++---------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/app/javascript/src/styles/specific/site_map.scss b/app/javascript/src/styles/specific/site_map.scss index 562e4f6fb..7dfccf9f8 100644 --- a/app/javascript/src/styles/specific/site_map.scss +++ b/app/javascript/src/styles/specific/site_map.scss @@ -1,20 +1,12 @@ - - div#c-static { div#a-site-map { - width: 80em; + display: flex; + flex-flow: row wrap; + max-width: 80em; section { width: 20em; - float: left; - - h1 { - font-size: $h3-size; - } - - ul { - margin-bottom: 1.5em; - } + ul { margin-bottom: 1.5em; } } } } diff --git a/app/views/static/site_map.html.erb b/app/views/static/site_map.html.erb index b906a5f3d..583fef41d 100644 --- a/app/views/static/site_map.html.erb +++ b/app/views/static/site_map.html.erb @@ -2,7 +2,7 @@
    -
  • Posts

  • +
  • Posts

  • <%= link_to("Listing", posts_path) %>
  • <%= link_to("Upload", new_upload_path) %>
  • <% if CurrentUser.is_janitor? %> @@ -17,7 +17,7 @@
  • <%= link_to("Help", help_page_path(id: "posts")) %>
    -
  • Post Events

  • +
  • Post Events

  • <%= link_to("Listing", post_events_path) %>
  • <%= link_to("Tag Changes", post_versions_path) %>
  • <%= link_to("Approvals", post_approvals_path) %>
  • @@ -25,7 +25,7 @@
  • <%= link_to("Replacements", post_replacements_path) %>
    -
  • Tools

  • +
  • Tools

  • <%= link_to("News Updates", news_updates_path) %>
  • <%= link_to("Mascots", mascots_path) %>
  • <%= link_to("Source Code", Danbooru.config.source_code_url) %>
  • @@ -35,7 +35,7 @@
  • <%= link_to("DB Export", "/db_export/") %>
    -
  • Artists

  • +
  • Artists

  • <%= link_to("Listing", artists_path) %>
  • <%= link_to("Avoid Posting Entries", avoid_postings_path) %>
  • <%= link_to("Avoid Posting List", avoid_posting_static_path) %>
  • @@ -45,7 +45,7 @@
    -
  • Tags

  • +
  • Tags

  • <%= link_to("Listing", tags_path) %>
  • <%= link_to("Aliases", tag_aliases_path) %>
  • <%= link_to("Implications", tag_implications_path) %>
  • @@ -53,13 +53,13 @@
  • <%= link_to("Help", help_page_path(id: "tags")) %>
    -
  • Notes

  • +
  • Notes

  • <%= link_to("Listing", notes_path) %>
  • <%= link_to("Changes", note_versions_path) %>
  • <%= link_to("Help", help_page_path(id: "notes")) %>
    -
  • Pools

  • +
  • Pools

  • <%= link_to("Listing", gallery_pools_path) %>
  • <%= link_to("Changes", pool_versions_path) %>
  • <%= link_to("Help", help_page_path(id: "pools")) %>
  • @@ -67,30 +67,30 @@
    -
  • Comments

  • +
  • Comments

  • <%= link_to("Listing", comments_path) %>
  • <%= link_to("Help", help_page_path(id: "comments")) %>
    -
  • Forum

  • +
  • Forum

  • <%= link_to("Listing", forum_topics_path) %>
  • <%= link_to("Help", help_page_path(id: "forum")) %>
    -
  • Wiki

  • +
  • Wiki

  • <%= link_to("Listing", wiki_pages_path) %>
  • <%= link_to("Changes", wiki_page_versions_path) %>
  • <%= link_to("Help", help_page_path(id: "wiki")) %>
    -
  • Blips

  • +
  • Blips

  • <%= link_to("Listing", blips_path) %>
  • <%= link_to("Help", help_page_path(id: "blips")) %>
    -
  • Users

  • +
  • Users

  • <%= link_to("Listing", users_path) %>
  • <%= link_to("Bans", bans_path) %>
  • <% if CurrentUser.is_anonymous? %> @@ -105,7 +105,7 @@
  • <%= link_to("Help", help_page_path(id: "accounts")) %>
    -
  • Admin

  • +
  • Admin

  • <% if CurrentUser.is_moderator? %>
  • <%= link_to("Mod Dashboard", moderator_dashboard_path) %>
  • <% end %> From ef3f43cf27e7ceee22e605e2840b120b07ab6fca Mon Sep 17 00:00:00 2001 From: Cinder Date: Tue, 4 Feb 2025 21:05:58 -0800 Subject: [PATCH 21/23] [Posts] Touch up the search index page UI (#890) --- app/helpers/icon_helper.rb | 4 + app/javascript/src/javascripts/post_search.js | 15 ++ .../src/javascripts/utility/storage.js | 3 + app/javascript/src/styles/base.scss | 3 + .../src/styles/common/_standard_elements.scss | 59 +++++++ .../styles/common/_standard_variables.scss | 21 +++ .../src/styles/specific/post_index.scss | 150 ++++++++++++++++-- .../src/styles/specific/post_mode_menu.scss | 2 +- app/views/layouts/_theme_include.html.erb | 28 ++-- app/views/posts/index.html.erb | 1 + .../posts/partials/index/_controls.html.erb | 6 + 11 files changed, 263 insertions(+), 29 deletions(-) create mode 100644 app/javascript/src/styles/common/_standard_elements.scss create mode 100644 app/javascript/src/styles/common/_standard_variables.scss create mode 100644 app/views/posts/partials/index/_controls.html.erb diff --git a/app/helpers/icon_helper.rb b/app/helpers/icon_helper.rb index 1b9b7e7dc..ab216e857 100644 --- a/app/helpers/icon_helper.rb +++ b/app/helpers/icon_helper.rb @@ -2,9 +2,13 @@ module IconHelper PATHS = { + # Pagination chevron_left: %(), chevron_right: %(), ellipsis: %(), + + # Posts + fullscreen: %(), }.freeze def svg_icon(name, *args) diff --git a/app/javascript/src/javascripts/post_search.js b/app/javascript/src/javascripts/post_search.js index 2b519ae98..a3e3a20cc 100644 --- a/app/javascript/src/javascripts/post_search.js +++ b/app/javascript/src/javascripts/post_search.js @@ -1,4 +1,5 @@ import LStorage from "./utility/storage"; +import Page from "./utility/page"; const PostSearch = {}; @@ -10,6 +11,8 @@ PostSearch.init = function () { $(".wiki-excerpt").each((index, element) => { PostSearch.initialize_wiki_preview($(element)); }); + + PostSearch.initialize_controls(); }; PostSearch.initialize_input = function ($form) { @@ -55,7 +58,19 @@ PostSearch.initialize_wiki_preview = function ($preview) { }); }; +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(); }); diff --git a/app/javascript/src/javascripts/utility/storage.js b/app/javascript/src/javascripts/utility/storage.js index 1f1c45d7a..5d910a66a 100644 --- a/app/javascript/src/javascripts/utility/storage.js +++ b/app/javascript/src/javascripts/utility/storage.js @@ -88,6 +88,9 @@ LStorage.Posts = { /** @returns {boolean} True if the wiki excerpt should be visible */ 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); diff --git a/app/javascript/src/styles/base.scss b/app/javascript/src/styles/base.scss index 05920d6db..6ec5cfc2f 100644 --- a/app/javascript/src/styles/base.scss +++ b/app/javascript/src/styles/base.scss @@ -9,6 +9,9 @@ @import "base/links"; @import "base/fontawesome"; +@import "common/standard_variables"; +@import "common/standard_elements"; + @import "common/footer"; @import "common/helper_classes"; @import "common/helper_palette"; diff --git a/app/javascript/src/styles/common/_standard_elements.scss b/app/javascript/src/styles/common/_standard_elements.scss new file mode 100644 index 000000000..843738416 --- /dev/null +++ b/app/javascript/src/styles/common/_standard_elements.scss @@ -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"); } + } + } +} diff --git a/app/javascript/src/styles/common/_standard_variables.scss b/app/javascript/src/styles/common/_standard_variables.scss new file mode 100644 index 000000000..8504ec0b5 --- /dev/null +++ b/app/javascript/src/styles/common/_standard_variables.scss @@ -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); } diff --git a/app/javascript/src/styles/specific/post_index.scss b/app/javascript/src/styles/specific/post_index.scss index 5859ae3de..67b6bfcfa 100644 --- a/app/javascript/src/styles/specific/post_index.scss +++ b/app/javascript/src/styles/specific/post_index.scss @@ -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 { display: grid; @@ -7,24 +26,46 @@ "sidebar"; grid-template-columns: 1fr; grid-template-rows: min-content 1fr min-content; - gap: 1em; + + flex: 1; // See Exhibit A // 1. Searchbox .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 { font-size: $h3-size; } + + .search-controls { + display: none; + flex-flow: column; + } } // 2. Content .content { + display: flex; // See Exhibit A + flex-flow: column; + 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 #edit-dialog textarea { - margin-bottom: 0.25em; + margin-bottom: 0.25rem; } // Actual content area: @@ -32,7 +73,14 @@ .post-index-gallery { display: flex; flex-flow: column; - gap: 1em; + gap: 1rem; + + flex: 1; // See Exhibit A + + .posts-container { + flex: 1; // See Exhibit A + grid-auto-rows: min-content; + } } } @@ -44,6 +92,11 @@ flex-flow: column; 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-box-mode, #mode-box #set-id { width: 100%; @@ -59,12 +112,76 @@ // Desktop .post-index { - @include window-larger-than(800px) { + @include window-larger-than(50rem) { grid-template-areas: "search content" "sidebar content"; - grid-template-columns: 15em 1fr; + grid-template-columns: 14rem 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; + } + } } } @@ -76,12 +193,13 @@ flex-flow: column; position: relative; - background: var(--color-section); + background: themed("color-section"); + border-radius: 0.25rem; // header h3 { cursor: pointer; - padding: 0.5em 1em 0.5em 1.5em; + padding: 0.5rem 1rem 0.5rem 1.5rem; &::after { @include font-awesome-icon; @@ -92,21 +210,21 @@ position: absolute; top: 0; left: 0; - padding: 0.5em; + padding: 0.5rem; } } // body .styled-dtext { - background: linear-gradient(to top, var(--color-section), var(--color-text)); + background: linear-gradient(to top, themed("color-section"), themed("color-text")); -webkit-background-clip: text; background-clip: text; color: transparent; - max-height: 0em; + max-height: 0rem; max-width: 50rem; overflow: hidden; - padding: 0 0.5em; + padding: 0 0.5rem; transition: max-height 0.25s; @@ -134,7 +252,7 @@ left: 0; right: 0; - height: 3em; + height: 3rem; max-width: 50rem; box-sizing: border-box; @@ -143,16 +261,16 @@ visibility: hidden; span { - padding: 0.5em 1em; - background: var(--color-section); - border-radius: 6px; + padding: 0.5rem 1rem; + background: themed("color-section"); + border-radius: 0.25rem; } } &.open{ .wiki-excerpt-toggle::after { transform: rotate(90deg); } .styled-dtext { - max-height: 10em; + max-height: 10rem; } .wiki-excerpt-readmore { visibility: visible; } } diff --git a/app/javascript/src/styles/specific/post_mode_menu.scss b/app/javascript/src/styles/specific/post_mode_menu.scss index 6e02eff31..467f43954 100644 --- a/app/javascript/src/styles/specific/post_mode_menu.scss +++ b/app/javascript/src/styles/specific/post_mode_menu.scss @@ -20,7 +20,7 @@ $modes: ( ); @each $mode, $color in $modes { - #page[data-mode-menu="#{$mode}"] { + #page[data-mode-menu="#{$mode}"] .content { background-color: $color; } } diff --git a/app/views/layouts/_theme_include.html.erb b/app/views/layouts/_theme_include.html.erb index cf3791804..bf5b3eb85 100644 --- a/app/views/layouts/_theme_include.html.erb +++ b/app/views/layouts/_theme_include.html.erb @@ -1,19 +1,23 @@ <%= javascript_tag nonce: true do -%> (function() { try { - var theme = localStorage.getItem('theme') || 'hexagon'; - var extra = localStorage.getItem('theme-extra') || 'hexagon'; - var sheader = localStorage.getItem('theme-sheader') || false; - var forumnotif = localStorage.getItem('theme-forumnotif') || false; - var palette = localStorage.getItem('theme-palette') || 'default'; - var nav = localStorage.getItem('theme-nav') || 'top'; + const values = { + // Theme + "th-main": localStorage.getItem("theme") || "hexagon", + "th-extra": localStorage.getItem("theme-extra") || "hexagon", + "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; - b.setAttribute('data-th-main', theme); - b.setAttribute('data-th-extra', extra); - b.setAttribute('data-th-sheader', sheader); - b.setAttribute('data-th-forumnotif', forumnotif); - b.setAttribute('data-th-palette', palette); - b.setAttribute('data-th-nav', nav); + for (const [name, value] of Object.entries(values)) { + b.setAttribute("data-" + name, value); + } } catch(e) {} })(); <% end -%> diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb index e1d7b60d5..1d452ab0d 100644 --- a/app/views/posts/index.html.erb +++ b/app/views/posts/index.html.erb @@ -3,6 +3,7 @@