Add body link fallback handler for in-app navigation (#7627)

This commit is contained in:
J. Ryan Stinnett 2022-01-25 14:31:00 +00:00 committed by GitHub
parent 51fd4d82fd
commit fad65f9582
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 11 deletions

View file

@ -176,8 +176,9 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
attribs.target = '_blank'; // by default attribs.target = '_blank'; // by default
const transformed = tryTransformPermalinkToLocalHref(attribs.href); // only used to check if it is a link that can be handled locally const transformed = tryTransformPermalinkToLocalHref(attribs.href); // only used to check if it is a link that can be handled locally
if (transformed !== attribs.href || // it could be converted so handle locally symbols e.g. @user:server.tdl, matrix: and matrix.to if (
attribs.href.match(ELEMENT_URL_PATTERN) // for https:vector|riot... transformed !== attribs.href || // it could be converted so handle locally symbols e.g. @user:server.tdl, matrix: and matrix.to
attribs.href.match(ELEMENT_URL_PATTERN) // for https links to Element domains
) { ) {
delete attribs.target; delete attribs.target;
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { createRef, SyntheticEvent } from 'react'; import React, { createRef, SyntheticEvent, MouseEvent } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import highlight from 'highlight.js'; import highlight from 'highlight.js';
import { MsgType } from "matrix-js-sdk/src/@types/event"; import { MsgType } from "matrix-js-sdk/src/@types/event";
@ -31,7 +31,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import ReplyChain from "../elements/ReplyChain"; import ReplyChain from "../elements/ReplyChain";
import { pillifyLinks, unmountPills } from '../../../utils/pillify'; import { pillifyLinks, unmountPills } from '../../../utils/pillify';
import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import { isPermalinkHost } from "../../../utils/permalinks/Permalinks"; import { isPermalinkHost, tryTransformPermalinkToLocalHref } from "../../../utils/permalinks/Permalinks";
import { copyPlaintext } from "../../../utils/strings"; import { copyPlaintext } from "../../../utils/strings";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
@ -47,6 +47,7 @@ import LinkPreviewGroup from '../rooms/LinkPreviewGroup';
import { IBodyProps } from "./IBodyProps"; import { IBodyProps } from "./IBodyProps";
import RoomContext from "../../../contexts/RoomContext"; import RoomContext from "../../../contexts/RoomContext";
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import { options as linkifyOpts } from "../../../linkify-matrix";
const MAX_HIGHLIGHT_LENGTH = 4096; const MAX_HIGHLIGHT_LENGTH = 4096;
@ -418,6 +419,23 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}); });
}; };
/**
* This acts as a fallback in-app navigation handler for any body links that
* were ignored as part of linkification because they were already links
* to start with (e.g. pills, links in the content).
*/
private onBodyLinkClick = (e: MouseEvent): void => {
const target = e.target as Element;
if (target.nodeName !== "A" || target.classList.contains(linkifyOpts.className)) return;
const { href } = target as HTMLLinkElement;
const localHref = tryTransformPermalinkToLocalHref(href);
if (localHref !== href) {
// it could be converted to a localHref -> therefore handle locally
e.preventDefault();
window.location.hash = localHref;
}
};
public getEventTileOps = () => ({ public getEventTileOps = () => ({
isWidgetHidden: () => { isWidgetHidden: () => {
return this.state.widgetHidden; return this.state.widgetHidden;
@ -606,7 +624,9 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
if (isEmote) { if (isEmote) {
return ( return (
<div className="mx_MEmoteBody mx_EventTile_content"> <div className="mx_MEmoteBody mx_EventTile_content"
onClick={this.onBodyLinkClick}
>
*&nbsp; *&nbsp;
<span <span
className="mx_MEmoteBody_sender" className="mx_MEmoteBody_sender"
@ -622,14 +642,18 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
} }
if (isNotice) { if (isNotice) {
return ( return (
<div className="mx_MNoticeBody mx_EventTile_content"> <div className="mx_MNoticeBody mx_EventTile_content"
onClick={this.onBodyLinkClick}
>
{ body } { body }
{ widgets } { widgets }
</div> </div>
); );
} }
return ( return (
<div className="mx_MTextBody mx_EventTile_content"> <div className="mx_MTextBody mx_EventTile_content"
onClick={this.onBodyLinkClick}
>
{ body } { body }
{ widgets } { widgets }
</div> </div>

View file

@ -21,7 +21,6 @@ import linkifyString from '@matrix-org/linkify-string';
import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { registerCustomProtocol, registerPlugin } from '@matrix-org/linkifyjs'; import { registerCustomProtocol, registerPlugin } from '@matrix-org/linkifyjs';
//linkifyjs/src/core/fsm
import { baseUrl } from "./utils/permalinks/MatrixToPermalinkConstructor"; import { baseUrl } from "./utils/permalinks/MatrixToPermalinkConstructor";
import { import {
parsePermalink, parsePermalink,
@ -227,8 +226,9 @@ export const options = {
if (type === Type.URL) { if (type === Type.URL) {
try { try {
const transformed = tryTransformPermalinkToLocalHref(href); const transformed = tryTransformPermalinkToLocalHref(href);
if (transformed !== href || // if it could be converted to handle locally for matrix symbols e.g. @user:server.tdl and matrix.to if (
decodeURIComponent(href).match(ELEMENT_URL_PATTERN) // for https:vector|riot... transformed !== href || // if it could be converted to handle locally for matrix symbols e.g. @user:server.tdl and matrix.to
decodeURIComponent(href).match(ELEMENT_URL_PATTERN) // for https links to Element domains
) { ) {
return null; return null;
} else { } else {

View file

@ -328,7 +328,7 @@ export function tryTransformEntityToPermalink(entity: string): string {
if (!entity) return null; if (!entity) return null;
// Check to see if it is a bare entity for starters // Check to see if it is a bare entity for starters
{if (entity[0] === '#' || entity[0] === '!') return makeRoomPermalink(entity);} if (entity[0] === '#' || entity[0] === '!') return makeRoomPermalink(entity);
if (entity[0] === '@') return makeUserPermalink(entity); if (entity[0] === '@') return makeUserPermalink(entity);
if (entity[0] === '+') return makeGroupPermalink(entity); if (entity[0] === '+') return makeGroupPermalink(entity);