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

pull/2791/head
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'; import { FormattedMessage } from 'react-intl';
@ -22,6 +22,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
statusId, statusId,
}) => { }) => {
const history = useHistory(); const history = useHistory();
const clickCoordinatesRef = useRef<[number, number] | null>();
const status = useAppSelector( const status = useAppSelector(
(state) => state.statuses.get(statusId) as Status | undefined, (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), state.accounts.get(status?.get('account') as string),
); );
const handleClick = useCallback(() => { const handleMouseDown = useCallback<React.MouseEventHandler<HTMLDivElement>>(
if (!account) return; ({ clientX, clientY }) => {
clickCoordinatesRef.current = [clientX, clientY];
},
[clickCoordinatesRef],
);
history.push(`/@${account.acct}/${statusId}`); const handleMouseUp = useCallback<React.MouseEventHandler<HTMLDivElement>>(
}, [statusId, account, history]); ({ 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}`);
}
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) { if (!status) {
return null; return null;
@ -51,7 +110,15 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
).size; ).size;
return ( 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'> <div className='notification-group__embedded-status__account'>
<Avatar account={account} size={16} /> <Avatar account={account} size={16} />
<DisplayName account={account} /> <DisplayName account={account} />
@ -62,7 +129,6 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
content={contentHtml} content={contentHtml}
language={language} language={language}
mentions={mentions} mentions={mentions}
onClick={handleClick}
/> />
{(poll || mediaAttachmentsSize > 0) && ( {(poll || mediaAttachmentsSize > 0) && (

View File

@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react'; import { useCallback } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
@ -34,76 +34,10 @@ export const EmbeddedStatusContent: React.FC<{
content: string; content: string;
mentions: List<Mention>; mentions: List<Mention>;
language: string; language: string;
onClick?: () => void;
className?: string; className?: string;
}> = ({ content, mentions, language, onClick, className }) => { }> = ({ content, mentions, language, className }) => {
const clickCoordinatesRef = useRef<[number, number] | null>();
const history = useHistory(); 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( const handleContentRef = useCallback(
(node: HTMLDivElement | null) => { (node: HTMLDivElement | null) => {
if (!node) { if (!node) {
@ -150,16 +84,10 @@ export const EmbeddedStatusContent: React.FC<{
return ( return (
<div <div
role='button'
tabIndex={0}
className={className} className={className}
ref={handleContentRef} ref={handleContentRef}
lang={language} lang={language}
dangerouslySetInnerHTML={{ __html: content }} dangerouslySetInnerHTML={{ __html: content }}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
/> />
); );
}; };

View File

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