feat: make links in chat clickable

This commit is contained in:
Bettenbuk Zoltan 2019-08-05 12:46:10 +02:00 committed by Zoltan Bettenbuk
parent e8eb7e1e1f
commit bed3f62536
11 changed files with 159 additions and 30 deletions

21
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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.

View File

@ -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>
);
}
}

View File

@ -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

View File

@ -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>
);
}
}

View File

@ -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';

View File

@ -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>

View File

@ -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 } />
);

View File

@ -34,6 +34,10 @@ export default {
flexDirection: 'column'
},
chatLink: {
color: ColorPalette.blue
},
/**
* Wrapper for the details together, such as name, message and time.
*/

View File

@ -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);
}