mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(links): warn on embed link middle clicks (#3305)
This commit is contained in:
1 parent
abfaff16c2
commit
ebf91ccc76
3 files changed
+30
-62
No files matched your search
@@ -8,6 +8,7 @@ import {
|
||||
} from '@app/features/channel/components/embeds/channel_embed/ChannelEmbedShared';
|
||||
import {SUPPRESS_EMBEDS_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import * as MessageCommands from '@app/features/messaging/commands/MessageCommands';
|
||||
import {useOpenInBrowserOnMiddleClick} from '@app/features/messaging/hooks/useOpenInBrowserOnMiddleClick';
|
||||
import type {Message} from '@app/features/messaging/models/MessagingMessage';
|
||||
import {openExternalUrlWithWarning} from '@app/features/messaging/utils/ExternalLinkUtils';
|
||||
import TrustedDomain from '@app/features/trusted_domain/state/TrustedDomain';
|
||||
@@ -31,6 +32,7 @@ export const EmbedLink: FC<LinkComponentProps> = observer(({url, children, class
|
||||
logger.warn('Invalid URL in embed link:', url);
|
||||
}
|
||||
};
|
||||
const openInBrowser = useOpenInBrowserOnMiddleClick(url);
|
||||
return (
|
||||
<FocusRing data-flx="channel.embeds.embed.link-component.focus-ring">
|
||||
<a
|
||||
@@ -39,6 +41,7 @@ export const EmbedLink: FC<LinkComponentProps> = observer(({url, children, class
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={handleClick}
|
||||
onAuxClick={openInBrowser.onAuxClick}
|
||||
data-flx="channel.embeds.embed.link-component.a"
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -31,8 +31,11 @@ import {
|
||||
type RendererProps,
|
||||
} from '@app/features/messaging/components/markdown/renderers/RendererTypes';
|
||||
import {ExternalLinkWarningModal} from '@app/features/messaging/components/modals/ExternalLinkWarningModal';
|
||||
import {
|
||||
MIDDLE_MOUSE_BUTTON,
|
||||
useOpenInBrowserOnMiddleClick,
|
||||
} from '@app/features/messaging/hooks/useOpenInBrowserOnMiddleClick';
|
||||
import AttachmentUrlRefresher from '@app/features/messaging/state/AttachmentUrlRefresher';
|
||||
import {openExternalUrlWithWarning} from '@app/features/messaging/utils/ExternalLinkUtils';
|
||||
import {goToMessage} from '@app/features/messaging/utils/MessageNavigator';
|
||||
import type {LinkNode} from '@app/features/messaging/utils/markdown/parser/Nodes';
|
||||
import * as NavigationCommands from '@app/features/navigation/commands/NavigationCommands';
|
||||
@@ -205,6 +208,19 @@ function promptForJumpGate(channel: Channel, scope: string | null | undefined, o
|
||||
});
|
||||
}
|
||||
|
||||
function useJumpLinkAuxClick(url: string, interactive: boolean) {
|
||||
return useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== MIDDLE_MOUSE_BUTTON) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (url) void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
}
|
||||
|
||||
type CompactGuildMentionIconStyle = React.CSSProperties & {
|
||||
'--jump-link-guild-icon-image'?: string;
|
||||
};
|
||||
@@ -324,16 +340,7 @@ const JumpLinkMention = observer(function JumpLinkMention({
|
||||
},
|
||||
[channel, guild?.id, interactive, navigateToJumpTarget],
|
||||
);
|
||||
const handleAuxClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== 1) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (url) void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useJumpLinkAuxClick(url, interactive);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
@@ -469,16 +476,7 @@ function InaccessibleJumpLinkMention({url, i18n, interactive = true}: Inaccessib
|
||||
},
|
||||
[i18n, interactive],
|
||||
);
|
||||
const handleAuxClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== 1) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (url) void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useJumpLinkAuxClick(url, interactive);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
@@ -555,16 +553,7 @@ const DiscoverableJumpLinkMention = observer(function DiscoverableJumpLinkMentio
|
||||
},
|
||||
[channelId, guildId, interactive, joining, messageId],
|
||||
);
|
||||
const handleAuxClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== 1) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useJumpLinkAuxClick(url, interactive);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
@@ -705,16 +694,7 @@ function SettingsJumpLinkMention({target, url, i18n, interactive = true}: Settin
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== 1) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (url) void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useJumpLinkAuxClick(url, interactive);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
@@ -808,16 +788,7 @@ function AppPageJumpLinkMention({page, url, i18n, interactive = true}: AppPageJu
|
||||
},
|
||||
[interactive, page],
|
||||
);
|
||||
const handleAuxClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
if (event.button !== 1) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
void openExternalUrl(url);
|
||||
},
|
||||
[interactive, url],
|
||||
);
|
||||
const handleAuxClick = useJumpLinkAuxClick(url, interactive);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement | HTMLSpanElement>) => {
|
||||
if (!interactive) return;
|
||||
@@ -876,6 +847,7 @@ export const LinkRenderer = observer(function LinkRenderer({
|
||||
}: RendererProps<LinkNode>): React.ReactElement {
|
||||
const i18n = options.i18n!;
|
||||
const {url, text} = node;
|
||||
const openInBrowser = useOpenInBrowserOnMiddleClick(url);
|
||||
const content = text ? renderChildren([text], LINK_CONTENT_OPTION_OVERRIDES) : url;
|
||||
const inviteCode = InviteUtils.findInvite(url);
|
||||
const themeCode = ThemeUtils.findTheme(url);
|
||||
@@ -1199,14 +1171,7 @@ export const LinkRenderer = observer(function LinkRenderer({
|
||||
void openExternalUrl(url);
|
||||
}
|
||||
}}
|
||||
onAuxClick={(e) => {
|
||||
if (e.button !== 1 || isInternal) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openExternalUrlWithWarning(url);
|
||||
}}
|
||||
onAuxClick={isInternal ? undefined : openInBrowser.onAuxClick}
|
||||
className={markupStyles.link}
|
||||
data-flx="messaging.markdown.renderers.link-renderer.a"
|
||||
>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {openExternalUrl} from '@app/features/ui/utils/NativeUtils';
|
||||
import {openExternalUrlWithWarning} from '@app/features/messaging/utils/ExternalLinkUtils';
|
||||
import type React from 'react';
|
||||
import {useMemo} from 'react';
|
||||
|
||||
const MIDDLE_MOUSE_BUTTON = 1;
|
||||
export const MIDDLE_MOUSE_BUTTON = 1;
|
||||
|
||||
interface MiddleClickOpenHandlers {
|
||||
onMouseDown: (event: React.MouseEvent) => void;
|
||||
@@ -21,7 +21,7 @@ export function useOpenInBrowserOnMiddleClick(url: string | null | undefined, en
|
||||
if (event.button !== MIDDLE_MOUSE_BUTTON || !enabled || !url) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
void openExternalUrl(url);
|
||||
openExternalUrlWithWarning(url);
|
||||
},
|
||||
}),
|
||||
[url, enabled],
|
||||
|
||||
Reference in new issue
Block a user