Widen the clickable area for statuses in grouped notifications (#31111)
parent
871b6197df
commit
a8330be93e
|
@ -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],
|
||||||
|
);
|
||||||
|
|
||||||
|
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}`);
|
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) {
|
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) && (
|
||||||
|
|
|
@ -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}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue