@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- import React , { createRef , SyntheticEvent } from 'react' ;
17
+ import React , { createRef , SyntheticEvent , MouseEvent } from 'react' ;
18
18
import ReactDOM from 'react-dom' ;
19
19
import highlight from 'highlight.js' ;
20
20
import { MsgType } from "matrix-js-sdk/src/@types/event" ;
@@ -31,7 +31,7 @@ import SettingsStore from "../../../settings/SettingsStore";
31
31
import ReplyChain from "../elements/ReplyChain" ;
32
32
import { pillifyLinks , unmountPills } from '../../../utils/pillify' ;
33
33
import { IntegrationManagers } from "../../../integrations/IntegrationManagers" ;
34
- import { isPermalinkHost } from "../../../utils/permalinks/Permalinks" ;
34
+ import { isPermalinkHost , tryTransformPermalinkToLocalHref } from "../../../utils/permalinks/Permalinks" ;
35
35
import { copyPlaintext } from "../../../utils/strings" ;
36
36
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton" ;
37
37
import { replaceableComponent } from "../../../utils/replaceableComponent" ;
@@ -47,6 +47,7 @@ import LinkPreviewGroup from '../rooms/LinkPreviewGroup';
47
47
import { IBodyProps } from "./IBodyProps" ;
48
48
import RoomContext from "../../../contexts/RoomContext" ;
49
49
import AccessibleButton from '../elements/AccessibleButton' ;
50
+ import { options as linkifyOpts } from "../../../linkify-matrix" ;
50
51
51
52
const MAX_HIGHLIGHT_LENGTH = 4096 ;
52
53
@@ -418,6 +419,23 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
418
419
} ) ;
419
420
} ;
420
421
422
+ /**
423
+ * This acts as a fallback in-app navigation handler for any body links that
424
+ * were ignored as part of linkification because they were already links
425
+ * to start with (e.g. pills, links in the content).
426
+ */
427
+ private onBodyLinkClick = ( e : MouseEvent ) : void => {
428
+ const target = e . target as Element ;
429
+ if ( target . nodeName !== "A" || target . classList . contains ( linkifyOpts . className ) ) return ;
430
+ const { href } = target as HTMLLinkElement ;
431
+ const localHref = tryTransformPermalinkToLocalHref ( href ) ;
432
+ if ( localHref !== href ) {
433
+ // it could be converted to a localHref -> therefore handle locally
434
+ e . preventDefault ( ) ;
435
+ window . location . hash = localHref ;
436
+ }
437
+ } ;
438
+
421
439
public getEventTileOps = ( ) => ( {
422
440
isWidgetHidden : ( ) => {
423
441
return this . state . widgetHidden ;
@@ -606,7 +624,9 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
606
624
607
625
if ( isEmote ) {
608
626
return (
609
- < div className = "mx_MEmoteBody mx_EventTile_content" >
627
+ < div className = "mx_MEmoteBody mx_EventTile_content"
628
+ onClick = { this . onBodyLinkClick }
629
+ >
610
630
*
611
631
< span
612
632
className = "mx_MEmoteBody_sender"
@@ -622,14 +642,18 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
622
642
}
623
643
if ( isNotice ) {
624
644
return (
625
- < div className = "mx_MNoticeBody mx_EventTile_content" >
645
+ < div className = "mx_MNoticeBody mx_EventTile_content"
646
+ onClick = { this . onBodyLinkClick }
647
+ >
626
648
{ body }
627
649
{ widgets }
628
650
</ div >
629
651
) ;
630
652
}
631
653
return (
632
- < div className = "mx_MTextBody mx_EventTile_content" >
654
+ < div className = "mx_MTextBody mx_EventTile_content"
655
+ onClick = { this . onBodyLinkClick }
656
+ >
633
657
{ body }
634
658
{ widgets }
635
659
</ div >
0 commit comments