feat: make links in chat clickable
This commit is contained in:
parent
e8eb7e1e1f
commit
bed3f62536
|
@ -9109,9 +9109,9 @@
|
|||
"from": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d"
|
||||
},
|
||||
"linkify-it": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
|
||||
"integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||
"requires": {
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
|
@ -12203,13 +12203,12 @@
|
|||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-linkify": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-linkify/-/react-linkify-0.2.2.tgz",
|
||||
"integrity": "sha512-0S8cvUNtEgfJpIGDPKklyrnrTffJ63WuJAc4KaYLBihl5TjgH5cHUmYD+AXLpsV+CVmfoo/56SUNfrZcY4zYMQ==",
|
||||
"version": "1.0.0-alpha",
|
||||
"resolved": "https://registry.npmjs.org/react-linkify/-/react-linkify-1.0.0-alpha.tgz",
|
||||
"integrity": "sha512-7gcIUvJkAXXttt1fmBK9cwn+1jTa4hbKLGCZ9J1U6EOkyb2/+LKL1Z28d9rtDLMnpvImlNlLPdTPooorl5cpmg==",
|
||||
"requires": {
|
||||
"linkify-it": "^2.0.3",
|
||||
"prop-types": "^15.5.8",
|
||||
"tlds": "^1.57.0"
|
||||
"tlds": "^1.199.0"
|
||||
}
|
||||
},
|
||||
"react-native": {
|
||||
|
@ -15553,9 +15552,9 @@
|
|||
"integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
|
||||
"integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||
},
|
||||
"uglify-es": {
|
||||
"version": "3.3.9",
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
"react-dom": "16.8.3",
|
||||
"react-emoji-render": "0.4.6",
|
||||
"react-i18next": "10.11.4",
|
||||
"react-linkify": "0.2.2",
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native": "0.59.8",
|
||||
"react-native-background-timer": "2.1.1",
|
||||
"react-native-calendar-events": "1.6.4",
|
||||
|
|
|
@ -18,12 +18,12 @@ type Props = {
|
|||
/**
|
||||
* Notifies that this Link failed to open the URL associated with it.
|
||||
*/
|
||||
onLinkingOpenURLRejected: Function,
|
||||
onLinkingOpenURLRejected?: Function,
|
||||
|
||||
/**
|
||||
* The CSS style to be applied to this Link for the purposes of display.
|
||||
*/
|
||||
style: Object,
|
||||
style?: Object,
|
||||
|
||||
/**
|
||||
* The URL to be opened when this Link is clicked/pressed.
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import ReactLinkify from 'react-linkify';
|
||||
|
||||
import { type StyleType } from '../../../styles';
|
||||
|
||||
import Link from './Link';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The children of the component.
|
||||
*/
|
||||
children: React$Node,
|
||||
|
||||
/**
|
||||
* The extra styles to be applied to links.
|
||||
*/
|
||||
linkStyle: StyleType
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a react native wrapper for the react-linkify component.
|
||||
*/
|
||||
export default class Linkify extends Component<Props> {
|
||||
/**
|
||||
* Initiates a new {@code Component}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._componentDecorator = this._componentDecorator.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<ReactLinkify
|
||||
componentDecorator = { this._componentDecorator }>
|
||||
<Text>
|
||||
{ this.props.children }
|
||||
</Text>
|
||||
</ReactLinkify>
|
||||
);
|
||||
}
|
||||
|
||||
_componentDecorator: (string, string, number) => React$Node;
|
||||
|
||||
/**
|
||||
* Implements a component decorator for react-linkify.
|
||||
*
|
||||
* @param {string} decoratedHref - The href src.
|
||||
* @param {string} decoratedText - The link text.
|
||||
* @param {string} key - The component key.
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
_componentDecorator(decoratedHref: string, decoratedText: string, key: number) {
|
||||
return (
|
||||
<Link
|
||||
key = { key }
|
||||
style = { this.props.linkStyle }
|
||||
url = { decoratedHref }>
|
||||
{decoratedText}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ export { default as HeaderLabel } from './HeaderLabel';
|
|||
export { default as HeaderWithNavigation } from './HeaderWithNavigation';
|
||||
export { default as Image } from './Image';
|
||||
export { default as Link } from './Link';
|
||||
export { default as Linkify } from './Linkify';
|
||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||
export { default as Modal } from './Modal';
|
||||
export { default as NavigateSectionListEmptyComponent } from
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import ReactLinkify from 'react-linkify';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The children of the component.
|
||||
*/
|
||||
children: React$Node
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a react wrapper for the react-linkify component.
|
||||
*/
|
||||
export default class Linkify extends Component<Props> {
|
||||
/**
|
||||
* Implements {@Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<ReactLinkify
|
||||
componentDecorator = { this._componentDecorator }>
|
||||
{ this.props.children }
|
||||
</ReactLinkify>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a component decorator for react-linkify.
|
||||
*
|
||||
* @param {string} decoratedHref - The href src.
|
||||
* @param {string} decoratedText - The link text.
|
||||
* @param {string} key - The component key.
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
_componentDecorator(decoratedHref: string, decoratedText: string, key: number) {
|
||||
return (
|
||||
<a
|
||||
href = { decoratedHref }
|
||||
key = { key }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{decoratedText}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ export { default as BaseIndicator } from './BaseIndicator';
|
|||
export { default as Button } from './Button';
|
||||
export { default as Container } from './Container';
|
||||
export { default as Image } from './Image';
|
||||
export { default as Linkify } from './Linkify';
|
||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||
export { default as MeetingsList } from './MeetingsList';
|
||||
export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete';
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Text, View } from 'react-native';
|
|||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Linkify } from '../../../base/react';
|
||||
|
||||
import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
|
||||
import styles from './styles';
|
||||
|
@ -42,6 +43,13 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
textWrapperStyle.push(styles.systemTextWrapper);
|
||||
}
|
||||
|
||||
const messageText = message.messageType === 'error'
|
||||
? this.props.t('chat.error', {
|
||||
error: message.error,
|
||||
originalText: message.message
|
||||
})
|
||||
: message.message;
|
||||
|
||||
return (
|
||||
<View style = { styles.messageWrapper } >
|
||||
{ this._renderAvatar() }
|
||||
|
@ -51,14 +59,9 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
this.props.showDisplayName
|
||||
&& this._renderDisplayName()
|
||||
}
|
||||
<Text style = { styles.messageText }>
|
||||
{ message.messageType === 'error'
|
||||
? this.props.t('chat.error', {
|
||||
error: message.error,
|
||||
originalText: message.message
|
||||
})
|
||||
: message.message }
|
||||
</Text>
|
||||
<Linkify linkStyle = { styles.chatLink }>
|
||||
{ messageText }
|
||||
</Linkify>
|
||||
</View>
|
||||
{ this.props.showTimestamp && this._renderTimestamp() }
|
||||
</View>
|
||||
|
|
|
@ -36,6 +36,7 @@ export default class MessageContainer extends AbstractMessageContainer {
|
|||
data = { this._getMessagesGroupedBySender() }
|
||||
inverted = { true }
|
||||
keyExtractor = { this._keyExtractor }
|
||||
keyboardShouldPersistTaps = 'always'
|
||||
renderItem = { this._renderMessageGroup }
|
||||
style = { styles.messageContainer } />
|
||||
);
|
||||
|
|
|
@ -34,6 +34,10 @@ export default {
|
|||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
chatLink: {
|
||||
color: ColorPalette.blue
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrapper for the details together, such as name, message and time.
|
||||
*/
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import { toArray } from 'react-emoji-render';
|
||||
import Linkify from 'react-linkify';
|
||||
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Linkify } from '../../../base/react';
|
||||
|
||||
import AbstractChatMessage, {
|
||||
type Props
|
||||
|
@ -43,13 +43,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
|
||||
content.forEach(i => {
|
||||
if (typeof i === 'string') {
|
||||
processedMessage.push(
|
||||
<Linkify
|
||||
key = { i }
|
||||
properties = {{
|
||||
rel: 'noopener noreferrer',
|
||||
target: '_blank'
|
||||
}}>{ i }</Linkify>);
|
||||
processedMessage.push(<Linkify key = { i }>{ i }</Linkify>);
|
||||
} else {
|
||||
processedMessage.push(i);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue