From 16499bc097a0d5625ae2f67b3f7c4c3157bfa50c Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 14 Jan 2024 13:23:13 +0100 Subject: [PATCH 1/4] Rewrite `Permalink` as functional component --- .../flavours/glitch/components/permalink.jsx | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/app/javascript/flavours/glitch/components/permalink.jsx b/app/javascript/flavours/glitch/components/permalink.jsx index 5226895415..4a6da125e7 100644 --- a/app/javascript/flavours/glitch/components/permalink.jsx +++ b/app/javascript/flavours/glitch/components/permalink.jsx @@ -1,50 +1,38 @@ import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; +import { useCallback } from 'react'; -import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router'; +import { useAppHistory } from './router'; -class Permalink extends PureComponent { +const Permalink = ({ className, href, to, children, onInterceptClick, ...props }) => { + const history = useAppHistory(); - static propTypes = { - className: PropTypes.string, - href: PropTypes.string.isRequired, - to: PropTypes.string.isRequired, - children: PropTypes.node, - onInterceptClick: PropTypes.func, - ...WithOptionalRouterPropTypes, - }; - - handleClick = (e) => { + const handleClick = useCallback((e) => { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - if (this.props.onInterceptClick && this.props.onInterceptClick()) { + if (onInterceptClick && onInterceptClick()) { e.preventDefault(); return; } - if (this.props.history) { + if (history) { e.preventDefault(); - this.props.history.push(this.props.to); + history.push(to); } } - }; + }, [onInterceptClick, history, to]); - render () { - const { - children, - className, - href, - to, - onInterceptClick, - ...other - } = this.props; + return ( + + {children} + + ); +}; - return ( - - {children} - - ); - } +Permalink.propTypes = { + className: PropTypes.string, + href: PropTypes.string.isRequired, + to: PropTypes.string.isRequired, + children: PropTypes.node, + onInterceptClick: PropTypes.func, +}; -} - -export default withOptionalRouter(Permalink); +export default Permalink; From 52c023a305cad2bd2d868aa3d8116bffd9b52d9e Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 14 Jan 2024 14:15:23 +0100 Subject: [PATCH 2/4] Convert `Permalink` to Typescript --- .../flavours/glitch/components/account.jsx | 2 +- .../flavours/glitch/components/hashtag.jsx | 2 +- .../flavours/glitch/components/permalink.jsx | 38 ----------------- .../flavours/glitch/components/permalink.tsx | 41 +++++++++++++++++++ .../glitch/components/status_content.jsx | 2 +- .../compose/components/navigation_bar.jsx | 2 +- .../components/conversation.jsx | 2 +- .../directory/components/account_card.jsx | 2 +- .../components/account_authorize.jsx | 2 +- .../notifications/components/admin_report.jsx | 2 +- .../notifications/components/admin_signup.jsx | 2 +- .../notifications/components/follow.jsx | 2 +- .../components/follow_request.jsx | 2 +- .../glitch/features/ui/components/header.jsx | 2 +- .../flavours/glitch/features/ui/index.jsx | 6 +-- 15 files changed, 56 insertions(+), 53 deletions(-) delete mode 100644 app/javascript/flavours/glitch/components/permalink.jsx create mode 100644 app/javascript/flavours/glitch/components/permalink.tsx diff --git a/app/javascript/flavours/glitch/components/account.jsx b/app/javascript/flavours/glitch/components/account.jsx index 109e0daddd..266a3ca995 100644 --- a/app/javascript/flavours/glitch/components/account.jsx +++ b/app/javascript/flavours/glitch/components/account.jsx @@ -17,7 +17,7 @@ import { Avatar } from './avatar'; import { Button } from './button'; import { FollowersCounter } from './counters'; import { DisplayName } from './display_name'; -import Permalink from './permalink'; +import { Permalink } from './permalink'; import { RelativeTimestamp } from './relative_timestamp'; const messages = defineMessages({ diff --git a/app/javascript/flavours/glitch/components/hashtag.jsx b/app/javascript/flavours/glitch/components/hashtag.jsx index 956834b47a..a47dbd8e1c 100644 --- a/app/javascript/flavours/glitch/components/hashtag.jsx +++ b/app/javascript/flavours/glitch/components/hashtag.jsx @@ -13,7 +13,7 @@ import { Sparklines, SparklinesCurve } from 'react-sparklines'; import { ShortNumber } from 'flavours/glitch/components/short_number'; import { Skeleton } from 'flavours/glitch/components/skeleton'; -import Permalink from './permalink'; +import { Permalink } from './permalink'; class SilentErrorBoundary extends Component { diff --git a/app/javascript/flavours/glitch/components/permalink.jsx b/app/javascript/flavours/glitch/components/permalink.jsx deleted file mode 100644 index 4a6da125e7..0000000000 --- a/app/javascript/flavours/glitch/components/permalink.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import PropTypes from 'prop-types'; -import { useCallback } from 'react'; - -import { useAppHistory } from './router'; - -const Permalink = ({ className, href, to, children, onInterceptClick, ...props }) => { - const history = useAppHistory(); - - const handleClick = useCallback((e) => { - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - if (onInterceptClick && onInterceptClick()) { - e.preventDefault(); - return; - } - - if (history) { - e.preventDefault(); - history.push(to); - } - } - }, [onInterceptClick, history, to]); - - return ( - - {children} - - ); -}; - -Permalink.propTypes = { - className: PropTypes.string, - href: PropTypes.string.isRequired, - to: PropTypes.string.isRequired, - children: PropTypes.node, - onInterceptClick: PropTypes.func, -}; - -export default Permalink; diff --git a/app/javascript/flavours/glitch/components/permalink.tsx b/app/javascript/flavours/glitch/components/permalink.tsx new file mode 100644 index 0000000000..9068efb06d --- /dev/null +++ b/app/javascript/flavours/glitch/components/permalink.tsx @@ -0,0 +1,41 @@ +import { useCallback } from 'react'; + +import { useAppHistory } from './router'; + +interface Props extends React.AnchorHTMLAttributes { + to: string; +} + +export const Permalink: React.FC = ({ + className, + href, + to, + children, + ...props +}) => { + const history = useAppHistory(); + + const handleClick = useCallback>( + (e) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- history can actually be undefined as the component can be mounted outside a router context + if (e.button === 0 && !(e.ctrlKey || e.metaKey) && history) { + e.preventDefault(); + history.push(to); + } + }, + [history, to], + ); + + return ( + + {children} + + ); +}; diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index a8e069426e..5e0a40835f 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -19,7 +19,7 @@ import { Icon } from 'flavours/glitch/components/icon'; import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; -import Permalink from './permalink'; +import { Permalink } from './permalink'; const textMatchesTarget = (text, origin, host) => { return (text === origin || text === host diff --git a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx index 9f88b0e92a..ca8e1222e6 100644 --- a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx @@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { profileLink } from 'flavours/glitch/utils/backend_links'; import { Avatar } from '../../../components/avatar'; diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx index 5266a8012a..92f951e635 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx @@ -15,7 +15,7 @@ import { HotKeys } from 'react-hotkeys'; import AttachmentList from 'flavours/glitch/components/attachment_list'; import AvatarComposite from 'flavours/glitch/components/avatar_composite'; import { IconButton } from 'flavours/glitch/components/icon_button'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp'; import StatusContent from 'flavours/glitch/components/status_content'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; diff --git a/app/javascript/flavours/glitch/features/directory/components/account_card.jsx b/app/javascript/flavours/glitch/features/directory/components/account_card.jsx index 27b363bd08..60927f81c3 100644 --- a/app/javascript/flavours/glitch/features/directory/components/account_card.jsx +++ b/app/javascript/flavours/glitch/features/directory/components/account_card.jsx @@ -19,7 +19,7 @@ import { Avatar } from 'flavours/glitch/components/avatar'; import { Button } from 'flavours/glitch/components/button'; import { DisplayName } from 'flavours/glitch/components/display_name'; import { IconButton } from 'flavours/glitch/components/icon_button'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { ShortNumber } from 'flavours/glitch/components/short_number'; import { autoPlayGif, me, unfollowModal } from 'flavours/glitch/initial_state'; import { makeGetAccount } from 'flavours/glitch/selectors'; diff --git a/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx index a252ab25bd..225045c4c8 100644 --- a/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx +++ b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx @@ -11,7 +11,7 @@ import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/ import { Avatar } from '../../../components/avatar'; import { DisplayName } from '../../../components/display_name'; import { IconButton } from '../../../components/icon_button'; -import Permalink from '../../../components/permalink'; +import { Permalink } from '../../../components/permalink'; const messages = defineMessages({ authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, diff --git a/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx b/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx index 70e8a4e343..01273f959b 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx +++ b/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx @@ -12,7 +12,7 @@ import { ReactComponent as FlagIcon } from '@material-symbols/svg-600/outlined/f import { HotKeys } from 'react-hotkeys'; import { Icon } from 'flavours/glitch/components/icon'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import NotificationOverlayContainer from '../containers/overlay_container'; diff --git a/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx b/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx index ee547e05b1..a99d90e0f5 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx +++ b/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx @@ -12,7 +12,7 @@ import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outli import { HotKeys } from 'react-hotkeys'; import { Icon } from 'flavours/glitch/components/icon'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import AccountContainer from 'flavours/glitch/containers/account_container'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; diff --git a/app/javascript/flavours/glitch/features/notifications/components/follow.jsx b/app/javascript/flavours/glitch/features/notifications/components/follow.jsx index 981d14aaf3..6aff0dfede 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/follow.jsx +++ b/app/javascript/flavours/glitch/features/notifications/components/follow.jsx @@ -12,7 +12,7 @@ import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outli import { HotKeys } from 'react-hotkeys'; import { Icon } from 'flavours/glitch/components/icon'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import AccountContainer from 'flavours/glitch/containers/account_container'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; diff --git a/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx b/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx index b3b415a2a4..8462cc16b1 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx +++ b/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx @@ -17,7 +17,7 @@ import { Avatar } from 'flavours/glitch/components/avatar'; import { DisplayName } from 'flavours/glitch/components/display_name'; import { Icon } from 'flavours/glitch/components/icon'; import { IconButton } from 'flavours/glitch/components/icon_button'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import NotificationOverlayContainer from '../containers/overlay_container'; diff --git a/app/javascript/flavours/glitch/features/ui/components/header.jsx b/app/javascript/flavours/glitch/features/ui/components/header.jsx index 1efacfc1c4..ed2d6ba7f3 100644 --- a/app/javascript/flavours/glitch/features/ui/components/header.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/header.jsx @@ -14,7 +14,7 @@ import { fetchServer } from 'flavours/glitch/actions/server'; import { Avatar } from 'flavours/glitch/components/avatar'; import { Icon } from 'flavours/glitch/components/icon'; import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo'; -import Permalink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import { registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state'; const Account = connect(state => ({ diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index 839e06eea2..ba7e521b75 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -15,7 +15,7 @@ import { HotKeys } from 'react-hotkeys'; import { changeLayout } from 'flavours/glitch/actions/app'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers'; import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding'; -import PermaLink from 'flavours/glitch/components/permalink'; +import { Permalink } from 'flavours/glitch/components/permalink'; import PictureInPicture from 'flavours/glitch/features/picture_in_picture'; import { layoutFromWindow } from 'flavours/glitch/is_mobile'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -649,9 +649,9 @@ class UI extends PureComponent { id='moved_to_warning' defaultMessage='This account is marked as moved to {moved_to_link}, and may thus not accept new follows.' values={{ moved_to_link: ( - + @{moved.get('acct')} - + ) }} /> )} From 695dcc6ca84ee86090b2b68d1d47ff1cc49cb304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 28 Nov 2023 18:47:55 +0100 Subject: [PATCH 3/4] [Glitch] Converted app/javascript/flavours/glitch/utils/ folder to TypeScript Port 1142f4c79e3eaf4450ed727de0f480e300e8b9a2 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/utils/config.js | 10 ----- .../flavours/glitch/utils/config.ts | 13 ++++++ app/javascript/flavours/glitch/utils/html.js | 6 --- app/javascript/flavours/glitch/utils/html.ts | 9 +++++ .../glitch/utils/{icons.jsx => icons.tsx} | 14 ++++++- .../glitch/utils/{log_out.js => log_out.ts} | 0 .../flavours/glitch/utils/notifications.js | 30 -------------- .../flavours/glitch/utils/notifications.ts | 13 ++++++ .../{react_router.jsx => react_router.tsx} | 40 +++++++++++-------- .../utils/{scrollbar.js => scrollbar.ts} | 17 ++++---- 10 files changed, 77 insertions(+), 75 deletions(-) delete mode 100644 app/javascript/flavours/glitch/utils/config.js create mode 100644 app/javascript/flavours/glitch/utils/config.ts delete mode 100644 app/javascript/flavours/glitch/utils/html.js create mode 100644 app/javascript/flavours/glitch/utils/html.ts rename app/javascript/flavours/glitch/utils/{icons.jsx => icons.tsx} (69%) rename app/javascript/flavours/glitch/utils/{log_out.js => log_out.ts} (100%) delete mode 100644 app/javascript/flavours/glitch/utils/notifications.js create mode 100644 app/javascript/flavours/glitch/utils/notifications.ts rename app/javascript/flavours/glitch/utils/{react_router.jsx => react_router.tsx} (53%) rename app/javascript/flavours/glitch/utils/{scrollbar.js => scrollbar.ts} (72%) diff --git a/app/javascript/flavours/glitch/utils/config.js b/app/javascript/flavours/glitch/utils/config.js deleted file mode 100644 index 932cd0cbf5..0000000000 --- a/app/javascript/flavours/glitch/utils/config.js +++ /dev/null @@ -1,10 +0,0 @@ -import ready from '../ready'; - -export let assetHost = ''; - -ready(() => { - const cdnHost = document.querySelector('meta[name=cdn-host]'); - if (cdnHost) { - assetHost = cdnHost.content || ''; - } -}); diff --git a/app/javascript/flavours/glitch/utils/config.ts b/app/javascript/flavours/glitch/utils/config.ts new file mode 100644 index 0000000000..9222c89d1b --- /dev/null +++ b/app/javascript/flavours/glitch/utils/config.ts @@ -0,0 +1,13 @@ +import ready from '../ready'; + +export let assetHost = ''; + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +ready(() => { + const cdnHost = document.querySelector( + 'meta[name=cdn-host]', + ); + if (cdnHost) { + assetHost = cdnHost.content || ''; + } +}); diff --git a/app/javascript/flavours/glitch/utils/html.js b/app/javascript/flavours/glitch/utils/html.js deleted file mode 100644 index 247e98c88a..0000000000 --- a/app/javascript/flavours/glitch/utils/html.js +++ /dev/null @@ -1,6 +0,0 @@ -// NB: This function can still return unsafe HTML -export const unescapeHTML = (html) => { - const wrapper = document.createElement('div'); - wrapper.innerHTML = html.replace(//g, '\n').replace(/<\/p>

/g, '\n\n').replace(/<[^>]*>/g, ''); - return wrapper.textContent; -}; diff --git a/app/javascript/flavours/glitch/utils/html.ts b/app/javascript/flavours/glitch/utils/html.ts new file mode 100644 index 0000000000..0145a04551 --- /dev/null +++ b/app/javascript/flavours/glitch/utils/html.ts @@ -0,0 +1,9 @@ +// NB: This function can still return unsafe HTML +export const unescapeHTML = (html: string) => { + const wrapper = document.createElement('div'); + wrapper.innerHTML = html + .replace(//g, '\n') + .replace(/<\/p>

/g, '\n\n') + .replace(/<[^>]*>/g, ''); + return wrapper.textContent; +}; diff --git a/app/javascript/flavours/glitch/utils/icons.jsx b/app/javascript/flavours/glitch/utils/icons.tsx similarity index 69% rename from app/javascript/flavours/glitch/utils/icons.jsx rename to app/javascript/flavours/glitch/utils/icons.tsx index be566032e0..6e432e32fa 100644 --- a/app/javascript/flavours/glitch/utils/icons.jsx +++ b/app/javascript/flavours/glitch/utils/icons.tsx @@ -1,13 +1,23 @@ // Copied from emoji-mart for consistency with emoji picker and since // they don't export the icons in the package export const loupeIcon = ( - + ); export const deleteIcon = ( - + ); diff --git a/app/javascript/flavours/glitch/utils/log_out.js b/app/javascript/flavours/glitch/utils/log_out.ts similarity index 100% rename from app/javascript/flavours/glitch/utils/log_out.js rename to app/javascript/flavours/glitch/utils/log_out.ts diff --git a/app/javascript/flavours/glitch/utils/notifications.js b/app/javascript/flavours/glitch/utils/notifications.js deleted file mode 100644 index 42623ac7c6..0000000000 --- a/app/javascript/flavours/glitch/utils/notifications.js +++ /dev/null @@ -1,30 +0,0 @@ -// Handles browser quirks, based on -// https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API - -const checkNotificationPromise = () => { - try { - // eslint-disable-next-line promise/valid-params, promise/catch-or-return - Notification.requestPermission().then(); - } catch(e) { - return false; - } - - return true; -}; - -const handlePermission = (permission, callback) => { - // Whatever the user answers, we make sure Chrome stores the information - if(!('permission' in Notification)) { - Notification.permission = permission; - } - - callback(Notification.permission); -}; - -export const requestNotificationPermission = (callback) => { - if (checkNotificationPromise()) { - Notification.requestPermission().then((permission) => handlePermission(permission, callback)).catch(console.warn); - } else { - Notification.requestPermission((permission) => handlePermission(permission, callback)); - } -}; diff --git a/app/javascript/flavours/glitch/utils/notifications.ts b/app/javascript/flavours/glitch/utils/notifications.ts new file mode 100644 index 0000000000..08f677f8fe --- /dev/null +++ b/app/javascript/flavours/glitch/utils/notifications.ts @@ -0,0 +1,13 @@ +/** + * Tries Notification.requestPermission, console warning instead of rejecting on error. + * @param callback Runs with the permission result on completion. + */ +export const requestNotificationPermission = async ( + callback: NotificationPermissionCallback, +) => { + try { + callback(await Notification.requestPermission()); + } catch (error) { + console.warn(error); + } +}; diff --git a/app/javascript/flavours/glitch/utils/react_router.jsx b/app/javascript/flavours/glitch/utils/react_router.tsx similarity index 53% rename from app/javascript/flavours/glitch/utils/react_router.jsx rename to app/javascript/flavours/glitch/utils/react_router.tsx index fa8f0db2b5..0682fee554 100644 --- a/app/javascript/flavours/glitch/utils/react_router.jsx +++ b/app/javascript/flavours/glitch/utils/react_router.tsx @@ -1,8 +1,8 @@ -import PropTypes from "prop-types"; +import PropTypes from 'prop-types'; -import { __RouterContext } from "react-router"; +import { __RouterContext } from 'react-router'; -import hoistStatics from "hoist-non-react-statics"; +import hoistStatics from 'hoist-non-react-statics'; export const WithRouterPropTypes = { match: PropTypes.object.isRequired, @@ -16,31 +16,37 @@ export const WithOptionalRouterPropTypes = { history: PropTypes.object, }; +export interface OptionalRouterProps { + ref: unknown; + wrappedComponentRef: unknown; +} + // This is copied from https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/withRouter.js // but does not fail if called outside of a React Router context -export function withOptionalRouter(Component) { - const displayName = `withRouter(${Component.displayName || Component.name})`; - const C = props => { +export function withOptionalRouter< + ComponentType extends React.ComponentType, +>(Component: ComponentType) { + const displayName = `withRouter(${Component.displayName ?? Component.name})`; + const C = (props: React.ComponentProps) => { const { wrappedComponentRef, ...remainingProps } = props; return ( <__RouterContext.Consumer> - {context => { - if(context) + {(context) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (context) { return ( + // @ts-expect-error - Dynamic covariant generic components are tough to type. ); - else - return ( - - ); + } else { + // @ts-expect-error - Dynamic covariant generic components are tough to type. + return ; + } }} ); @@ -53,8 +59,8 @@ export function withOptionalRouter(Component) { wrappedComponentRef: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, - PropTypes.object - ]) + PropTypes.object, + ]), }; return hoistStatics(C, Component); diff --git a/app/javascript/flavours/glitch/utils/scrollbar.js b/app/javascript/flavours/glitch/utils/scrollbar.ts similarity index 72% rename from app/javascript/flavours/glitch/utils/scrollbar.js rename to app/javascript/flavours/glitch/utils/scrollbar.ts index b3f543ffb3..d505df1244 100644 --- a/app/javascript/flavours/glitch/utils/scrollbar.js +++ b/app/javascript/flavours/glitch/utils/scrollbar.ts @@ -1,9 +1,7 @@ -/** @type {number | null} */ -let cachedScrollbarWidth = null; +import { isMobile } from '../is_mobile'; + +let cachedScrollbarWidth: number | null = null; -/** - * @returns {number} - */ const getActualScrollbarWidth = () => { const outer = document.createElement('div'); outer.style.visibility = 'hidden'; @@ -14,20 +12,19 @@ const getActualScrollbarWidth = () => { outer.appendChild(inner); const scrollbarWidth = outer.offsetWidth - inner.offsetWidth; - outer.parentNode.removeChild(outer); + outer.remove(); return scrollbarWidth; }; -/** - * @returns {number} - */ export const getScrollbarWidth = () => { if (cachedScrollbarWidth !== null) { return cachedScrollbarWidth; } - const scrollbarWidth = getActualScrollbarWidth(); + const scrollbarWidth = isMobile(window.innerWidth) + ? 0 + : getActualScrollbarWidth(); cachedScrollbarWidth = scrollbarWidth; return scrollbarWidth; From f8941c41a5f3b714ecf52e5245528da437d2c73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 28 Nov 2023 19:20:10 +0100 Subject: [PATCH 4/4] [Glitch] Converted hashtag.jsx to TypeScript Port 3a7f10c3f10e6a7ab154030a2abf5a1a68c1b8f9 to glitch-soc Co-authored-by: Claire Co-authored-by: Renaud Chaput Signed-off-by: Claire --- .../glitch/components/admin/Trends.jsx | 2 +- .../flavours/glitch/components/hashtag.jsx | 123 --------------- .../flavours/glitch/components/hashtag.tsx | 149 ++++++++++++++++++ .../account/components/featured_tags.jsx | 2 +- .../glitch/features/followed_tags/index.jsx | 2 +- 5 files changed, 152 insertions(+), 126 deletions(-) delete mode 100644 app/javascript/flavours/glitch/components/hashtag.jsx create mode 100644 app/javascript/flavours/glitch/components/hashtag.tsx diff --git a/app/javascript/flavours/glitch/components/admin/Trends.jsx b/app/javascript/flavours/glitch/components/admin/Trends.jsx index 975ea6e0f2..d7755fcdbb 100644 --- a/app/javascript/flavours/glitch/components/admin/Trends.jsx +++ b/app/javascript/flavours/glitch/components/admin/Trends.jsx @@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import api from 'flavours/glitch/api'; -import Hashtag from 'flavours/glitch/components/hashtag'; +import { Hashtag } from 'flavours/glitch/components/hashtag'; export default class Trends extends PureComponent { diff --git a/app/javascript/flavours/glitch/components/hashtag.jsx b/app/javascript/flavours/glitch/components/hashtag.jsx deleted file mode 100644 index a47dbd8e1c..0000000000 --- a/app/javascript/flavours/glitch/components/hashtag.jsx +++ /dev/null @@ -1,123 +0,0 @@ -// @ts-check -import PropTypes from 'prop-types'; -import { Component } from 'react'; - -import { FormattedMessage } from 'react-intl'; - -import classNames from 'classnames'; - -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import { Sparklines, SparklinesCurve } from 'react-sparklines'; - -import { ShortNumber } from 'flavours/glitch/components/short_number'; -import { Skeleton } from 'flavours/glitch/components/skeleton'; - -import { Permalink } from './permalink'; - -class SilentErrorBoundary extends Component { - - static propTypes = { - children: PropTypes.node, - }; - - state = { - error: false, - }; - - componentDidCatch() { - this.setState({ error: true }); - } - - render() { - if (this.state.error) { - return null; - } - - return this.props.children; - } - -} - -/** - * Used to render counter of how much people are talking about hashtag - * @type {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element} - */ -export const accountsCountRenderer = (displayNumber, pluralReady) => ( - {displayNumber}, - days: 2, - }} - /> -); - -// @ts-expect-error -export const ImmutableHashtag = ({ hashtag }) => ( - day.get('uses')).toArray()} - /> -); - -ImmutableHashtag.propTypes = { - hashtag: ImmutablePropTypes.map.isRequired, -}; - -// @ts-expect-error -const Hashtag = ({ name, href, to, people, uses, history, className, description, withGraph }) => ( -

