diff --git a/app/javascript/mastodon/actions/app.ts b/app/javascript/mastodon/actions/app.ts index 0acfbfae7a..50fd317a65 100644 --- a/app/javascript/mastodon/actions/app.ts +++ b/app/javascript/mastodon/actions/app.ts @@ -1,10 +1,11 @@ import { createAction } from '@reduxjs/toolkit'; +import type { LayoutType } from '../is_mobile'; export const focusApp = createAction('APP_FOCUS'); export const unfocusApp = createAction('APP_UNFOCUS'); type ChangeLayoutPayload = { - layout: 'mobile' | 'single-column' | 'multi-column'; + layout: LayoutType; }; export const changeLayout = createAction('APP_LAYOUT_CHANGE'); diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index 0e2295e3a9..6a0682e9c1 100644 --- a/app/javascript/mastodon/components/account.jsx +++ b/app/javascript/mastodon/components/account.jsx @@ -151,7 +151,7 @@ class Account extends ImmutablePureComponent { const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); if (firstVerifiedField) { - verification = <>· ; + verification = <>· ; } return ( diff --git a/app/javascript/mastodon/components/check.jsx b/app/javascript/mastodon/components/check.tsx similarity index 89% rename from app/javascript/mastodon/components/check.jsx rename to app/javascript/mastodon/components/check.tsx index 2fd0af7401..3a4a113205 100644 --- a/app/javascript/mastodon/components/check.jsx +++ b/app/javascript/mastodon/components/check.tsx @@ -1,6 +1,6 @@ import React from 'react'; -const Check = () => ( +export const Check: React.FC = () => ( diff --git a/app/javascript/mastodon/components/domain.jsx b/app/javascript/mastodon/components/domain.jsx deleted file mode 100644 index 85ebdbde93..0000000000 --- a/app/javascript/mastodon/components/domain.jsx +++ /dev/null @@ -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 ( -
-
- - {domain} - - -
- -
-
-
- ); - } - -} - -export default injectIntl(Account); diff --git a/app/javascript/mastodon/components/domain.tsx b/app/javascript/mastodon/components/domain.tsx new file mode 100644 index 0000000000..6cb8f7b8ff --- /dev/null +++ b/app/javascript/mastodon/components/domain.tsx @@ -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 = ({ domain, onUnblockDomain, intl }) => { + const handleDomainUnblock = useCallback(() => { + onUnblockDomain(domain); + }, [domain, onUnblockDomain]); + + return ( +
+
+ + {domain} + + +
+ +
+
+
+ ); +}; + +export const Domain = injectIntl(_Domain); diff --git a/app/javascript/mastodon/components/hashtag.jsx b/app/javascript/mastodon/components/hashtag.jsx index 254fae2fe0..d03b1a45a7 100644 --- a/app/javascript/mastodon/components/hashtag.jsx +++ b/app/javascript/mastodon/components/hashtag.jsx @@ -5,9 +5,7 @@ import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { Link } from 'react-router-dom'; -// @ts-expect-error import ShortNumber from 'mastodon/components/short_number'; -// @ts-expect-error import Skeleton from 'mastodon/components/skeleton'; import classNames from 'classnames'; diff --git a/app/javascript/mastodon/components/image.jsx b/app/javascript/mastodon/components/image.jsx deleted file mode 100644 index 6e81ddf082..0000000000 --- a/app/javascript/mastodon/components/image.jsx +++ /dev/null @@ -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 ( -
- {blurhash && } - -
- ); - } - -} diff --git a/app/javascript/mastodon/components/image.tsx b/app/javascript/mastodon/components/image.tsx new file mode 100644 index 0000000000..9b4d602255 --- /dev/null +++ b/app/javascript/mastodon/components/image.tsx @@ -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 = ({ src, srcSet, blurhash, className }) => { + const [loaded, setLoaded] = useState(false); + + const handleLoad = useCallback(() => { + setLoaded(true); + }, [setLoaded]); + + return ( +
+ {blurhash && } + +
+ ); +}; + +export default Image; diff --git a/app/javascript/mastodon/components/not_signed_in_indicator.jsx b/app/javascript/mastodon/components/not_signed_in_indicator.tsx similarity index 86% rename from app/javascript/mastodon/components/not_signed_in_indicator.jsx rename to app/javascript/mastodon/components/not_signed_in_indicator.tsx index b440c6be2f..0df90ddbf3 100644 --- a/app/javascript/mastodon/components/not_signed_in_indicator.jsx +++ b/app/javascript/mastodon/components/not_signed_in_indicator.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; -const NotSignedInIndicator = () => ( +export const NotSignedInIndicator: React.FC = () => (
diff --git a/app/javascript/mastodon/components/radio_button.jsx b/app/javascript/mastodon/components/radio_button.jsx deleted file mode 100644 index 0496fa2868..0000000000 --- a/app/javascript/mastodon/components/radio_button.jsx +++ /dev/null @@ -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 ( - - ); - } - -} diff --git a/app/javascript/mastodon/components/radio_button.tsx b/app/javascript/mastodon/components/radio_button.tsx new file mode 100644 index 0000000000..9ba098f78d --- /dev/null +++ b/app/javascript/mastodon/components/radio_button.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import classNames from 'classnames'; + +type Props = { + value: string; + checked: boolean; + name: string; + onChange: (event: React.ChangeEvent) => void; + label: React.ReactNode; +}; + +export const RadioButton: React.FC = ({ name, value, checked, onChange, label }) => { + return ( + + ); +}; + +export default RadioButton; diff --git a/app/javascript/mastodon/components/scrollable_list.jsx b/app/javascript/mastodon/components/scrollable_list.jsx index 57bc881218..3f4e4a59c6 100644 --- a/app/javascript/mastodon/components/scrollable_list.jsx +++ b/app/javascript/mastodon/components/scrollable_list.jsx @@ -8,6 +8,7 @@ import IntersectionObserverWrapper from '../features/ui/util/intersection_observ import { throttle } from 'lodash'; import { List as ImmutableList } from 'immutable'; import classNames from 'classnames'; +import { supportsPassiveEvents } from 'detect-passive-events'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen'; import LoadingIndicator from './loading_indicator'; import { connect } from 'react-redux'; @@ -236,10 +237,10 @@ class ScrollableList extends PureComponent { attachScrollListener () { if (this.props.bindToDocument) { document.addEventListener('scroll', this.handleScroll); - document.addEventListener('wheel', this.handleWheel); + document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined); } else { this.node.addEventListener('scroll', this.handleScroll); - this.node.addEventListener('wheel', this.handleWheel); + this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined); } } diff --git a/app/javascript/mastodon/components/verified_badge.jsx b/app/javascript/mastodon/components/verified_badge.jsx deleted file mode 100644 index 3d878d5dd1..0000000000 --- a/app/javascript/mastodon/components/verified_badge.jsx +++ /dev/null @@ -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 ( - - - - - ); - } - -} - -export default VerifiedBadge; \ No newline at end of file diff --git a/app/javascript/mastodon/components/verified_badge.tsx b/app/javascript/mastodon/components/verified_badge.tsx new file mode 100644 index 0000000000..78686b521b --- /dev/null +++ b/app/javascript/mastodon/components/verified_badge.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Icon } from './icon'; + +type Props = { + link: string; +}; +export const VerifiedBadge: React.FC = ({ link }) => ( + + + + +); + +export default VerifiedBadge; diff --git a/app/javascript/mastodon/containers/domain_container.jsx b/app/javascript/mastodon/containers/domain_container.jsx index 8a8ba1df12..419d5d29f5 100644 --- a/app/javascript/mastodon/containers/domain_container.jsx +++ b/app/javascript/mastodon/containers/domain_container.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { blockDomain, unblockDomain } from '../actions/domain_blocks'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import Domain from '../components/domain'; +import { Domain } from '../components/domain'; import { openModal } from '../actions/modal'; const messages = defineMessages({ diff --git a/app/javascript/types/image.d.ts b/app/javascript/types/image.d.ts new file mode 100644 index 0000000000..8bd6ab0286 --- /dev/null +++ b/app/javascript/types/image.d.ts @@ -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; +} diff --git a/tsconfig.json b/tsconfig.json index 7c6529d48c..480f6bdb7f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,14 +8,17 @@ "strict": true, "esModuleInterop": true, "skipLibCheck": true, - "baseUrl": ".", + "baseUrl": "./", "paths": { - "*": ["app/javascript/*"] + "*": ["app/javascript/*"], + "mastodon": ["app/javascript/mastodon"], + "mastodon/*": ["app/javascript/mastodon/*"] } }, "include": [ "app/javascript/mastodon", - "app/javascript/flavours/glitch", - "app/javascript/packs" + "app/javascript/packs", + "app/javascript/types", + "app/javascript/flavours/glitch" ] }