mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-15 08:48:15 +00:00
Merge branch 'main' into mute-prefill
This commit is contained in:
commit
cce87b7f35
|
@ -6,6 +6,8 @@ class OAuth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
before_action :store_current_location
|
||||
before_action :authenticate_resource_owner!
|
||||
|
||||
layout 'modal'
|
||||
|
||||
content_security_policy do |p|
|
||||
p.form_action(false)
|
||||
end
|
||||
|
|
|
@ -11,6 +11,8 @@ class OAuth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
layout 'admin'
|
||||
|
||||
include Localized
|
||||
|
||||
def destroy
|
||||
|
|
|
@ -18,6 +18,7 @@ import initialState, { title as siteTitle } from 'mastodon/initial_state';
|
|||
import { IntlProvider } from 'mastodon/locales';
|
||||
import { store } from 'mastodon/store';
|
||||
import { isProduction } from 'mastodon/utils/environment';
|
||||
import { BodyScrollLock } from 'mastodon/features/ui/components/body_scroll_lock';
|
||||
|
||||
const title = isProduction() ? siteTitle : `${siteTitle} (Dev)`;
|
||||
|
||||
|
@ -58,6 +59,7 @@ export default class Mastodon extends PureComponent {
|
|||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
<BodyScrollLock />
|
||||
</Router>
|
||||
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
|
|
|
@ -14,7 +14,6 @@ import MediaModal from 'mastodon/features/ui/components/media_modal';
|
|||
import { Video } from 'mastodon/features/video';
|
||||
import { IntlProvider } from 'mastodon/locales';
|
||||
import { createPollFromServerJSON } from 'mastodon/models/poll';
|
||||
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
|
||||
|
||||
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio };
|
||||
|
||||
|
@ -34,9 +33,6 @@ export default class MediaContainer extends PureComponent {
|
|||
};
|
||||
|
||||
handleOpenMedia = (media, index, lang) => {
|
||||
document.body.classList.add('with-modals--active');
|
||||
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
|
||||
|
||||
this.setState({ media, index, lang });
|
||||
};
|
||||
|
||||
|
@ -45,16 +41,10 @@ export default class MediaContainer extends PureComponent {
|
|||
const { media } = JSON.parse(components[options.componentIndex].getAttribute('data-props'));
|
||||
const mediaList = fromJS(media);
|
||||
|
||||
document.body.classList.add('with-modals--active');
|
||||
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
|
||||
|
||||
this.setState({ media: mediaList, lang, options });
|
||||
};
|
||||
|
||||
handleCloseMedia = () => {
|
||||
document.body.classList.remove('with-modals--active');
|
||||
document.documentElement.style.marginRight = '0';
|
||||
|
||||
this.setState({
|
||||
media: null,
|
||||
index: null,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import { createAppSelector, useAppSelector } from 'mastodon/store';
|
||||
|
||||
const getShouldLockBodyScroll = createAppSelector(
|
||||
[
|
||||
(state) => state.navigation.open,
|
||||
(state) => state.modal.get('stack').size > 0,
|
||||
],
|
||||
(isMobileMenuOpen: boolean, isModalOpen: boolean) =>
|
||||
isMobileMenuOpen || isModalOpen,
|
||||
);
|
||||
|
||||
/**
|
||||
* This component locks scrolling on the body when
|
||||
* `getShouldLockBodyScroll` returns true.
|
||||
*/
|
||||
|
||||
export const BodyScrollLock: React.FC = () => {
|
||||
const shouldLockBodyScroll = useAppSelector(getShouldLockBodyScroll);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
document.documentElement.classList.toggle(
|
||||
'has-modal',
|
||||
shouldLockBodyScroll,
|
||||
);
|
||||
}, [shouldLockBodyScroll]);
|
||||
|
||||
return null;
|
||||
};
|
|
@ -20,7 +20,6 @@ import {
|
|||
IgnoreNotificationsModal,
|
||||
AnnualReportModal,
|
||||
} from 'mastodon/features/ui/util/async-components';
|
||||
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
|
||||
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
|
||||
|
@ -90,20 +89,6 @@ export default class ModalRoot extends PureComponent {
|
|||
backgroundColor: null,
|
||||
};
|
||||
|
||||
getSnapshotBeforeUpdate () {
|
||||
return { visible: !!this.props.type };
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps, prevState, { visible }) {
|
||||
if (visible) {
|
||||
document.body.classList.add('with-modals--active');
|
||||
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
|
||||
} else {
|
||||
document.body.classList.remove('with-modals--active');
|
||||
document.documentElement.style.marginRight = '0';
|
||||
}
|
||||
}
|
||||
|
||||
setBackgroundColor = color => {
|
||||
this.setState({ backgroundColor: color });
|
||||
};
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { isMobile } from '../is_mobile';
|
||||
|
||||
let cachedScrollbarWidth: number | null = null;
|
||||
|
||||
const getActualScrollbarWidth = () => {
|
||||
const outer = document.createElement('div');
|
||||
outer.style.visibility = 'hidden';
|
||||
outer.style.overflow = 'scroll';
|
||||
document.body.appendChild(outer);
|
||||
|
||||
const inner = document.createElement('div');
|
||||
outer.appendChild(inner);
|
||||
|
||||
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
||||
outer.remove();
|
||||
|
||||
return scrollbarWidth;
|
||||
};
|
||||
|
||||
export const getScrollbarWidth = () => {
|
||||
if (cachedScrollbarWidth !== null) {
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
|
||||
const scrollbarWidth = isMobile(window.innerWidth)
|
||||
? 0
|
||||
: getActualScrollbarWidth();
|
||||
cachedScrollbarWidth = scrollbarWidth;
|
||||
|
||||
return scrollbarWidth;
|
||||
};
|
|
@ -1,6 +1,20 @@
|
|||
@use 'variables' as *;
|
||||
@use 'functions' as *;
|
||||
|
||||
html.has-modal {
|
||||
&,
|
||||
body {
|
||||
touch-action: none;
|
||||
overscroll-behavior: none;
|
||||
-webkit-overflow-scrolling: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
background: var(--background-color);
|
||||
|
@ -64,20 +78,6 @@ body {
|
|||
height: 100%;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
&.with-modals--active {
|
||||
overflow-y: hidden;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-modals {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
&--active {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.player {
|
||||
|
|
|
@ -2894,6 +2894,7 @@ a.account__display-name {
|
|||
background: var(--background-color);
|
||||
backdrop-filter: var(--background-filter);
|
||||
border-top: 1px solid var(--background-border-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
.layout-multiple-columns & {
|
||||
display: none;
|
||||
|
@ -3105,7 +3106,7 @@ a.account__display-name {
|
|||
.search__input {
|
||||
line-height: 18px;
|
||||
font-size: 16px;
|
||||
padding: 15px;
|
||||
padding-block: 15px;
|
||||
padding-inline-end: 30px;
|
||||
}
|
||||
|
||||
|
@ -3165,7 +3166,7 @@ a.account__display-name {
|
|||
.navigation-panel {
|
||||
margin: 0;
|
||||
border-inline-start: 1px solid var(--background-border-color);
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.navigation-panel__banner,
|
||||
|
@ -3223,6 +3224,7 @@ a.account__display-name {
|
|||
.navigation-panel {
|
||||
width: 284px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&__menu {
|
||||
flex-shrink: 0;
|
||||
|
@ -8972,7 +8974,7 @@ noscript {
|
|||
|
||||
.search__input {
|
||||
border: 1px solid var(--background-border-color);
|
||||
padding: 12px;
|
||||
padding-block: 12px;
|
||||
padding-inline-end: 30px;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class Rule < ApplicationRecord
|
|||
|
||||
self.discard_column = :deleted_at
|
||||
|
||||
has_many :translations, inverse_of: :rule, class_name: 'RuleTranslation', dependent: :destroy
|
||||
has_many :translations, -> { order(language: :asc) }, inverse_of: :rule, class_name: 'RuleTranslation', dependent: :destroy
|
||||
accepts_nested_attributes_for :translations, reject_if: ->(attributes) { attributes['text'].blank? }, allow_destroy: true
|
||||
|
||||
validates :text, presence: true, length: { maximum: TEXT_SIZE_LIMIT }
|
||||
|
|
|
@ -115,8 +115,6 @@ module Mastodon
|
|||
end
|
||||
|
||||
config.to_prepare do
|
||||
Doorkeeper::AuthorizationsController.layout 'modal'
|
||||
Doorkeeper::AuthorizedApplicationsController.layout 'admin'
|
||||
Doorkeeper::Application.include ApplicationExtension
|
||||
Doorkeeper::AccessGrant.include AccessGrantExtension
|
||||
Doorkeeper::AccessToken.include AccessTokenExtension
|
||||
|
|
|
@ -9,7 +9,7 @@ module ViteRuby::ManifestIntegrityExtension
|
|||
|
||||
def load_manifest
|
||||
# Invalidate the name lookup cache when reloading manifest
|
||||
@name_lookup_cache = load_name_lookup_cache unless dev_server_running?
|
||||
@name_lookup_cache = nil unless dev_server_running?
|
||||
|
||||
super
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe OAuth::AuthorizationsController do
|
||||
render_views
|
||||
|
||||
let(:app) { Doorkeeper::Application.create!(name: 'test', redirect_uri: 'http://localhost/', scopes: 'read') }
|
||||
|
||||
describe 'GET #new' do
|
||||
|
@ -24,6 +26,8 @@ RSpec.describe OAuth::AuthorizationsController do
|
|||
.to have_http_status(200)
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to include('private, no-store')
|
||||
expect(response.parsed_body.at('body.modal-layout'))
|
||||
.to be_present
|
||||
expect(controller.stored_location_for(:user))
|
||||
.to eq authorize_path_for(app)
|
||||
end
|
||||
|
|
|
@ -21,6 +21,8 @@ RSpec.describe OAuth::AuthorizedApplicationsController do
|
|||
.to have_http_status(200)
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to include('private, no-store')
|
||||
expect(response.parsed_body.at('body.admin'))
|
||||
.to be_present
|
||||
expect(controller.stored_location_for(:user))
|
||||
.to eq '/oauth/authorized_applications'
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user