-
- - {name ? <>#{name} : } - - - {description ? ( - {description} - ) : ( - typeof people !== 'undefined' ? : - )} -
- - {typeof uses !== 'undefined' && ( -
- -
- )} - - {withGraph && ( -
- - 0)}> - - - -
- )} -
-); - -Hashtag.propTypes = { - name: PropTypes.string, - href: PropTypes.string, - to: PropTypes.string, - people: PropTypes.number, - description: PropTypes.node, - uses: PropTypes.number, - history: PropTypes.arrayOf(PropTypes.number), - className: PropTypes.string, - withGraph: PropTypes.bool, -}; - -Hashtag.defaultProps = { - withGraph: true, -}; - -export default Hashtag; diff --git a/app/javascript/flavours/glitch/components/hashtag.tsx b/app/javascript/flavours/glitch/components/hashtag.tsx new file mode 100644 index 0000000000..f9a7455b8f --- /dev/null +++ b/app/javascript/flavours/glitch/components/hashtag.tsx @@ -0,0 +1,149 @@ +import type { JSX } from 'react'; +import { Component } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import classNames from 'classnames'; + +import type Immutable from 'immutable'; + +import { Sparklines, SparklinesCurve } from 'react-sparklines'; + +import { ShortNumber } from 'flavours/glitch/components/short_number'; +import { Skeleton } from 'flavours/glitch/components/skeleton'; + +import { Permalink } from './permalink'; + +interface SilentErrorBoundaryProps { + children: React.ReactNode; +} + +class SilentErrorBoundary extends Component { + state = { + error: false, + }; + + componentDidCatch() { + this.setState({ error: true }); + } + + render() { + if (this.state.error) { + return null; + } + + return this.props.children; + } +} + +/** + * Used to render counter of how much people are talking about hashtag + * @param displayNumber Counter number to display + * @param pluralReady Whether the count is plural + * @returns Formatted counter of how much people are talking about hashtag + */ +export const accountsCountRenderer = ( + displayNumber: JSX.Element, + pluralReady: number, +) => ( + {displayNumber}, + days: 2, + }} + /> +); + +interface ImmutableHashtagProps { + hashtag: Immutable.Map; +} + +export const ImmutableHashtag = ({ hashtag }: ImmutableHashtagProps) => ( + + > + ) + .reverse() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .map((day) => day.get('uses')!) + .toArray()} + /> +); + +export interface HashtagProps { + className?: string; + description?: React.ReactNode; + history?: number[]; + href: string; + name: string; + people: number; + to: string; + uses?: number; + withGraph?: boolean; +} + +export const Hashtag: React.FC = ({ + name, + href, + to, + people, + uses, + history, + className, + description, + withGraph = true, +}) => ( +
+
+ + {name ? ( + <> + #{name} + + ) : ( + + )} + + + {description ? ( + {description} + ) : typeof people !== 'undefined' ? ( + + ) : ( + + )} +
+ + {typeof uses !== 'undefined' && ( +
+ +
+ )} + + {withGraph && ( +
+ + 0)} + > + + + +
+ )} +
+); diff --git a/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx index 1b9e3572db..38b3edfb56 100644 --- a/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx +++ b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx @@ -5,7 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import Hashtag from 'flavours/glitch/components/hashtag'; +import { Hashtag } from 'flavours/glitch/components/hashtag'; const messages = defineMessages({ lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' }, diff --git a/app/javascript/flavours/glitch/features/followed_tags/index.jsx b/app/javascript/flavours/glitch/features/followed_tags/index.jsx index 477a51d409..4ac7dddb99 100644 --- a/app/javascript/flavours/glitch/features/followed_tags/index.jsx +++ b/app/javascript/flavours/glitch/features/followed_tags/index.jsx @@ -13,7 +13,7 @@ import { debounce } from 'lodash'; import { expandFollowedHashtags, fetchFollowedHashtags } from 'flavours/glitch/actions/tags'; import ColumnHeader from 'flavours/glitch/components/column_header'; -import Hashtag from 'flavours/glitch/components/hashtag'; +import { Hashtag } from 'flavours/glitch/components/hashtag'; import ScrollableList from 'flavours/glitch/components/scrollable_list'; import Column from 'flavours/glitch/features/ui/components/column';