Merge commit '9215e1ec53dcf3714080c84af7af070bcb8d9978' into glitch-soc/merge-upstream

This commit is contained in:
Claire committed 2026-05-27 20:04:47 +02:00
commit 83e77e88ae
110 files changed
+959 -536

No files matched your search

@@ -9,7 +9,7 @@ import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column_header';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import BundleColumnError from '@/mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import { useColumnsContext } from '../../ui/util/columns_context';
import classes from '../styles.module.scss';
@@ -19,7 +19,7 @@ import {
} from '@/mastodon/components/scrollable_list/components';
import type { TruncatedListItemInfo } from '@/mastodon/components/truncated_list';
import { TruncatedListItems } from '@/mastodon/components/truncated_list';
import BundleColumnError from '@/mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import Column from '@/mastodon/features/ui/components/column';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAccountId } from '@/mastodon/hooks/useAccountId';
@@ -11,7 +11,7 @@ import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { LimitedAccountHint } from '@/mastodon/components/limited_account_hint';
import { RemoteHint } from '@/mastodon/components/remote_hint';
import ScrollableList from '@/mastodon/components/scrollable_list';
import BundleColumnError from '@/mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import Column from '@/mastodon/features/ui/components/column';
import { useAccountId } from '@/mastodon/hooks/useAccountId';
import { useAccountVisibility } from '@/mastodon/hooks/useAccountVisibility';
@@ -19,7 +19,7 @@ import { LimitedAccountHint } from '@/mastodon/components/limited_account_hint';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import { RemoteHint } from '@/mastodon/components/remote_hint';
import StatusList from '@/mastodon/components/status_list';
import BundleColumnError from '@/mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import {
useAccountId,
useCurrentAccountId,
@@ -19,6 +19,7 @@ import { useHistory } from 'react-router-dom';
import { isFulfilled } from '@reduxjs/toolkit';
import { getCollectionPath } from '@/mastodon/features/collections/utils';
import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
@@ -337,6 +338,10 @@ export const Search: React.FC<{
history.push(
`/@${result.payload.statuses[0].account.acct}/${result.payload.statuses[0].id}`,
);
} else if (result.payload.collections[0]) {
history.push(
getCollectionPath(result.payload.collections[0].id),
);
}
}
@@ -6,7 +6,7 @@ import { Column } from '@/mastodon/components/column';
import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import ScrollableList from '@/mastodon/components/scrollable_list';
import BundleColumnError from '@/mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAccountVisibility } from '@/mastodon/hooks/useAccountVisibility';
import { useLayout } from '@/mastodon/hooks/useLayout';
@@ -4,16 +4,14 @@ import { Helmet } from '@unhead/react/helmet';
import { Column } from 'mastodon/components/column';
import { NavigationPanel, messages } from '../navigation_panel';
import { NavigationPanel } from '../navigation_panel';
import { LinkFooter } from '../ui/components/link_footer';
const GettingStarted: React.FC = () => {
const intl = useIntl();
return (
<Column>
<nav aria-label={intl.formatMessage(messages.main)}>
<NavigationPanel multiColumn />
</nav>
<NavigationPanel multiColumn />
<LinkFooter context='multi-column' />
@@ -60,7 +60,7 @@ import { MoreLink } from './components/more_link';
import { SignInBanner } from './components/sign_in_banner';
import { Trends } from './components/trends';
export const messages = defineMessages({
const messages = defineMessages({
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
notifications: {
id: 'tabs_bar.notifications',
@@ -238,7 +238,10 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
}
return (
<div className='navigation-panel'>
<nav
className='navigation-panel'
aria-label={intl.formatMessage(messages.main)}
>
<div className='navigation-panel__logo'>
<Link
to='/'
@@ -420,12 +423,11 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
<div className='flex-spacer' />
<Trends />
</div>
</nav>
);
};
export const CollapsibleNavigationPanel: React.FC = () => {
const intl = useIntl();
const open = useAppSelector((state) => state.navigation.open);
const dispatch = useAppDispatch();
const openable = useBreakpoint('openable');
@@ -534,8 +536,7 @@ export const CollapsibleNavigationPanel: React.FC = () => {
const showOverlay = openable && open;
return (
<nav
aria-label={intl.formatMessage(messages.main)}
<div
className={classNames(
'columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational',
{ 'columns-area__panels__pane--overlay': showOverlay },
@@ -549,6 +550,6 @@ export const CollapsibleNavigationPanel: React.FC = () => {
>
<NavigationPanel />
</animated.div>
</nav>
</div>
);
};
@@ -14,7 +14,7 @@ import { Helmet } from '@unhead/react/helmet';
import { apiGetTermsOfService } from 'mastodon/api/instance';
import type { ApiTermsOfServiceJSON } from 'mastodon/api_types/instance';
import { Column } from 'mastodon/components/column';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { BundleColumnError } from 'mastodon/features/ui/components/bundle_column_error';
const messages = defineMessages({
title: { id: 'terms_of_service.title', defaultMessage: 'Terms of Service' },
@@ -1,116 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Helmet } from '@unhead/react/helmet';
import { Link } from 'react-router-dom';
import { Button } from 'mastodon/components/button';
import Column from 'mastodon/components/column';
import { injectIntl } from '@/mastodon/components/intl';
import { GIF } from 'mastodon/components/gif';
class CopyButton extends PureComponent {
static propTypes = {
children: PropTypes.node.isRequired,
value: PropTypes.string.isRequired,
};
state = {
copied: false,
};
handleClick = () => {
const { value } = this.props;
navigator.clipboard.writeText(value);
this.setState({ copied: true });
this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
};
componentWillUnmount () {
if (this.timeout) clearTimeout(this.timeout);
}
render () {
const { children } = this.props;
const { copied } = this.state;
return (
<Button onClick={this.handleClick} className={copied ? 'copied' : 'copyable'}>{copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : children}</Button>
);
}
}
class BundleColumnError extends PureComponent {
static propTypes = {
errorType: PropTypes.oneOf(['routing', 'network', 'error']),
onRetry: PropTypes.func,
intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool,
stacktrace: PropTypes.string,
};
static defaultProps = {
errorType: 'routing',
};
handleRetry = () => {
const { onRetry } = this.props;
if (onRetry) {
onRetry();
}
};
render () {
const { errorType, multiColumn, stacktrace } = this.props;
let title, body;
switch(errorType) {
case 'routing':
title = <FormattedMessage id='bundle_column_error.routing.title' defaultMessage='404' />;
body = <FormattedMessage id='bundle_column_error.routing.body' defaultMessage='The requested page could not be found. Are you sure the URL in the address bar is correct?' />;
break;
case 'network':
title = <FormattedMessage id='bundle_column_error.network.title' defaultMessage='Network error' />;
body = <FormattedMessage id='bundle_column_error.network.body' defaultMessage='There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.' />;
break;
case 'error':
title = <FormattedMessage id='bundle_column_error.error.title' defaultMessage='Oh, no!' />;
body = <FormattedMessage id='bundle_column_error.error.body' defaultMessage='The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.' />;
break;
}
return (
<Column bindToDocument={!multiColumn}>
<div className='error-column'>
<GIF src='/oops.gif' staticSrc='/oops.png' className='error-column__image' />
<div className='error-column__message'>
<h1>{title}</h1>
<p>{body}</p>
<div className='error-column__message__actions'>
{errorType === 'network' && <Button onClick={this.handleRetry}><FormattedMessage id='bundle_column_error.retry' defaultMessage='Try again' /></Button>}
{errorType === 'error' && <CopyButton value={stacktrace}><FormattedMessage id='bundle_column_error.copy_stacktrace' defaultMessage='Copy error report' /></CopyButton>}
<Link to='/' className={classNames('button', { 'button-secondary': errorType !== 'routing' })}><FormattedMessage id='bundle_column_error.return' defaultMessage='Go back home' /></Link>
</div>
</div>
</div>
<Helmet>
<meta name='robots' content='noindex' />
</Helmet>
</Column>
);
}
}
export default injectIntl(BundleColumnError);
@@ -0,0 +1,122 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { CopyButton } from '@/mastodon/components/copy_button';
import { EmptyState } from '@/mastodon/components/empty_state';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import classes from './styles.module.scss';
interface BundleColumnErrorProps {
errorType?: 'routing' | 'network' | 'error';
onRetry?: () => void;
multiColumn?: boolean;
stacktrace?: string;
}
export const BundleColumnError: React.FC<BundleColumnErrorProps> = ({
errorType = 'routing',
onRetry,
multiColumn,
stacktrace,
}) => {
let title, body;
switch (errorType) {
case 'routing':
title = (
<FormattedMessage
id='bundle_column_error.routing.title'
defaultMessage='404'
/>
);
body = (
<FormattedMessage
id='bundle_column_error.routing.body'
defaultMessage='The requested page could not be found. Are you sure the URL in the address bar is correct?'
/>
);
break;
case 'network':
title = (
<FormattedMessage
id='bundle_column_error.network.title'
defaultMessage='Network error'
/>
);
body = (
<FormattedMessage
id='bundle_column_error.network.body'
defaultMessage='There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.'
/>
);
break;
case 'error':
title = (
<FormattedMessage
id='bundle_column_error.error.title'
defaultMessage='Oh, no!'
/>
);
body = (
<FormattedMessage
id='bundle_column_error.error.body'
defaultMessage='The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.'
/>
);
break;
}
return (
<Column bindToDocument={!multiColumn}>
<EmptyState
image='error'
title={title}
message={body}
className={classes.error}
>
<div className={classes.actions}>
{errorType === 'network' && onRetry && (
<Button onClick={onRetry}>
<FormattedMessage
id='bundle_column_error.retry'
defaultMessage='Try again'
/>
</Button>
)}
{errorType === 'error' && stacktrace && (
<CopyButton value={stacktrace}>
<FormattedMessage
id='bundle_column_error.copy_stacktrace'
defaultMessage='Copy error report'
/>
</CopyButton>
)}
<Link
to='/'
className={classNames('button', {
'button-secondary': errorType !== 'routing',
})}
>
<FormattedMessage
id='bundle_column_error.return'
defaultMessage='Go back home'
/>
</Link>
</div>
</EmptyState>
<Helmet>
<meta name='robots' content='noindex' />
</Helmet>
</Column>
);
};
// eslint-disable-next-line import/no-default-export
export default BundleColumnError;
@@ -0,0 +1,12 @@
.error {
flex-grow: 1;
border: 1px solid var(--color-border-primary);
border-radius: 4px;
}
.actions {
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
justify-content: center;
}
@@ -33,7 +33,7 @@ import {
import { useColumnsContext } from '../util/columns_context';
import Bundle from './bundle';
import BundleColumnError from './bundle_column_error';
import { BundleColumnError } from './bundle_column_error';
import { ColumnLoading } from './column_loading';
import { ComposePanel, RedirectToMobileComposeIfNeeded } from './compose_panel';
import DrawerLoading from './drawer_loading';
@@ -29,6 +29,7 @@ export const LinkFooter: React.FC<{
id='footer.about_this_server'
defaultMessage='About'
/>
<span className='sr-only'> {domain}</span>
</Link>
</li>
{statusPageUrl && (
@@ -82,6 +83,7 @@ export const LinkFooter: React.FC<{
<li>
<a href='https://joinmastodon.org' target='_blank' rel='noopener'>
<FormattedMessage id='footer.about' defaultMessage='About' />
<span className='sr-only'> Mastodon</span>
</a>
</li>
<li>