From 6e80bca6be44fa838505d4f199e9a9a448b495c7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 18 Jun 2024 22:56:21 +0200 Subject: [PATCH] Extract link handlers to hook --- app/javascript/hooks/useLinks.ts | 81 +++++++++++++++++++ .../mastodon/components/account_bio.tsx | 75 +---------------- .../mastodon/components/account_fields.tsx | 77 +----------------- .../styles/mastodon-light/variables.scss | 2 +- 4 files changed, 86 insertions(+), 149 deletions(-) create mode 100644 app/javascript/hooks/useLinks.ts diff --git a/app/javascript/hooks/useLinks.ts b/app/javascript/hooks/useLinks.ts new file mode 100644 index 0000000000000..78c439ff34cef --- /dev/null +++ b/app/javascript/hooks/useLinks.ts @@ -0,0 +1,81 @@ +import { useEffect, useRef, useCallback } from 'react'; + +import { useHistory } from 'react-router-dom'; + +import { openURL } from 'mastodon/actions/search'; +import { useAppDispatch } from 'mastodon/store'; + +export const useLinks = (canary: unknown) => { + const ref = useRef(null); + const history = useHistory(); + const dispatch = useAppDispatch(); + + const handleHashtagClick = useCallback( + (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/${textContent.replace(/^#/, '')}`); + } + }, + [history], + ); + + const handleMentionClick = useCallback( + (e: MouseEvent) => { + const { currentTarget } = e; + + if (!(currentTarget instanceof HTMLAnchorElement)) return; + + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { + e.preventDefault(); + + dispatch( + openURL(currentTarget.href, history, () => { + window.location.href = currentTarget.href; + }), + ); + } + }, + [dispatch, history], + ); + + useEffect(() => { + if (!ref.current) { + return; + } + + const links = ref.current.querySelectorAll('a'); + + for (const link of links) { + if ( + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') + ) { + link.addEventListener('click', handleHashtagClick, false); + } else if (link.classList.contains('mention')) { + link.addEventListener('click', handleMentionClick, false); + } + } + + return () => { + for (const link of links) { + if ( + link.textContent?.[0] === '#' || + link.previousSibling?.textContent?.endsWith('#') + ) { + link.removeEventListener('click', handleHashtagClick); + } else if (link.classList.contains('mention')) { + link.removeEventListener('click', handleMentionClick); + } + } + }; + }, [canary, handleHashtagClick, handleMentionClick]); + + return ref; +}; diff --git a/app/javascript/mastodon/components/account_bio.tsx b/app/javascript/mastodon/components/account_bio.tsx index ea9dcc1c0c85d..40437440beb9c 100644 --- a/app/javascript/mastodon/components/account_bio.tsx +++ b/app/javascript/mastodon/components/account_bio.tsx @@ -1,81 +1,10 @@ -import { useEffect, useRef, useCallback } from 'react'; - -import { useHistory } from 'react-router-dom'; - -import { openURL } from 'mastodon/actions/search'; -import { useAppDispatch } from 'mastodon/store'; +import { useLinks } from 'mastodon/../hooks/useLinks'; export const AccountBio: React.FC<{ note: string; className: string; }> = ({ note, className }) => { - const ref = useRef(null); - const history = useHistory(); - const dispatch = useAppDispatch(); - - const handleHashtagClick = useCallback( - (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/${textContent.replace(/^#/, '')}`); - } - }, - [history], - ); - - const handleMentionClick = useCallback( - (e: MouseEvent) => { - const { currentTarget } = e; - if (!(currentTarget instanceof HTMLAnchorElement)) return; - - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - - dispatch( - openURL(currentTarget.href, history, () => { - window.location.href = currentTarget.href; - }), - ); - } - }, - [dispatch, history], - ); - - useEffect(() => { - if (ref.current === null) { - return; - } - - const links = ref.current.querySelectorAll('a'); - - for (const link of links) { - if ( - link.textContent?.[0] === '#' || - link.previousSibling?.textContent?.endsWith('#') - ) { - link.addEventListener('click', handleHashtagClick, false); - } else if (link.classList.contains('mention')) { - link.addEventListener('click', handleMentionClick, false); - } - } - - return () => { - for (const link of links) { - if ( - link.textContent?.[0] === '#' || - link.previousSibling?.textContent?.endsWith('#') - ) { - link.removeEventListener('click', handleHashtagClick); - } else if (link.classList.contains('mention')) { - link.removeEventListener('click', handleMentionClick); - } - } - }; - }, [note, handleHashtagClick, handleMentionClick]); + const ref = useLinks(note); if (note.length === 0 || note === '

') { return null; diff --git a/app/javascript/mastodon/components/account_fields.tsx b/app/javascript/mastodon/components/account_fields.tsx index f34aee0ceaf92..9f112e0b21524 100644 --- a/app/javascript/mastodon/components/account_fields.tsx +++ b/app/javascript/mastodon/components/account_fields.tsx @@ -1,87 +1,14 @@ -import { useEffect, useRef, useCallback } from 'react'; - import classNames from 'classnames'; -import { useHistory } from 'react-router-dom'; import CheckIcon from '@/material-icons/400-24px/check.svg?react'; -import { openURL } from 'mastodon/actions/search'; +import { useLinks } from 'mastodon/../hooks/useLinks'; import { Icon } from 'mastodon/components/icon'; import type { Account } from 'mastodon/models/account'; -import { useAppDispatch } from 'mastodon/store'; export const AccountFields: React.FC<{ fields: Account['fields']; }> = ({ fields }) => { - const ref = useRef(null); - const history = useHistory(); - const dispatch = useAppDispatch(); - - const handleHashtagClick = useCallback( - (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/${textContent.replace(/^#/, '')}`); - } - }, - [history], - ); - - const handleMentionClick = useCallback( - (e: MouseEvent) => { - const { currentTarget } = e; - - if (!(currentTarget instanceof HTMLAnchorElement)) return; - - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - - dispatch( - openURL(currentTarget.href, history, () => { - window.location.href = currentTarget.href; - }), - ); - } - }, - [dispatch, history], - ); - - useEffect(() => { - if (!ref.current) { - return; - } - - const links = ref.current.querySelectorAll('a'); - - for (const link of links) { - if ( - link.textContent?.[0] === '#' || - link.previousSibling?.textContent?.endsWith('#') - ) { - link.addEventListener('click', handleHashtagClick, false); - } else if (link.classList.contains('mention')) { - link.addEventListener('click', handleMentionClick, false); - } - } - - return () => { - for (const link of links) { - if ( - link.textContent?.[0] === '#' || - link.previousSibling?.textContent?.endsWith('#') - ) { - link.removeEventListener('click', handleHashtagClick); - } else if (link.classList.contains('mention')) { - link.removeEventListener('click', handleMentionClick); - } - } - }; - }, [fields, handleHashtagClick, handleMentionClick]); + const ref = useLinks(fields); if (fields.size === 0) { return null; diff --git a/app/javascript/styles/mastodon-light/variables.scss b/app/javascript/styles/mastodon-light/variables.scss index 1e35c9d41ed7f..6d570d2f01c6b 100644 --- a/app/javascript/styles/mastodon-light/variables.scss +++ b/app/javascript/styles/mastodon-light/variables.scss @@ -60,7 +60,7 @@ $emojis-requiring-inversion: 'chains'; --dropdown-border-color: #d9e1e8; --dropdown-background-color: #fff; --modal-border-color: #d9e1e8; - --modal-background-color: #fff; + --modal-background-color: var(--background-color-tint); --background-border-color: #d9e1e8; --background-color: #fff; --background-color-tint: rgba(255, 255, 255, 90%);