diff --git a/app/components/client_upgrade_listener/client_upgrade_listener.js b/app/components/client_upgrade_listener/client_upgrade_listener.js index 5013971bd..7f154608a 100644 --- a/app/components/client_upgrade_listener/client_upgrade_listener.js +++ b/app/components/client_upgrade_listener/client_upgrade_listener.js @@ -117,7 +117,11 @@ export default class ClientUpgradeListener extends PureComponent { const {downloadLink} = this.props; const {intl} = this.context; - Linking.openURL(downloadLink).catch(() => { + Linking.canOpenURL(downloadLink).then((supported) => { + if (supported) { + return Linking.openURL(downloadLink); + } + Alert.alert( intl.formatMessage({ id: 'mobile.client_upgrade.download_error.title', @@ -128,6 +132,8 @@ export default class ClientUpgradeListener extends PureComponent { defaultMessage: 'An error occurred while trying to open the download link.', }), ); + + return false; }); this.toggleUpgradeMessage(false); diff --git a/app/components/markdown/markdown_image/markdown_image.js b/app/components/markdown/markdown_image/markdown_image.js index 0b5a1e38e..e2317234e 100644 --- a/app/components/markdown/markdown_image/markdown_image.js +++ b/app/components/markdown/markdown_image/markdown_image.js @@ -5,7 +5,6 @@ import PropTypes from 'prop-types'; import React from 'react'; import {intlShape} from 'react-intl'; import { - Alert, Linking, Platform, StyleSheet, @@ -124,19 +123,11 @@ export default class MarkdownImage extends ImageViewPort { handleLinkPress = () => { const url = normalizeProtocol(this.props.linkDestination); - const {intl} = this.context; - Linking.openURL(url).catch(() => { - Alert.alert( - intl.formatMessage({ - id: 'mobile.link.error.title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.link.error.text', - defaultMessage: 'Unable to open the link.', - }), - ); + Linking.canOpenURL(url).then((supported) => { + if (supported) { + Linking.openURL(url); + } }); }; diff --git a/app/components/markdown/markdown_link/markdown_link.js b/app/components/markdown/markdown_link/markdown_link.js index 452690614..1c1f37046 100644 --- a/app/components/markdown/markdown_link/markdown_link.js +++ b/app/components/markdown/markdown_link/markdown_link.js @@ -64,18 +64,22 @@ export default class MarkdownLink extends PureComponent { onPermalinkPress(match.postId, match.teamName); } } else { - Linking.openURL(url).catch(() => { - const {formatMessage} = this.context.intl; - Alert.alert( - formatMessage({ - id: 'mobile.server_link.error.title', - defaultMessage: 'Link Error', - }), - formatMessage({ - id: 'mobile.server_link.error.text', - defaultMessage: 'The link could not be found on this server.', - }), - ); + Linking.canOpenURL(url).then((supported) => { + if (supported) { + Linking.openURL(url); + } else { + const {formatMessage} = this.context.intl; + Alert.alert( + formatMessage({ + id: 'mobile.server_link.error.title', + defaultMessage: 'Link Error', + }), + formatMessage({ + id: 'mobile.server_link.error.text', + defaultMessage: 'The link could not be found on this server.', + }), + ); + } }); } }); diff --git a/app/components/message_attachments/attachment_author.js b/app/components/message_attachments/attachment_author.js index 3a15361cf..c9b408a60 100644 --- a/app/components/message_attachments/attachment_author.js +++ b/app/components/message_attachments/attachment_author.js @@ -2,10 +2,9 @@ // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; -import {Alert, Linking, Text, View} from 'react-native'; +import {Linking, Text, View} from 'react-native'; import FastImage from 'react-native-fast-image'; import PropTypes from 'prop-types'; -import {intlShape} from 'react-intl'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; @@ -17,27 +16,10 @@ export default class AttachmentAuthor extends PureComponent { theme: PropTypes.object.isRequired, }; - static contextTypes = { - intl: intlShape.isRequired, - }; - openLink = () => { const {link} = this.props; - const {intl} = this.context; - - if (link) { - Linking.openURL(link).catch(() => { - Alert.alert( - intl.formatMessage({ - id: 'mobile.link.error.title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.link.error.text', - defaultMessage: 'Unable to open the link.', - }), - ); - }); + if (link && Linking.canOpenURL(link)) { + Linking.openURL(link); } }; diff --git a/app/components/message_attachments/attachment_title.js b/app/components/message_attachments/attachment_title.js index 7b2ad49da..ab7be0c33 100644 --- a/app/components/message_attachments/attachment_title.js +++ b/app/components/message_attachments/attachment_title.js @@ -2,9 +2,8 @@ // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; -import {Alert, Linking, Text, View} from 'react-native'; +import {Linking, Text, View} from 'react-native'; import PropTypes from 'prop-types'; -import {intlShape} from 'react-intl'; import {makeStyleSheetFromTheme} from 'app/utils/theme'; import Markdown from 'app/components/markdown'; @@ -16,27 +15,10 @@ export default class AttachmentTitle extends PureComponent { value: PropTypes.string, }; - static contextTypes = { - intl: intlShape.isRequired, - }; - openLink = () => { const {link} = this.props; - const {intl} = this.context; - - if (link) { - Linking.openURL(link).catch(() => { - Alert.alert( - intl.formatMessage({ - id: 'mobile.link.error.title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.link.error.text', - defaultMessage: 'Unable to open the link.', - }), - ); - }); + if (link && Linking.canOpenURL(link)) { + Linking.openURL(link); } }; diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js index 3affdd367..166788f52 100644 --- a/app/screens/client_upgrade/client_upgrade.js +++ b/app/screens/client_upgrade/client_upgrade.js @@ -105,7 +105,11 @@ export default class ClientUpgrade extends PureComponent { const {downloadLink} = this.props; const {intl} = this.context; - Linking.openURL(downloadLink).catch(() => { + Linking.canOpenURL(downloadLink).then((supported) => { + if (supported) { + return Linking.openURL(downloadLink); + } + Alert.alert( intl.formatMessage({ id: 'mobile.client_upgrade.download_error.title', @@ -116,6 +120,8 @@ export default class ClientUpgrade extends PureComponent { defaultMessage: 'An error occurred while trying to open the download link.', }), ); + + return false; }); }; diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js index 41ff15f10..70a40d9c5 100644 --- a/app/screens/settings/general/settings.js +++ b/app/screens/settings/general/settings.js @@ -5,7 +5,6 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape, injectIntl} from 'react-intl'; import { - Alert, Linking, Platform, ScrollView, @@ -136,42 +135,27 @@ class Settings extends PureComponent { }); openErrorEmail = preventDoubleTap(() => { - const {config, intl} = this.props; + const {config} = this.props; const recipient = config.SupportEmail; const subject = `Problem with ${config.SiteName} React Native app`; const mailTo = `mailto:${recipient}?subject=${subject}&body=${this.errorEmailBody()}`; - Linking.openURL(mailTo).then(() => { - this.props.actions.clearErrors(); - }).catch(() => { - Alert.alert( - intl.formatMessage({ - id: 'mobile.mailTo.error.title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.mailTo.error.text', - defaultMessage: 'Unable to open an email client.', - }), - ); + Linking.canOpenURL(mailTo).then((supported) => { + if (supported) { + Linking.openURL(mailTo); + this.props.actions.clearErrors(); + } }); }); openHelp = preventDoubleTap(() => { - const {config, intl} = this.props; + const {config} = this.props; const link = config.HelpLink ? config.HelpLink.toLowerCase() : ''; - Linking.openURL(link).catch(() => { - Alert.alert( - intl.formatMessage({ - id: 'mobile.link.error.title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.link.error.text', - defaultMessage: 'Unable to open the link.', - }), - ); + Linking.canOpenURL(link).then((supported) => { + if (supported) { + Linking.openURL(link); + } }); }); diff --git a/app/utils/supported_server.ts b/app/utils/supported_server.ts index 97e29e533..36bea7c84 100644 --- a/app/utils/supported_server.ts +++ b/app/utils/supported_server.ts @@ -36,18 +36,9 @@ function unsupportedServerAdminAlert(formatMessage: FormatMessageType) { style: 'cancel', onPress: () => { const url = 'https://mattermost.com/blog/support-for-esr-5-9-has-ended/'; - Linking.openURL(url).catch(() => { - Alert.alert( - formatMessage({ - id: 'mobile.link.error.title', - defaultMessage: 'Error', - }), - formatMessage({ - id: 'mobile.link.error.text', - defaultMessage: 'Unable to open the link.', - }), - ); - }); + if (Linking.canOpenURL(url)) { + Linking.openURL(url); + } }, }; const buttons: AlertButton[] = [cancel, learnMore]; diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 80e8a873f..3a4fe5035 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -290,16 +290,12 @@ "mobile.ios.photos_permission_denied_description": "Upload photos and videos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your photo and video library.", "mobile.ios.photos_permission_denied_title": "{applicationName} would like to access your photos", "mobile.join_channel.error": "We couldn't join the channel {displayName}. Please check your connection and try again.", - "mobile.link.error.text": "Unable to open the link.", - "mobile.link.error.title": "Error", "mobile.loading_channels": "Loading Channels...", "mobile.loading_members": "Loading Members...", "mobile.loading_options": "Loading Options...", "mobile.loading_posts": "Loading messages...", "mobile.login_options.choose_title": "Choose your login method", "mobile.long_post_title": "{channelName} - Post", - "mobile.mailTo.error.text": "Unable to open an email client.", - "mobile.mailTo.error.title": "Error", "mobile.managed.blocked_by": "Blocked by {vendor}", "mobile.managed.exit": "Exit", "mobile.managed.jailbreak": "Jailbroken devices are not trusted by {vendor}, please exit the app.", diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist index 70e6e8efb..541e130cd 100644 --- a/ios/Mattermost/Info.plist +++ b/ios/Mattermost/Info.plist @@ -129,5 +129,10 @@ UIViewControllerBasedStatusBarAppearance + LSApplicationQueriesSchemes + + https + http + diff --git a/test/setup.js b/test/setup.js index 6a43001c5..2c53ff475 100644 --- a/test/setup.js +++ b/test/setup.js @@ -187,6 +187,7 @@ jest.mock('react-native-cookies', () => ({ addEventListener: jest.fn(), removeEventListener: jest.fn(), openURL: jest.fn(), + canOpenURL: jest.fn(), getInitialURL: jest.fn(), clearAll: jest.fn(), get: () => Promise.resolve(({