mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-13 07:49:29 +00:00
Merge pull request #3083 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 44d71d59ef
This commit is contained in:
@@ -36,7 +36,6 @@ Rails/OutputSafety:
|
|||||||
Style/FetchEnvVar:
|
Style/FetchEnvVar:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/initializers/2_limited_federation_mode.rb'
|
- 'config/initializers/2_limited_federation_mode.rb'
|
||||||
- 'config/initializers/3_omniauth.rb'
|
|
||||||
- 'config/initializers/paperclip.rb'
|
- 'config/initializers/paperclip.rb'
|
||||||
- 'lib/tasks/repo.rake'
|
- 'lib/tasks/repo.rake'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import type { ComponentPropsWithRef } from 'react';
|
import type { ComponentPropsWithRef } from 'react';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
useId,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@@ -11,11 +18,14 @@ import { animated, useSpring } from '@react-spring/web';
|
|||||||
import { useDrag } from '@use-gesture/react';
|
import { useDrag } from '@use-gesture/react';
|
||||||
|
|
||||||
import { expandAccountFeaturedTimeline } from '@/flavours/glitch/actions/timelines';
|
import { expandAccountFeaturedTimeline } from '@/flavours/glitch/actions/timelines';
|
||||||
|
import { Icon } from '@/flavours/glitch/components/icon';
|
||||||
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
||||||
import StatusContainer from '@/flavours/glitch/containers/status_container';
|
import StatusContainer from '@/flavours/glitch/containers/status_container';
|
||||||
|
import { usePrevious } from '@/flavours/glitch/hooks/usePrevious';
|
||||||
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||||
|
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' },
|
previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' },
|
||||||
@@ -31,6 +41,7 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
tagged?: string;
|
tagged?: string;
|
||||||
}> = ({ accountId, tagged }) => {
|
}> = ({ accountId, tagged }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const accessibilityId = useId();
|
||||||
|
|
||||||
// Load pinned statuses
|
// Load pinned statuses
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -74,6 +85,7 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
||||||
wrapperRef.current?.scrollHeight ?? 0,
|
wrapperRef.current?.scrollHeight ?? 0,
|
||||||
);
|
);
|
||||||
|
const previousSlideHeight = usePrevious(currentSlideHeight);
|
||||||
const observerRef = useRef<ResizeObserver>(
|
const observerRef = useRef<ResizeObserver>(
|
||||||
new ResizeObserver(() => {
|
new ResizeObserver(() => {
|
||||||
handleSlideChange(0);
|
handleSlideChange(0);
|
||||||
@@ -82,8 +94,10 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
const wrapperStyles = useSpring({
|
const wrapperStyles = useSpring({
|
||||||
x: `-${slideIndex * 100}%`,
|
x: `-${slideIndex * 100}%`,
|
||||||
height: currentSlideHeight,
|
height: currentSlideHeight,
|
||||||
|
// Don't animate from zero to the height of the initial slide
|
||||||
|
immediate: !previousSlideHeight,
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
// Update slide height when the component mounts
|
// Update slide height when the component mounts
|
||||||
if (currentSlideHeight === 0) {
|
if (currentSlideHeight === 0) {
|
||||||
handleSlideChange(0);
|
handleSlideChange(0);
|
||||||
@@ -110,11 +124,15 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
className='featured-carousel'
|
className='featured-carousel'
|
||||||
{...bind()}
|
{...bind()}
|
||||||
aria-roledescription='carousel'
|
aria-roledescription='carousel'
|
||||||
aria-labelledby='featured-carousel-title'
|
aria-labelledby={`${accessibilityId}-title`}
|
||||||
role='region'
|
role='region'
|
||||||
>
|
>
|
||||||
<div className='featured-carousel__header'>
|
<div className='featured-carousel__header'>
|
||||||
<h4 className='featured-carousel__title' id='featured-carousel-title'>
|
<h4
|
||||||
|
className='featured-carousel__title'
|
||||||
|
id={`${accessibilityId}-title`}
|
||||||
|
>
|
||||||
|
<Icon id='thumb-tack' icon={PushPinIcon} />
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='featured_carousel.header'
|
id='featured_carousel.header'
|
||||||
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
|
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
|
||||||
|
|||||||
16
app/javascript/flavours/glitch/hooks/usePrevious.ts
Normal file
16
app/javascript/flavours/glitch/hooks/usePrevious.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useRef, useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previous state of the passed in value.
|
||||||
|
* On first render, undefined is returned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function usePrevious<T>(value: T): T | undefined {
|
||||||
|
const ref = useRef<T>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = value;
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return ref.current;
|
||||||
|
}
|
||||||
@@ -11397,5 +11397,13 @@ noscript {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import type { ComponentPropsWithRef } from 'react';
|
import type { ComponentPropsWithRef } from 'react';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
useId,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@@ -11,11 +18,14 @@ import { animated, useSpring } from '@react-spring/web';
|
|||||||
import { useDrag } from '@use-gesture/react';
|
import { useDrag } from '@use-gesture/react';
|
||||||
|
|
||||||
import { expandAccountFeaturedTimeline } from '@/mastodon/actions/timelines';
|
import { expandAccountFeaturedTimeline } from '@/mastodon/actions/timelines';
|
||||||
|
import { Icon } from '@/mastodon/components/icon';
|
||||||
import { IconButton } from '@/mastodon/components/icon_button';
|
import { IconButton } from '@/mastodon/components/icon_button';
|
||||||
import StatusContainer from '@/mastodon/containers/status_container';
|
import StatusContainer from '@/mastodon/containers/status_container';
|
||||||
|
import { usePrevious } from '@/mastodon/hooks/usePrevious';
|
||||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||||
|
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' },
|
previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' },
|
||||||
@@ -31,6 +41,7 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
tagged?: string;
|
tagged?: string;
|
||||||
}> = ({ accountId, tagged }) => {
|
}> = ({ accountId, tagged }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const accessibilityId = useId();
|
||||||
|
|
||||||
// Load pinned statuses
|
// Load pinned statuses
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -74,6 +85,7 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
||||||
wrapperRef.current?.scrollHeight ?? 0,
|
wrapperRef.current?.scrollHeight ?? 0,
|
||||||
);
|
);
|
||||||
|
const previousSlideHeight = usePrevious(currentSlideHeight);
|
||||||
const observerRef = useRef<ResizeObserver>(
|
const observerRef = useRef<ResizeObserver>(
|
||||||
new ResizeObserver(() => {
|
new ResizeObserver(() => {
|
||||||
handleSlideChange(0);
|
handleSlideChange(0);
|
||||||
@@ -82,8 +94,10 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
const wrapperStyles = useSpring({
|
const wrapperStyles = useSpring({
|
||||||
x: `-${slideIndex * 100}%`,
|
x: `-${slideIndex * 100}%`,
|
||||||
height: currentSlideHeight,
|
height: currentSlideHeight,
|
||||||
|
// Don't animate from zero to the height of the initial slide
|
||||||
|
immediate: !previousSlideHeight,
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
// Update slide height when the component mounts
|
// Update slide height when the component mounts
|
||||||
if (currentSlideHeight === 0) {
|
if (currentSlideHeight === 0) {
|
||||||
handleSlideChange(0);
|
handleSlideChange(0);
|
||||||
@@ -110,11 +124,15 @@ export const FeaturedCarousel: React.FC<{
|
|||||||
className='featured-carousel'
|
className='featured-carousel'
|
||||||
{...bind()}
|
{...bind()}
|
||||||
aria-roledescription='carousel'
|
aria-roledescription='carousel'
|
||||||
aria-labelledby='featured-carousel-title'
|
aria-labelledby={`${accessibilityId}-title`}
|
||||||
role='region'
|
role='region'
|
||||||
>
|
>
|
||||||
<div className='featured-carousel__header'>
|
<div className='featured-carousel__header'>
|
||||||
<h4 className='featured-carousel__title' id='featured-carousel-title'>
|
<h4
|
||||||
|
className='featured-carousel__title'
|
||||||
|
id={`${accessibilityId}-title`}
|
||||||
|
>
|
||||||
|
<Icon id='thumb-tack' icon={PushPinIcon} />
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='featured_carousel.header'
|
id='featured_carousel.header'
|
||||||
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
|
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
|
||||||
|
|||||||
16
app/javascript/mastodon/hooks/usePrevious.ts
Normal file
16
app/javascript/mastodon/hooks/usePrevious.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useRef, useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previous state of the passed in value.
|
||||||
|
* On first render, undefined is returned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function usePrevious<T>(value: T): T | undefined {
|
||||||
|
const ref = useRef<T>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = value;
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return ref.current;
|
||||||
|
}
|
||||||
@@ -11074,5 +11074,13 @@ noscript {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Devise.setup do |config|
|
|||||||
# CAS strategy
|
# CAS strategy
|
||||||
if ENV['CAS_ENABLED'] == 'true'
|
if ENV['CAS_ENABLED'] == 'true'
|
||||||
cas_options = {}
|
cas_options = {}
|
||||||
cas_options[:display_name] = ENV['CAS_DISPLAY_NAME']
|
cas_options[:display_name] = ENV.fetch('CAS_DISPLAY_NAME', nil)
|
||||||
cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL']
|
cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL']
|
||||||
cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST']
|
cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST']
|
||||||
cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT']
|
cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT']
|
||||||
@@ -41,7 +41,7 @@ Devise.setup do |config|
|
|||||||
# SAML strategy
|
# SAML strategy
|
||||||
if ENV['SAML_ENABLED'] == 'true'
|
if ENV['SAML_ENABLED'] == 'true'
|
||||||
saml_options = {}
|
saml_options = {}
|
||||||
saml_options[:display_name] = ENV['SAML_DISPLAY_NAME']
|
saml_options[:display_name] = ENV.fetch('SAML_DISPLAY_NAME', nil)
|
||||||
saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL']
|
saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL']
|
||||||
saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER']
|
saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER']
|
||||||
saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL']
|
saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL']
|
||||||
@@ -73,7 +73,7 @@ Devise.setup do |config|
|
|||||||
# OpenID Connect Strategy
|
# OpenID Connect Strategy
|
||||||
if ENV['OIDC_ENABLED'] == 'true'
|
if ENV['OIDC_ENABLED'] == 'true'
|
||||||
oidc_options = {}
|
oidc_options = {}
|
||||||
oidc_options[:display_name] = ENV['OIDC_DISPLAY_NAME'] # OPTIONAL
|
oidc_options[:display_name] = ENV.fetch('OIDC_DISPLAY_NAME', nil) # OPTIONAL
|
||||||
oidc_options[:issuer] = ENV['OIDC_ISSUER'] if ENV['OIDC_ISSUER'] # NEED
|
oidc_options[:issuer] = ENV['OIDC_ISSUER'] if ENV['OIDC_ISSUER'] # NEED
|
||||||
oidc_options[:discovery] = ENV['OIDC_DISCOVERY'] == 'true' if ENV['OIDC_DISCOVERY'] # OPTIONAL (default: false)
|
oidc_options[:discovery] = ENV['OIDC_DISCOVERY'] == 'true' if ENV['OIDC_DISCOVERY'] # OPTIONAL (default: false)
|
||||||
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] # OPTIONAL (default: basic)
|
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] # OPTIONAL (default: basic)
|
||||||
|
|||||||
Reference in New Issue
Block a user