From 140aa6b054bb73b10a33bda17090453dd550267a Mon Sep 17 00:00:00 2001 From: fusagiko / takayamaki <24884114+takayamaki@users.noreply.github.com> Date: Sun, 7 May 2023 16:10:58 +0900 Subject: [PATCH 1/9] Rewrite VerifiedBadge component as function component (#24892) --- .../mastodon/components/account.jsx | 2 +- .../mastodon/components/verified_badge.jsx | 25 ------------------- .../mastodon/components/verified_badge.tsx | 14 +++++++++++ 3 files changed, 15 insertions(+), 26 deletions(-) delete mode 100644 app/javascript/mastodon/components/verified_badge.jsx create mode 100644 app/javascript/mastodon/components/verified_badge.tsx diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index 0e2295e3a..6a0682e9c 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/verified_badge.jsx b/app/javascript/mastodon/components/verified_badge.jsx deleted file mode 100644 index 3d878d5dd..000000000 --- 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 000000000..78686b521 --- /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; From 6fdbee240cbfa8d9068e9b5d0f43e6717186742a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=84=E3=81=A1=20=E3=81=B2?= Date: Mon, 8 May 2023 15:26:02 +0900 Subject: [PATCH 2/9] Rewrite as FC (#24901) --- app/javascript/mastodon/components/{check.jsx => check.tsx} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/javascript/mastodon/components/{check.jsx => check.tsx} (89%) 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 2fd0af740..3a4a11320 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 = () => ( From 490ccbf40b06e97f59f09253b0d2e9d613cf353d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=84=E3=81=A1=20=E3=81=B2?= Date: Mon, 8 May 2023 18:12:13 +0900 Subject: [PATCH 3/9] Rewrite as FC (#24903) --- ...{not_signed_in_indicator.jsx => not_signed_in_indicator.tsx} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/javascript/mastodon/components/{not_signed_in_indicator.jsx => not_signed_in_indicator.tsx} (86%) 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 b440c6be2..0df90ddbf 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 = () => (
From a65d2d10458fcb6c1c36fa6dd52b8f64d12ce50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=84=E3=81=A1=20=E3=81=B2?= Date: Mon, 8 May 2023 18:12:44 +0900 Subject: [PATCH 4/9] Rewrite Image component as function component (#24893) --- app/javascript/mastodon/components/image.jsx | 33 -------------------- app/javascript/mastodon/components/image.tsx | 27 ++++++++++++++++ 2 files changed, 27 insertions(+), 33 deletions(-) delete mode 100644 app/javascript/mastodon/components/image.jsx create mode 100644 app/javascript/mastodon/components/image.tsx diff --git a/app/javascript/mastodon/components/image.jsx b/app/javascript/mastodon/components/image.jsx deleted file mode 100644 index 6e81ddf08..000000000 --- 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 000000000..9b4d60225 --- /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; From 76264e3fe86d1ac3c9f6d91290e77db8d9272d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=84=E3=81=A1=20=E3=81=B2?= Date: Mon, 8 May 2023 18:12:53 +0900 Subject: [PATCH 5/9] Rewrite RadioButton component as FC (#24897) --- .../mastodon/components/radio_button.jsx | 35 ------------------- .../mastodon/components/radio_button.tsx | 30 ++++++++++++++++ 2 files changed, 30 insertions(+), 35 deletions(-) delete mode 100644 app/javascript/mastodon/components/radio_button.jsx create mode 100644 app/javascript/mastodon/components/radio_button.tsx diff --git a/app/javascript/mastodon/components/radio_button.jsx b/app/javascript/mastodon/components/radio_button.jsx deleted file mode 100644 index 0496fa286..000000000 --- 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 000000000..9ba098f78 --- /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; From 7c1305b3a4ce0ee39aa923a1f1ac604794265b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=84=E3=81=A1=20=E3=81=B2?= Date: Mon, 8 May 2023 18:28:36 +0900 Subject: [PATCH 6/9] Add TypeScript support for `mastodon` alias and image imports (#24895) --- .../mastodon/components/hashtag.jsx | 2 -- app/javascript/types/image.d.ts | 34 +++++++++++++++++++ tsconfig.json | 13 +++++-- 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 app/javascript/types/image.d.ts diff --git a/app/javascript/mastodon/components/hashtag.jsx b/app/javascript/mastodon/components/hashtag.jsx index 254fae2fe..d03b1a45a 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/types/image.d.ts b/app/javascript/types/image.d.ts new file mode 100644 index 000000000..8bd6ab028 --- /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 505b19d89..09cea2a75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,16 @@ "noEmit": true, "strict": true, "esModuleInterop": true, - "skipLibCheck": true + "skipLibCheck": true, + "baseUrl": "./", + "paths": { + "mastodon": ["app/javascript/mastodon"], + "mastodon/*": ["app/javascript/mastodon/*"] + } }, - "include": ["app/javascript/mastodon", "app/javascript/packs"] + "include": [ + "app/javascript/mastodon", + "app/javascript/packs", + "app/javascript/types" + ] } From 5bc8e2d1fdc3f1b1a0b9af5aed762d44e048250c Mon Sep 17 00:00:00 2001 From: fusagiko / takayamaki <24884114+takayamaki@users.noreply.github.com> Date: Mon, 8 May 2023 22:10:21 +0900 Subject: [PATCH 7/9] Use LayoutType from is_mobile in actions/app (#24863) --- app/javascript/mastodon/actions/app.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/app.ts b/app/javascript/mastodon/actions/app.ts index 0acfbfae7..50fd317a6 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'); From 9818f342735d1765baa281aaeeab2f60b8d049fe Mon Sep 17 00:00:00 2001 From: fusagiko / takayamaki <24884114+takayamaki@users.noreply.github.com> Date: Mon, 8 May 2023 22:12:12 +0900 Subject: [PATCH 8/9] Rewrite Domain component as function component (#24896) --- app/javascript/mastodon/components/domain.jsx | 43 ------------------- app/javascript/mastodon/components/domain.tsx | 42 ++++++++++++++++++ .../mastodon/containers/domain_container.jsx | 2 +- 3 files changed, 43 insertions(+), 44 deletions(-) delete mode 100644 app/javascript/mastodon/components/domain.jsx create mode 100644 app/javascript/mastodon/components/domain.tsx diff --git a/app/javascript/mastodon/components/domain.jsx b/app/javascript/mastodon/components/domain.jsx deleted file mode 100644 index 85ebdbde9..000000000 --- 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 000000000..6cb8f7b8f --- /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/containers/domain_container.jsx b/app/javascript/mastodon/containers/domain_container.jsx index 8a8ba1df1..419d5d29f 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({ From 89269e4b713e3291a5c8c29b8d2e7b950b60eb35 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 9 May 2023 03:07:13 +0200 Subject: [PATCH 9/9] Mark `wheel` events on scrollable list as passive (#24914) --- app/javascript/mastodon/components/scrollable_list.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/scrollable_list.jsx b/app/javascript/mastodon/components/scrollable_list.jsx index 57bc88121..3f4e4a59c 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); } }