Widen the clickable area for statuses in grouped notifications (#31111)

shrike
Claire 2024-07-23 14:11:08 +02:00 committed by GitHub
parent 871b6197df
commit a8330be93e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 82 deletions

View File

@ -1,4 +1,4 @@
import { useCallback } from 'react';
import { useCallback, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
@ -22,6 +22,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
statusId,
}) => {
const history = useHistory();
const clickCoordinatesRef = useRef<[number, number] | null>();
const status = useAppSelector(
(state) => state.statuses.get(statusId) as Status | undefined,
@ -31,11 +32,69 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
state.accounts.get(status?.get('account') as string),
);
const handleClick = useCallback(() => {
if (!account) return;
const handleMouseDown = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY }) => {
clickCoordinatesRef.current = [clientX, clientY];
},
[clickCoordinatesRef],
);
const handleMouseUp = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY, target, button }) => {
const [startX, startY] = clickCoordinatesRef.current ?? [0, 0];
const [deltaX, deltaY] = [
Math.abs(clientX - startX),
Math.abs(clientY - startY),
];
let element: HTMLDivElement | null = target as HTMLDivElement;
while (element) {
if (
element.localName === 'button' ||
element.localName === 'a' ||
element.localName === 'label'
) {
return;
}
element = element.parentNode as HTMLDivElement | null;
}
if (deltaX + deltaY < 5 && button === 0 && account) {
history.push(`/@${account.acct}/${statusId}`);
}, [statusId, account, history]);
}
clickCoordinatesRef.current = null;
},
[clickCoordinatesRef, statusId, account, history],
);
const handleMouseEnter = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) {
const newSrc = emoji.getAttribute('data-original');
if (newSrc) emoji.src = newSrc;
}
},
[],
);
const handleMouseLeave = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) {
const newSrc = emoji.getAttribute('data-static');
if (newSrc) emoji.src = newSrc;
}
},
[],
);
if (!status) {
return null;
@ -51,7 +110,15 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
).size;
return (
<div className='notification-group__embedded-status'>
<div
className='notification-group__embedded-status'
role='button'
tabIndex={-1}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div className='notification-group__embedded-status__account'>
<Avatar account={account} size={16} />
<DisplayName account={account} />
@ -62,7 +129,6 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
content={contentHtml}
language={language}
mentions={mentions}
onClick={handleClick}
/>
{(poll || mediaAttachmentsSize > 0) && (

View File

@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react';
import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
@ -34,76 +34,10 @@ export const EmbeddedStatusContent: React.FC<{
content: string;
mentions: List<Mention>;
language: string;
onClick?: () => void;
className?: string;
}> = ({ content, mentions, language, onClick, className }) => {
const clickCoordinatesRef = useRef<[number, number] | null>();
}> = ({ content, mentions, language, className }) => {
const history = useHistory();
const handleMouseDown = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY }) => {
clickCoordinatesRef.current = [clientX, clientY];
},
[clickCoordinatesRef],
);
const handleMouseUp = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY, target, button }) => {
const [startX, startY] = clickCoordinatesRef.current ?? [0, 0];
const [deltaX, deltaY] = [
Math.abs(clientX - startX),
Math.abs(clientY - startY),
];
let element: HTMLDivElement | null = target as HTMLDivElement;
while (element) {
if (
element.localName === 'button' ||
element.localName === 'a' ||
element.localName === 'label'
) {
return;
}
element = element.parentNode as HTMLDivElement | null;
}
if (deltaX + deltaY < 5 && button === 0 && onClick) {
onClick();
}
clickCoordinatesRef.current = null;
},
[clickCoordinatesRef, onClick],
);
const handleMouseEnter = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) {
const newSrc = emoji.getAttribute('data-original');
if (newSrc) emoji.src = newSrc;
}
},
[],
);
const handleMouseLeave = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) {
const newSrc = emoji.getAttribute('data-static');
if (newSrc) emoji.src = newSrc;
}
},
[],
);
const handleContentRef = useCallback(
(node: HTMLDivElement | null) => {
if (!node) {
@ -150,16 +84,10 @@ export const EmbeddedStatusContent: React.FC<{
return (
<div
role='button'
tabIndex={0}
className={className}
ref={handleContentRef}
lang={language}
dangerouslySetInnerHTML={{ __html: content }}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
/>
);
};

View File

@ -10457,6 +10457,8 @@ noscript {
}
&__embedded-status {
cursor: pointer;
&__account {
display: flex;
align-items: center;
@ -10478,7 +10480,6 @@ noscript {
font-size: 15px;
line-height: 22px;
color: $dark-text-color;
cursor: pointer;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
max-height: 4 * 22px;