From d640c0356ebda19e1940c8c100613881243dc4b2 Mon Sep 17 00:00:00 2001 From: Cytrogen Date: Wed, 25 Mar 2026 21:31:57 -0400 Subject: [PATCH] Fix merged PR bugs and rewrite reduced-motion to pure CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PR #64: fix typo "ad" → "add" in bookmark folder empty message - PR #64: fix reducer using wrong key ('folder'/'title' → 'name') - PR #84: implement confirm_unfollow using local_settings instead of server-side unfollowModal global - PR #104: replace JS body-class approach with native CSS @media (prefers-reduced-motion) queries across all SCSS files - Wide view: columns fill available space, content centered at 600px --- .../glitch/containers/account_container.jsx | 33 +++++----- .../glitch/features/bookmark_folder/index.jsx | 2 +- app/javascript/flavours/glitch/main.jsx | 2 - .../glitch/reducers/bookmark_folder_editor.js | 2 +- .../glitch/styles/components/columns.scss | 3 +- .../styles/components/compose_form.scss | 10 +-- .../glitch/styles/components/media.scss | 6 +- .../glitch/styles/components/misc.scss | 34 +++++----- .../glitch/styles/components/status.scss | 33 +++++++--- .../flavours/glitch/utils/accessibility.js | 26 -------- .../styles/mastodon/components.scss | 64 +++++++++++-------- 11 files changed, 111 insertions(+), 104 deletions(-) delete mode 100644 app/javascript/flavours/glitch/utils/accessibility.js diff --git a/app/javascript/flavours/glitch/containers/account_container.jsx b/app/javascript/flavours/glitch/containers/account_container.jsx index ac3316b61024df947b2a7b4996f9ede131f67f30..ab4852e8a883dc0461700b78c4791cfe377bc32d 100644 --- a/app/javascript/flavours/glitch/containers/account_container.jsx +++ b/app/javascript/flavours/glitch/containers/account_container.jsx @@ -13,7 +13,7 @@ import { import { openModal } from "flavours/glitch/actions/modal"; import { initMuteModal } from "flavours/glitch/actions/mutes"; import Account from "flavours/glitch/components/account"; -import { unfollowModal } from "flavours/glitch/initial_state"; + import { makeGetAccount } from "flavours/glitch/selectors"; const messages = defineMessages({ @@ -33,22 +33,25 @@ const makeMapStateToProps = () => { const mapDispatchToProps = (dispatch, { intl }) => ({ onFollow (account) { - if (account.getIn(["relationship", "following"]) || account.getIn(["relationship", "requested"])) { - if (unfollowModal) { - dispatch(openModal({ - modalType: "CONFIRM", - modalProps: { - message: @{account.get("acct")} }} />, - confirm: intl.formatMessage(messages.unfollowConfirm), - onConfirm: () => dispatch(unfollowAccount(account.get("id"))), - }, - })); + dispatch((_, getState) => { + const state = getState(); + if (account.getIn(["relationship", "following"]) || account.getIn(["relationship", "requested"])) { + if (state.getIn(["local_settings", "confirm_unfollow"])) { + dispatch(openModal({ + modalType: "CONFIRM", + modalProps: { + message: @{account.get("acct")} }} />, + confirm: intl.formatMessage(messages.unfollowConfirm), + onConfirm: () => dispatch(unfollowAccount(account.get("id"))), + }, + })); + } else { + dispatch(unfollowAccount(account.get("id"))); + } } else { - dispatch(unfollowAccount(account.get("id"))); + dispatch(followAccount(account.get("id"))); } - } else { - dispatch(followAccount(account.get("id"))); - } + }); }, onBlock (account) { diff --git a/app/javascript/flavours/glitch/features/bookmark_folder/index.jsx b/app/javascript/flavours/glitch/features/bookmark_folder/index.jsx index 1c2534c4a4af955f5dea42777154d724ab4b25d9..53912fbb86a0678445a9543a38d529adfbaa9d9a 100644 --- a/app/javascript/flavours/glitch/features/bookmark_folder/index.jsx +++ b/app/javascript/flavours/glitch/features/bookmark_folder/index.jsx @@ -88,7 +88,7 @@ class BookmarkFolder extends ImmutablePureComponent { const pinned = !!columnId; const name = folder ? folder.get('name') : folderId; - const emptyMessage = ; + const emptyMessage = ; if (typeof folder === 'undefined') { return ( diff --git a/app/javascript/flavours/glitch/main.jsx b/app/javascript/flavours/glitch/main.jsx index 58e31a101346ccb5a32678bf98b952616490a9aa..f30e6fb4d33e5525293f71252863641d67e564b8 100644 --- a/app/javascript/flavours/glitch/main.jsx +++ b/app/javascript/flavours/glitch/main.jsx @@ -5,7 +5,6 @@ import Mastodon from "flavours/glitch/containers/mastodon"; import { me } from "flavours/glitch/initial_state"; import * as perf from "flavours/glitch/performance"; import ready from "flavours/glitch/ready"; -import { setReducedMotionBodyClass } from "flavours/glitch/utils/accessibility"; import { store } from "flavours/glitch/store"; /** @@ -13,7 +12,6 @@ import { store } from "flavours/glitch/store"; */ function main() { perf.start("main()"); - setReducedMotionBodyClass(); return ready(async () => { const mountNode = document.getElementById("mastodon"); diff --git a/app/javascript/flavours/glitch/reducers/bookmark_folder_editor.js b/app/javascript/flavours/glitch/reducers/bookmark_folder_editor.js index 5f2fd53db50508f62fac63f75a499c61001fe0fc..8740b2b9dc2dd6700a4f783322636196ad33183b 100644 --- a/app/javascript/flavours/glitch/reducers/bookmark_folder_editor.js +++ b/app/javascript/flavours/glitch/reducers/bookmark_folder_editor.js @@ -26,7 +26,7 @@ export default function listEditorReducer(state = initialState, action) { case BOOKMARK_FOLDER_EDITOR_SETUP: return state.withMutations(map => { map.set('folderId', action.folder.get('id')); - map.set('folder', action.folder.get('title')); + map.set('name', action.folder.get('name')); }); case BOOKMARK_FOLDER_EDITOR_NAME_CHANGE: return state.withMutations(map => { diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 126c68c4129627eb0890c069b9d46ee793d5cf00..3f2748062f3268a36885cddef53b1c29c42ceccc 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -393,9 +393,8 @@ $ui-header-height: 55px; overflow: hidden; .wide .columns-area:not(.columns-area--mobile) & { - flex: auto; + flex: 1 1 auto; min-width: 330px; - max-width: 400px; } > .scrollable { diff --git a/app/javascript/flavours/glitch/styles/components/compose_form.scss b/app/javascript/flavours/glitch/styles/components/compose_form.scss index 3b85dfbf15e302d8d29484f57b58514d8a5c943c..86263f119608ad57f311ee1123f3ede0aef75d9c 100644 --- a/app/javascript/flavours/glitch/styles/components/compose_form.scss +++ b/app/javascript/flavours/glitch/styles/components/compose_form.scss @@ -25,10 +25,12 @@ } } -.no-reduce-motion .spoiler-input { - transition: - height 0.4s ease, - opacity 0.4s ease; +@media (prefers-reduced-motion: no-preference) { + .spoiler-input { + transition: + height 0.4s ease, + opacity 0.4s ease; + } } .spoiler-input { diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss index 535af9d0f25ce143f891ebd0a8528057d4e978a5..22babe03e2fe411612bdb0f53af8538fe5ca5afb 100644 --- a/app/javascript/flavours/glitch/styles/components/media.scss +++ b/app/javascript/flavours/glitch/styles/components/media.scss @@ -643,7 +643,7 @@ position: relative; overflow: hidden; - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: all 100ms linear; } @@ -691,7 +691,7 @@ box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); opacity: 0; - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: opacity 100ms linear; } } @@ -760,7 +760,7 @@ background: lighten($ui-highlight-color, 8%); box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: opacity 0.1s ease; } diff --git a/app/javascript/flavours/glitch/styles/components/misc.scss b/app/javascript/flavours/glitch/styles/components/misc.scss index 6811e57fba61ab2750820f07a830040ab815710d..73d8f1235259c8e87ab51d40dc397a13f409fecc 100644 --- a/app/javascript/flavours/glitch/styles/components/misc.scss +++ b/app/javascript/flavours/glitch/styles/components/misc.scss @@ -365,16 +365,18 @@ body > [data-popper-placement] { color: $red-bookmark; } -.no-reduce-motion .icon-button.star-icon { - &.activate { - & > .fa-star { - animation: spring-rotate-in 1s linear; +@media (prefers-reduced-motion: no-preference) { + .icon-button.star-icon { + &.activate { + & > .fa-star { + animation: spring-rotate-in 1s linear; + } } - } - &.deactivate { - & > .fa-star { - animation: spring-rotate-out 1s linear; + &.deactivate { + & > .fa-star { + animation: spring-rotate-out 1s linear; + } } } } @@ -518,7 +520,7 @@ body > [data-popper-placement] { } } - .reduce-motion & { + @media (prefers-reduced-motion: reduce) { animation: none; } } @@ -1086,13 +1088,15 @@ button.icon-button.active i.fa-retweet { background-position: 0 100%; } -.reduce-motion button.icon-button i.fa-retweet, -.reduce-motion button.icon-button.active i.fa-retweet { - transition: none; -} +@media (prefers-reduced-motion: reduce) { + button.icon-button i.fa-retweet, + button.icon-button.active i.fa-retweet { + transition: none; + } -.reduce-motion button.icon-button.disabled i.fa-retweet { - color: darken($action-button-color, 13%); + button.icon-button.disabled i.fa-retweet { + color: darken($action-button-color, 13%); + } } .load-more { diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index e25636780ecab3650f276416043b075e33497e2f..82d206a6f64ecb9692d0a9a1b2178a186f210e74 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -381,6 +381,21 @@ } } +// Wide mode: constrain content width for readability, +// columns fill available space but content stays centered. +.wide .columns-area:not(.columns-area--mobile) { + .status, + .detailed-status, + .detailed-status__action-bar, + .notification-favourite, + .notification-follow, + .notification-follow-request, + .status__wrapper--filtered { + max-width: 600px; + margin-inline: auto; + } +} + .notification-favourite { .status.status-direct { background: transparent; @@ -445,16 +460,18 @@ } } -.no-reduce-motion .status__collapse-button { - &.activate { - & > .fa-angle-double-up { - animation: spring-flip-in 1s linear; +@media (prefers-reduced-motion: no-preference) { + .status__collapse-button { + &.activate { + & > .fa-angle-double-up { + animation: spring-flip-in 1s linear; + } } - } - &.deactivate { - & > .fa-angle-double-up { - animation: spring-flip-out 1s linear; + &.deactivate { + & > .fa-angle-double-up { + animation: spring-flip-out 1s linear; + } } } } diff --git a/app/javascript/flavours/glitch/utils/accessibility.js b/app/javascript/flavours/glitch/utils/accessibility.js deleted file mode 100644 index 38a38ecea7d99aec6eb2b58e74737f626d985e4a..0000000000000000000000000000000000000000 --- a/app/javascript/flavours/glitch/utils/accessibility.js +++ /dev/null @@ -1,26 +0,0 @@ -import ready from "../ready"; - -ready(() => { - setReducedMotionBodyClass(); -}); - -export function setMediaQueryBodyClass(query, className) { - if (query.matches) { - document.body.classList.add(className.true); - document.body.classList.remove(className.false); - } else { - document.body.classList.add(className.false); - document.body.classList.remove(className.true); - } -} - -export function setReducedMotionBodyClass() { - const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); - const className = { - true: "reduce-motion", - false: "no-reduce-motion", - }; - - setMediaQueryBodyClass(prefersReducedMotion, className); - prefersReducedMotion.addEventListener("change", () => setReducedMotionBodyClass(prefersReducedMotion)); -} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 32da8961f1762fc6273b991dc749fcea29993b54..f50d5ec166d3f95cd67cfa5846f6f2331d740d24 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -15,7 +15,7 @@ color: $valid-value-color; font-weight: 400; - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: opacity 200ms ease; } } @@ -717,10 +717,12 @@ body > [data-popper-placement] { } } -.no-reduce-motion .spoiler-input { - transition: - height 0.4s ease, - opacity 0.4s ease; +@media (prefers-reduced-motion: no-preference) { + .spoiler-input { + transition: + height 0.4s ease, + opacity 0.4s ease; + } } .sign-in-banner { @@ -1776,16 +1778,18 @@ a.account__display-name { color: $red-bookmark; } -.no-reduce-motion .icon-button.star-icon { - &.activate { - & > .fa-star { - animation: spring-rotate-in 1s linear; +@media (prefers-reduced-motion: no-preference) { + .icon-button.star-icon { + &.activate { + & > .fa-star { + animation: spring-rotate-in 1s linear; + } } - } - &.deactivate { - & > .fa-star { - animation: spring-rotate-out 1s linear; + &.deactivate { + & > .fa-star { + animation: spring-rotate-out 1s linear; + } } } } @@ -1946,7 +1950,7 @@ a.account__display-name { } } - .reduce-motion & { + @media (prefers-reduced-motion: reduce) { animation: none; } } @@ -3525,9 +3529,11 @@ button.icon-button.active i.fa-retweet { background-position: 0 100%; } -.reduce-motion button.icon-button i.fa-retweet, -.reduce-motion button.icon-button.active i.fa-retweet { - transition: none; +@media (prefers-reduced-motion: reduce) { + button.icon-button i.fa-retweet, + button.icon-button.active i.fa-retweet { + transition: none; + } } .status-card { @@ -4592,9 +4598,11 @@ a.status-card { } } -.no-reduce-motion .pulse-loading { - transform-origin: center center; - animation: heartbeat 1.5s ease-in-out infinite both; +@media (prefers-reduced-motion: no-preference) { + .pulse-loading { + transform-origin: center center; + animation: heartbeat 1.5s ease-in-out infinite both; + } } @keyframes shake-bottom { @@ -4629,9 +4637,11 @@ a.status-card { } } -.no-reduce-motion .shake-bottom { - transform-origin: 50% 100%; - animation: shake-bottom 0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955) 2s 2 both; +@media (prefers-reduced-motion: no-preference) { + .shake-bottom { + transform-origin: 50% 100%; + animation: shake-bottom 0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955) 2s 2 both; + } } .emoji-picker-dropdown__menu { @@ -6641,7 +6651,7 @@ a.status-card { position: relative; overflow: hidden; - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: all 100ms linear; } @@ -6689,7 +6699,7 @@ a.status-card { box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); opacity: 0; - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: opacity 100ms linear; } } @@ -6758,7 +6768,7 @@ a.status-card { background: lighten($ui-highlight-color, 8%); box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: opacity 0.1s ease; } @@ -9298,7 +9308,7 @@ noscript { inset-inline-start: 1rem; } - .no-reduce-motion & { + @media (prefers-reduced-motion: no-preference) { transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1); transform: translateZ(0); }