diff --git a/app/components/formatted_text.js b/app/components/formatted_text.js
index a84ec74e8..299edf0f9 100644
--- a/app/components/formatted_text.js
+++ b/app/components/formatted_text.js
@@ -1,7 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React, {Component} from 'react';
+import {Component, createElement, isValidElement} from 'react';
import PropTypes from 'prop-types';
import {Text} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
@@ -27,11 +27,67 @@ class FormattedText extends Component {
...props
} = this.props;
- return (
-
- {intl.formatMessage({id, defaultMessage}, values)}
-
- );
+ let tokenDelimiter;
+ let tokenizedValues;
+ let elements;
+ const hasValues = values && Object.keys(values).length > 0;
+ if (hasValues) {
+ // Creates a token with a random UID that should not be guessable or
+ // conflict with other parts of the `message` string.
+ const uid = Math.floor(Math.random() * 0x10000000000).toString(16);
+
+ const generateToken = (() => {
+ let counter = 0;
+ return () => {
+ const elementId = `ELEMENT-${uid}-${counter += 1}`;
+ return elementId;
+ };
+ })();
+
+ // Splitting with a delimiter to support IE8. When using a regex
+ // with a capture group IE8 does not include the capture group in
+ // the resulting array.
+ tokenDelimiter = `@__${uid}__@`;
+ tokenizedValues = {};
+ elements = {};
+
+ // Iterates over the `props` to keep track of any React Element
+ // values so they can be represented by the `token` as a placeholder
+ // when the `message` is formatted. This allows the formatted
+ // message to then be broken-up into parts with references to the
+ // React Elements inserted back in.
+ Object.keys(values).forEach((name) => {
+ const value = values[name];
+
+ if (isValidElement(value)) {
+ const token = generateToken();
+ tokenizedValues[name] = tokenDelimiter + token + tokenDelimiter;
+ elements[token] = value;
+ } else {
+ tokenizedValues[name] = value;
+ }
+ });
+ }
+
+ const descriptor = {id, defaultMessage};
+ const formattedMessage = intl.formatMessage(descriptor, tokenizedValues || values);
+ const hasElements = elements && Object.keys(elements).length > 0;
+
+ let nodes;
+ if (hasElements) {
+ // Split the message into parts so the React Element values captured
+ // above can be inserted back into the rendered message. This
+ // approach allows messages to render with React Elements while
+ // keeping React's virtual diffing working properly.
+ nodes = formattedMessage.
+ split(tokenDelimiter).
+ filter((part) => Boolean(part)).
+ map((part) => elements[part] || part);
+ } else {
+ nodes = [formattedMessage];
+ }
+
+ return createElement(Text, props, ...nodes);
}
}
diff --git a/app/screens/about/about.js b/app/screens/about/about.js
index ad7f533bb..9e432256e 100644
--- a/app/screens/about/about.js
+++ b/app/screens/about/about.js
@@ -18,6 +18,7 @@ import StatusBar from 'app/components/status_bar';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import MattermostIcon from 'app/components/mattermost_icon';
+import Config from 'assets/config';
export default class About extends PureComponent {
static propTypes = {
@@ -34,6 +35,14 @@ export default class About extends PureComponent {
Linking.openURL('http://about.mattermost.com/');
};
+ handlePlatformNotice = () => {
+ Linking.openURL(Config.PlatformNoticeURL);
+ };
+
+ handleMobileNotice = () => {
+ Linking.openURL(Config.MobileNoticeURL);
+ };
+
render() {
const {theme, config, license} = this.props;
const style = getStyleSheet(theme);
@@ -206,6 +215,33 @@ export default class About extends PureComponent {
currentYear: new Date().getFullYear()
}}
/>
+
+
+
+ ),
+ mobile: (
+
+ )
+ }}
+ />
+
+
{
flexDirection: 'row',
marginVertical: 20
},
+ noticeContainer: {
+ flex: 1,
+ flexDirection: 'column',
+ marginTop: 10
+ },
+ noticeLink: {
+ color: theme.linkColor,
+ fontSize: 11,
+ lineHeight: 13
+ },
hashContainer: {
flex: 1,
flexDirection: 'column',
diff --git a/assets/base/config.json b/assets/base/config.json
index 72164bbdc..b897f9757 100644
--- a/assets/base/config.json
+++ b/assets/base/config.json
@@ -3,5 +3,7 @@
"TestServerUrl": "http://localhost:8065",
"DefaultTheme": "default",
"ShowErrorsList": false,
- "MinServerVersion": "3.10.0"
+ "MinServerVersion": "3.10.0",
+ "PlatformNoticeURL": "https://about.mattermost.com/platform-notice-txt/",
+ "MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/"
}
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 99eb35bbe..642fb1504 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -1768,6 +1768,9 @@
"mobile.login_options.choose_title": "Choose your login method",
"mobile.more_dms.start": "Start",
"mobile.more_dms.title": "New Conversation",
+ "mobile.notice_text": "Mattermost is made possible by the open source software in our {platform} and {mobile}.",
+ "mobile.notice_mobile_link": "mobile apps",
+ "mobile.notice_platform_link": "platform",
"mobile.notification.in": " in ",
"mobile.offlineIndicator.connected": "Connected",
"mobile.offlineIndicator.connecting": "Connecting...",