From c215bbff3f98e955e7a630a1ae8c0de2aa82a228 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 18 Jun 2024 18:41:35 +0200 Subject: [PATCH] Fix linting --- .../mastodon/components/account_bio.tsx | 37 ++++++++------- .../mastodon/components/account_fields.tsx | 47 ++++++++++--------- .../mastodon/components/follow_button.tsx | 4 +- .../mastodon/components/hover_card.tsx | 35 +++++++++----- .../components/inline_follow_suggestions.jsx | 1 - 5 files changed, 68 insertions(+), 56 deletions(-) diff --git a/app/javascript/mastodon/components/account_bio.tsx b/app/javascript/mastodon/components/account_bio.tsx index 3550a76a74571..ea9dcc1c0c85d 100644 --- a/app/javascript/mastodon/components/account_bio.tsx +++ b/app/javascript/mastodon/components/account_bio.tsx @@ -9,28 +9,35 @@ export const AccountBio: React.FC<{ note: string; className: string; }> = ({ note, className }) => { - const ref = useRef(null); + const ref = useRef(null); const history = useHistory(); const dispatch = useAppDispatch(); const handleHashtagClick = useCallback( - (e) => { - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { + (e: MouseEvent) => { + const { currentTarget } = e; + if (!(currentTarget instanceof HTMLElement)) return; + const { textContent } = currentTarget; + + if (textContent && e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); - history.push(`/tags/${e.currentTarget.textContent.replace(/^#/, '')}`); + history.push(`/tags/${textContent.replace(/^#/, '')}`); } }, [history], ); const handleMentionClick = useCallback( - (e) => { + (e: MouseEvent) => { + const { currentTarget } = e; + if (!(currentTarget instanceof HTMLAnchorElement)) return; + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); dispatch( - openURL(e.currentTarget.href, history, () => { - window.location = e.currentTarget.href; + openURL(currentTarget.href, history, () => { + window.location.href = currentTarget.href; }), ); } @@ -43,15 +50,12 @@ export const AccountBio: React.FC<{ return; } - const links = ref.current.querySelectorAll('a'); + const links = ref.current.querySelectorAll('a'); for (const link of links) { if ( - link.textContent[0] === '#' || - (link.previousSibling?.textContent && - link.previousSibling.textContent[ - link.previousSibling.textContent.length - 1 - ] === '#') + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') ) { link.addEventListener('click', handleHashtagClick, false); } else if (link.classList.contains('mention')) { @@ -62,11 +66,8 @@ export const AccountBio: React.FC<{ return () => { for (const link of links) { if ( - link.textContent[0] === '#' || - (link.previousSibling?.textContent && - link.previousSibling.textContent[ - link.previousSibling.textContent.length - 1 - ] === '#') + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') ) { link.removeEventListener('click', handleHashtagClick); } else if (link.classList.contains('mention')) { diff --git a/app/javascript/mastodon/components/account_fields.tsx b/app/javascript/mastodon/components/account_fields.tsx index c7c78ef29ad62..f34aee0ceaf92 100644 --- a/app/javascript/mastodon/components/account_fields.tsx +++ b/app/javascript/mastodon/components/account_fields.tsx @@ -1,4 +1,3 @@ - import { useEffect, useRef, useCallback } from 'react'; import classNames from 'classnames'; @@ -6,35 +5,45 @@ import { useHistory } from 'react-router-dom'; import CheckIcon from '@/material-icons/400-24px/check.svg?react'; import { openURL } from 'mastodon/actions/search'; -import type { ApiAccountFieldJSON } from 'mastodon/api_types/accounts'; import { Icon } from 'mastodon/components/icon'; +import type { Account } from 'mastodon/models/account'; import { useAppDispatch } from 'mastodon/store'; export const AccountFields: React.FC<{ - fields: ApiAccountFieldJSON[]; + fields: Account['fields']; }> = ({ fields }) => { - const ref = useRef(null); + const ref = useRef(null); const history = useHistory(); const dispatch = useAppDispatch(); const handleHashtagClick = useCallback( - (e) => { + (e: MouseEvent) => { + const { currentTarget } = e; + if (!(currentTarget instanceof HTMLElement)) return; + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { + const { textContent } = currentTarget; + if (!textContent) return; + e.preventDefault(); - history.push(`/tags/${e.currentTarget.textContent.replace(/^#/, '')}`); + history.push(`/tags/${textContent.replace(/^#/, '')}`); } }, [history], ); const handleMentionClick = useCallback( - (e) => { + (e: MouseEvent) => { + const { currentTarget } = e; + + if (!(currentTarget instanceof HTMLAnchorElement)) return; + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); dispatch( - openURL(e.currentTarget.href, history, () => { - window.location = e.currentTarget.href; + openURL(currentTarget.href, history, () => { + window.location.href = currentTarget.href; }), ); } @@ -43,19 +52,16 @@ export const AccountFields: React.FC<{ ); useEffect(() => { - if (ref.current === null) { + if (!ref.current) { return; } - const links = ref.current.querySelectorAll('a'); + const links = ref.current.querySelectorAll('a'); for (const link of links) { if ( - link.textContent[0] === '#' || - (link.previousSibling?.textContent && - link.previousSibling.textContent[ - link.previousSibling.textContent.length - 1 - ] === '#') + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') ) { link.addEventListener('click', handleHashtagClick, false); } else if (link.classList.contains('mention')) { @@ -66,11 +72,8 @@ export const AccountFields: React.FC<{ return () => { for (const link of links) { if ( - link.textContent[0] === '#' || - (link.previousSibling?.textContent && - link.previousSibling.textContent[ - link.previousSibling.textContent.length - 1 - ] === '#') + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') ) { link.removeEventListener('click', handleHashtagClick); } else if (link.classList.contains('mention')) { @@ -96,7 +99,7 @@ export const AccountFields: React.FC<{ className='translate' /> -
+
{pair.get('verified_at') && ( )} diff --git a/app/javascript/mastodon/components/follow_button.tsx b/app/javascript/mastodon/components/follow_button.tsx index ef2b3436fc3b0..e7905893a549f 100644 --- a/app/javascript/mastodon/components/follow_button.tsx +++ b/app/javascript/mastodon/components/follow_button.tsx @@ -2,7 +2,6 @@ import { useCallback, useEffect } from 'react'; import { useIntl, defineMessages } from 'react-intl'; - import { fetchRelationships, followAccount, @@ -11,7 +10,7 @@ import { import { Button } from 'mastodon/components/button'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { me } from 'mastodon/initial_state'; -import { useAppDispatch , useAppSelector } from 'mastodon/store'; +import { useAppDispatch, useAppSelector } from 'mastodon/store'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -35,6 +34,7 @@ export const FollowButton: React.FC<{ }, [dispatch, accountId]); const handleClick = useCallback(() => { + if (!relationship) return; if (accountId === me) { return; } else if (relationship.following || relationship.requested) { diff --git a/app/javascript/mastodon/components/hover_card.tsx b/app/javascript/mastodon/components/hover_card.tsx index 0c6d3c4343989..d780fd4833f7c 100644 --- a/app/javascript/mastodon/components/hover_card.tsx +++ b/app/javascript/mastodon/components/hover_card.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; import { Link } from 'react-router-dom'; import Overlay from 'react-overlays/Overlay'; +import type { OffsetValue } from 'react-overlays/esm/usePopper'; import { fetchAccount } from 'mastodon/actions/accounts'; import { AccountBio } from 'mastodon/components/account_bio'; @@ -17,34 +18,40 @@ import { ShortNumber } from 'mastodon/components/short_number'; import { domain } from 'mastodon/initial_state'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; -const offset = [-12, 4]; +const offset = [-12, 4] as OffsetValue; const enterDelay = 100; const leaveDelay = 500; export const HoverCard: React.FC = () => { const [open, setOpen] = useState(false); - const [accountId, setAccountId] = useState(); - const cardRef = useRef(); - const anchorRef = useRef(); + const [accountId, setAccountId] = useState(); + const cardRef = useRef(null); + const anchorRef = useRef(null); const dispatch = useAppDispatch(); - const account = useAppSelector((state) => state.accounts.get(accountId)); - const leaveTimerRef = useRef(); - const enterTimerRef = useRef(); + const account = useAppSelector((state) => + accountId ? state.accounts.get(accountId) : undefined, + ); + const leaveTimerRef = useRef>(); + const enterTimerRef = useRef>(); const handleAnchorMouseEnter = useCallback( - (e) => { - if (e.target.matches('[data-hover-card]')) { + (e: MouseEvent) => { + const { target } = e; + + if (!(target instanceof HTMLElement)) return; + + if (target.matches('[data-hover-card]')) { clearTimeout(leaveTimerRef.current); clearTimeout(enterTimerRef.current); enterTimerRef.current = setTimeout(() => { - anchorRef.current = e.target; + anchorRef.current = target; setOpen(true); - setAccountId(e.target.getAttribute('data-hover-card')); + setAccountId(target.getAttribute('data-hover-card') ?? undefined); }, enterDelay); } - if (e.target === cardRef.current?.parentNode) { + if (target === cardRef.current?.parentNode) { clearTimeout(leaveTimerRef.current); } }, @@ -52,7 +59,7 @@ export const HoverCard: React.FC = () => { ); const handleAnchorMouseLeave = useCallback( - (e) => { + (e: MouseEvent) => { if ( e.target === anchorRef.current || e.target === cardRef.current?.parentNode @@ -96,6 +103,8 @@ export const HoverCard: React.FC = () => { dispatch(fetchAccount(accountId)); }, [dispatch, accountId]); + if (!accountId) return null; + return ( { const intl = useIntl(); const account = useSelector(state => state.getIn(['accounts', id])); - const relationship = useSelector(state => state.getIn(['relationships', id])); const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); const dispatch = useDispatch();