Merge commit '89269e4b713e3291a5c8c29b8d2e7b950b60eb35' into glitch-soc/merge-upstream

Conflicts:
- `tsconfig.json`:
  Upstream changed the config to properly process imports.
  Glitch-soc had previously already done so.
  Changed the config to better match upstream.
pull/2216/head
Claire 2023-05-09 20:12:33 +02:00
commit 8b568755ad
17 changed files with 163 additions and 149 deletions

View File

@ -1,10 +1,11 @@
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import type { LayoutType } from '../is_mobile';
export const focusApp = createAction('APP_FOCUS'); export const focusApp = createAction('APP_FOCUS');
export const unfocusApp = createAction('APP_UNFOCUS'); export const unfocusApp = createAction('APP_UNFOCUS');
type ChangeLayoutPayload = { type ChangeLayoutPayload = {
layout: 'mobile' | 'single-column' | 'multi-column'; layout: LayoutType;
}; };
export const changeLayout = export const changeLayout =
createAction<ChangeLayoutPayload>('APP_LAYOUT_CHANGE'); createAction<ChangeLayoutPayload>('APP_LAYOUT_CHANGE');

View File

@ -151,7 +151,7 @@ class Account extends ImmutablePureComponent {
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
if (firstVerifiedField) { if (firstVerifiedField) {
verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} verifiedAt={firstVerifiedField.get('verified_at')} /></>; verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} /></>;
} }
return ( return (

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
const Check = () => ( export const Check: React.FC = () => (
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'> <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd' d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' clipRule='evenodd' /> <path fillRule='evenodd' d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' clipRule='evenodd' />
</svg> </svg>

View File

@ -1,43 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import IconButton from './icon_button';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
});
class Account extends ImmutablePureComponent {
static propTypes = {
domain: PropTypes.string,
onUnblockDomain: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleDomainUnblock = () => {
this.props.onUnblockDomain(this.props.domain);
};
render () {
const { domain, intl } = this.props;
return (
<div className='domain'>
<div className='domain__wrapper'>
<span className='domain__domain-name'>
<strong>{domain}</strong>
</span>
<div className='domain__buttons'>
<IconButton active icon='unlock' title={intl.formatMessage(messages.unblockDomain, { domain })} onClick={this.handleDomainUnblock} />
</div>
</div>
</div>
);
}
}
export default injectIntl(Account);

View File

@ -0,0 +1,42 @@
import React, { useCallback } from 'react';
import IconButton from './icon_button';
import { InjectedIntl, defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({
unblockDomain: {
id: 'account.unblock_domain',
defaultMessage: 'Unblock domain {domain}',
},
});
type Props = {
domain: string;
onUnblockDomain: (domain: string) => void;
intl: InjectedIntl;
};
const _Domain: React.FC<Props> = ({ domain, onUnblockDomain, intl }) => {
const handleDomainUnblock = useCallback(() => {
onUnblockDomain(domain);
}, [domain, onUnblockDomain]);
return (
<div className='domain'>
<div className='domain__wrapper'>
<span className='domain__domain-name'>
<strong>{domain}</strong>
</span>
<div className='domain__buttons'>
<IconButton
active
icon='unlock'
title={intl.formatMessage(messages.unblockDomain, { domain })}
onClick={handleDomainUnblock}
/>
</div>
</div>
</div>
);
};
export const Domain = injectIntl(_Domain);

View File

@ -5,9 +5,7 @@ import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
// @ts-expect-error
import ShortNumber from 'mastodon/components/short_number'; import ShortNumber from 'mastodon/components/short_number';
// @ts-expect-error
import Skeleton from 'mastodon/components/skeleton'; import Skeleton from 'mastodon/components/skeleton';
import classNames from 'classnames'; import classNames from 'classnames';

View File

@ -1,33 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Blurhash from './blurhash';
import classNames from 'classnames';
export default class Image extends React.PureComponent {
static propTypes = {
src: PropTypes.string,
srcSet: PropTypes.string,
blurhash: PropTypes.string,
className: PropTypes.string,
};
state = {
loaded: false,
};
handleLoad = () => this.setState({ loaded: true });
render () {
const { src, srcSet, blurhash, className } = this.props;
const { loaded } = this.state;
return (
<div className={classNames('image', { loaded }, className)} role='presentation'>
{blurhash && <Blurhash hash={blurhash} className='image__preview' />}
<img src={src} srcSet={srcSet} alt='' onLoad={this.handleLoad} />
</div>
);
}
}

View File

@ -0,0 +1,27 @@
import React, { useCallback, useState } from 'react';
import Blurhash from './blurhash';
import classNames from 'classnames';
type Props = {
src: string;
srcSet?: string;
blurhash?: string;
className?: string;
}
export const Image: React.FC<Props> = ({ src, srcSet, blurhash, className }) => {
const [loaded, setLoaded] = useState(false);
const handleLoad = useCallback(() => {
setLoaded(true);
}, [setLoaded]);
return (
<div className={classNames('image', { loaded }, className)} role='presentation'>
{blurhash && <Blurhash hash={blurhash} className='image__preview' />}
<img src={src} srcSet={srcSet} alt='' onLoad={handleLoad} />
</div>
);
};
export default Image;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
const NotSignedInIndicator = () => ( export const NotSignedInIndicator: React.FC = () => (
<div className='scrollable scrollable--flex'> <div className='scrollable scrollable--flex'>
<div className='empty-column-indicator'> <div className='empty-column-indicator'>
<FormattedMessage id='not_signed_in_indicator.not_signed_in' defaultMessage='You need to sign in to access this resource.' /> <FormattedMessage id='not_signed_in_indicator.not_signed_in' defaultMessage='You need to sign in to access this resource.' />

View File

@ -1,35 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
export default class RadioButton extends React.PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
checked: PropTypes.bool,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
label: PropTypes.node.isRequired,
};
render () {
const { name, value, checked, onChange, label } = this.props;
return (
<label className='radio-button'>
<input
name={name}
type='radio'
value={value}
checked={checked}
onChange={onChange}
/>
<span className={classNames('radio-button__input', { checked })} />
<span>{label}</span>
</label>
);
}
}

View File

@ -0,0 +1,30 @@
import React from 'react';
import classNames from 'classnames';
type Props = {
value: string;
checked: boolean;
name: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
label: React.ReactNode;
};
export const RadioButton: React.FC<Props> = ({ name, value, checked, onChange, label }) => {
return (
<label className='radio-button'>
<input
name={name}
type='radio'
value={value}
checked={checked}
onChange={onChange}
/>
<span className={classNames('radio-button__input', { checked })} />
<span>{label}</span>
</label>
);
};
export default RadioButton;

View File

@ -8,6 +8,7 @@ import IntersectionObserverWrapper from '../features/ui/util/intersection_observ
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import classNames from 'classnames'; import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen';
import LoadingIndicator from './loading_indicator'; import LoadingIndicator from './loading_indicator';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -236,10 +237,10 @@ class ScrollableList extends PureComponent {
attachScrollListener () { attachScrollListener () {
if (this.props.bindToDocument) { if (this.props.bindToDocument) {
document.addEventListener('scroll', this.handleScroll); document.addEventListener('scroll', this.handleScroll);
document.addEventListener('wheel', this.handleWheel); document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
} else { } else {
this.node.addEventListener('scroll', this.handleScroll); this.node.addEventListener('scroll', this.handleScroll);
this.node.addEventListener('wheel', this.handleWheel); this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
} }
} }

View File

@ -1,25 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'mastodon/components/icon';
class VerifiedBadge extends React.PureComponent {
static propTypes = {
link: PropTypes.string.isRequired,
verifiedAt: PropTypes.string.isRequired,
};
render () {
const { link } = this.props;
return (
<span className='verified-badge'>
<Icon id='check' className='verified-badge__mark' />
<span dangerouslySetInnerHTML={{ __html: link }} />
</span>
);
}
}
export default VerifiedBadge;

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Icon } from './icon';
type Props = {
link: string;
};
export const VerifiedBadge: React.FC<Props> = ({ link }) => (
<span className='verified-badge'>
<Icon id='check' className='verified-badge__mark' />
<span dangerouslySetInnerHTML={{ __html: link }} />
</span>
);
export default VerifiedBadge;

View File

@ -2,7 +2,7 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { blockDomain, unblockDomain } from '../actions/domain_blocks'; import { blockDomain, unblockDomain } from '../actions/domain_blocks';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Domain from '../components/domain'; import { Domain } from '../components/domain';
import { openModal } from '../actions/modal'; import { openModal } from '../actions/modal';
const messages = defineMessages({ const messages = defineMessages({

34
app/javascript/types/image.d.ts vendored Normal file
View File

@ -0,0 +1,34 @@
declare module '*.avif' {
const path: string;
export default path;
}
declare module '*.gif' {
const path: string;
export default path;
}
declare module '*.jpg' {
const path: string;
export default path;
}
declare module '*.jpg' {
const path: string;
export default path;
}
declare module '*.png' {
const path: string;
export default path;
}
declare module '*.svg' {
const path: string;
export default path;
}
declare module '*.webp' {
const path: string;
export default path;
}

View File

@ -8,14 +8,17 @@
"strict": true, "strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"baseUrl": ".", "baseUrl": "./",
"paths": { "paths": {
"*": ["app/javascript/*"] "*": ["app/javascript/*"],
"mastodon": ["app/javascript/mastodon"],
"mastodon/*": ["app/javascript/mastodon/*"]
} }
}, },
"include": [ "include": [
"app/javascript/mastodon", "app/javascript/mastodon",
"app/javascript/flavours/glitch", "app/javascript/packs",
"app/javascript/packs" "app/javascript/types",
"app/javascript/flavours/glitch"
] ]
} }