diff --git a/package-lock.json b/package-lock.json index 771b0bb0f4..d43ba49f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 13394a446d..ac5771daaf 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/react/features/base/react/components/native/Link.js b/react/features/base/react/components/native/Link.js index 2297f8a2be..252b5694b9 100644 --- a/react/features/base/react/components/native/Link.js +++ b/react/features/base/react/components/native/Link.js @@ -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. diff --git a/react/features/base/react/components/native/Linkify.js b/react/features/base/react/components/native/Linkify.js new file mode 100644 index 0000000000..f80159958a --- /dev/null +++ b/react/features/base/react/components/native/Linkify.js @@ -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 { + /** + * Initiates a new {@code Component}. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._componentDecorator = this._componentDecorator.bind(this); + } + + /** + * Implements {@code Component#render}. + * + * @inheritdoc + */ + render() { + return ( + + + { this.props.children } + + + ); + } + + _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 ( + + {decoratedText} + + ); + } +} diff --git a/react/features/base/react/components/native/index.js b/react/features/base/react/components/native/index.js index 643c247723..10de6d84ed 100644 --- a/react/features/base/react/components/native/index.js +++ b/react/features/base/react/components/native/index.js @@ -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 diff --git a/react/features/base/react/components/web/Linkify.js b/react/features/base/react/components/web/Linkify.js new file mode 100644 index 0000000000..f813c71870 --- /dev/null +++ b/react/features/base/react/components/web/Linkify.js @@ -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 { + /** + * Implements {@Component#render}. + * + * @inheritdoc + */ + render() { + return ( + + { this.props.children } + + ); + } + + /** + * 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 ( + + {decoratedText} + + ); + } +} diff --git a/react/features/base/react/components/web/index.js b/react/features/base/react/components/web/index.js index 55dcb8b963..7306381a66 100644 --- a/react/features/base/react/components/web/index.js +++ b/react/features/base/react/components/web/index.js @@ -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'; diff --git a/react/features/chat/components/native/ChatMessage.js b/react/features/chat/components/native/ChatMessage.js index 692669514e..48f732ae89 100644 --- a/react/features/chat/components/native/ChatMessage.js +++ b/react/features/chat/components/native/ChatMessage.js @@ -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 { textWrapperStyle.push(styles.systemTextWrapper); } + const messageText = message.messageType === 'error' + ? this.props.t('chat.error', { + error: message.error, + originalText: message.message + }) + : message.message; + return ( { this._renderAvatar() } @@ -51,14 +59,9 @@ class ChatMessage extends AbstractChatMessage { this.props.showDisplayName && this._renderDisplayName() } - - { message.messageType === 'error' - ? this.props.t('chat.error', { - error: message.error, - originalText: message.message - }) - : message.message } - + + { messageText } + { this.props.showTimestamp && this._renderTimestamp() } diff --git a/react/features/chat/components/native/MessageContainer.js b/react/features/chat/components/native/MessageContainer.js index 8ee3dce563..a2a6c23fe9 100644 --- a/react/features/chat/components/native/MessageContainer.js +++ b/react/features/chat/components/native/MessageContainer.js @@ -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 } /> ); diff --git a/react/features/chat/components/native/styles.js b/react/features/chat/components/native/styles.js index 7a54d29e18..d18baa213f 100644 --- a/react/features/chat/components/native/styles.js +++ b/react/features/chat/components/native/styles.js @@ -34,6 +34,10 @@ export default { flexDirection: 'column' }, + chatLink: { + color: ColorPalette.blue + }, + /** * Wrapper for the details together, such as name, message and time. */ diff --git a/react/features/chat/components/web/ChatMessage.js b/react/features/chat/components/web/ChatMessage.js index 4ff1f6e35b..ff58c1ab95 100644 --- a/react/features/chat/components/web/ChatMessage.js +++ b/react/features/chat/components/web/ChatMessage.js @@ -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 { content.forEach(i => { if (typeof i === 'string') { - processedMessage.push( - { i }); + processedMessage.push({ i }); } else { processedMessage.push(i); }