Add alert when deeplink is invalid (#7538)
This commit is contained in:
parent
397744dc72
commit
df52740752
8 changed files with 71 additions and 37 deletions
|
|
@ -74,7 +74,7 @@ const launchApp = async (props: LaunchProps) => {
|
|||
let serverUrl: string | undefined;
|
||||
switch (props?.launchType) {
|
||||
case Launch.DeepLink:
|
||||
if (props.extra?.type !== DeepLink.Invalid) {
|
||||
if (props.extra && props.extra.type !== DeepLink.Invalid) {
|
||||
const extra = props.extra as DeepLinkWithData;
|
||||
const existingServer = DatabaseManager.searchUrl(extra.data!.serverUrl);
|
||||
serverUrl = existingServer;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import {queryTeamDefaultChannel} from '@queries/servers/channel';
|
|||
import {getCommonSystemValues} from '@queries/servers/system';
|
||||
import {getTeamChannelHistory} from '@queries/servers/team';
|
||||
import {setScreensOrientation} from '@screens/navigation';
|
||||
import {handleDeepLink} from '@utils/deep_link';
|
||||
import {alertInvalidDeepLink, handleDeepLink} from '@utils/deep_link';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
|
||||
type LinkingCallbackArg = {url: string};
|
||||
|
||||
|
|
@ -50,13 +51,16 @@ class GlobalEventHandler {
|
|||
}
|
||||
};
|
||||
|
||||
onDeepLink = (event: LinkingCallbackArg) => {
|
||||
onDeepLink = async (event: LinkingCallbackArg) => {
|
||||
if (event.url?.startsWith(Sso.REDIRECT_URL_SCHEME) || event.url?.startsWith(Sso.REDIRECT_URL_SCHEME_DEV)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.url) {
|
||||
handleDeepLink(event.url);
|
||||
const {error} = await handleDeepLink(event.url);
|
||||
if (error) {
|
||||
alertInvalidDeepLink(getIntlShape(DEFAULT_LOCALE));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {useAppState} from '@hooks/device';
|
|||
import {getAllServers} from '@queries/app/servers';
|
||||
import {findChannels, popToRoot} from '@screens/navigation';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {handleDeepLink} from '@utils/deep_link';
|
||||
import {alertInvalidDeepLink, handleDeepLink} from '@utils/deep_link';
|
||||
import {logError} from '@utils/log';
|
||||
import {alertChannelArchived, alertChannelRemove, alertTeamRemove} from '@utils/navigation';
|
||||
import {notificationError} from '@utils/notification';
|
||||
|
|
@ -121,9 +121,18 @@ export default function HomeScreen(props: HomeProps) {
|
|||
|
||||
useEffect(() => {
|
||||
if (props.launchType === 'deeplink') {
|
||||
if (props.launchError) {
|
||||
alertInvalidDeepLink(intl);
|
||||
return;
|
||||
}
|
||||
|
||||
const deepLink = props.extra as DeepLinkWithData;
|
||||
if (deepLink?.url) {
|
||||
handleDeepLink(deepLink.url);
|
||||
handleDeepLink(deepLink.url).then((result) => {
|
||||
if (result.error) {
|
||||
alertInvalidDeepLink(intl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import {fetchUsersByUsernames} from '@actions/remote/user';
|
|||
import {DeepLink, Launch, Screens} from '@constants';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {DEFAULT_LOCALE, getTranslations} from '@i18n';
|
||||
import {DEFAULT_LOCALE, getTranslations, t} from '@i18n';
|
||||
import WebsocketManager from '@managers/websocket_manager';
|
||||
import {getActiveServerUrl} from '@queries/app/servers';
|
||||
import {getCurrentUser, queryUsersByUsername} from '@queries/servers/user';
|
||||
import {dismissAllModalsAndPopToRoot} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {errorBadChannel, errorUnkownUser} from '@utils/draft';
|
||||
import {alertErrorWithFallback, errorBadChannel, errorUnkownUser} from '@utils/draft';
|
||||
import {logError} from '@utils/log';
|
||||
import {escapeRegex} from '@utils/markdown';
|
||||
import {addNewServer} from '@utils/server';
|
||||
|
|
@ -115,31 +115,39 @@ export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape,
|
|||
}
|
||||
|
||||
export function parseDeepLink(deepLinkUrl: string): DeepLinkWithData {
|
||||
const url = removeProtocol(deepLinkUrl);
|
||||
try {
|
||||
const url = removeProtocol(decodeURIComponent(deepLinkUrl));
|
||||
|
||||
let match = new RegExp('(.*)\\/([^\\/]+)\\/channels\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Channel, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], channelName: match[3]}};
|
||||
}
|
||||
if (url.includes('../') || url.includes('/..')) {
|
||||
return {type: DeepLink.Invalid, url: deepLinkUrl};
|
||||
}
|
||||
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/pl\\/(\\w+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Permalink, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], postId: match[3]}};
|
||||
}
|
||||
let match = new RegExp('(.*)\\/([^\\/]+)\\/channels\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Channel, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], channelName: match[3]}};
|
||||
}
|
||||
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/messages\\/@(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.DirectMessage, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], userName: match[3]}};
|
||||
}
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/pl\\/(\\w+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Permalink, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], postId: match[3]}};
|
||||
}
|
||||
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/messages\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.GroupMessage, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], channelId: match[3]}};
|
||||
}
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/messages\\/@(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.DirectMessage, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], userName: match[3]}};
|
||||
}
|
||||
|
||||
match = new RegExp('(.*)\\/plugins\\/([^\\/]+)\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Plugin, url: deepLinkUrl, data: {serverUrl: match[1], id: match[2], teamName: ''}};
|
||||
match = new RegExp('(.*)\\/([^\\/]+)\\/messages\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.GroupMessage, url: deepLinkUrl, data: {serverUrl: match[1], teamName: match[2], channelId: match[3]}};
|
||||
}
|
||||
|
||||
match = new RegExp('(.*)\\/plugins\\/([^\\/]+)\\/(\\S+)').exec(url);
|
||||
if (match) {
|
||||
return {type: DeepLink.Plugin, url: deepLinkUrl, data: {serverUrl: match[1], id: match[2], teamName: ''}};
|
||||
}
|
||||
} catch {
|
||||
// do nothing just return invalid deeplink
|
||||
}
|
||||
|
||||
return {type: DeepLink.Invalid, url: deepLinkUrl};
|
||||
|
|
@ -201,3 +209,12 @@ export const getLaunchPropsFromDeepLink = (deepLinkUrl: string, coldStart = fals
|
|||
|
||||
return launchProps;
|
||||
};
|
||||
|
||||
export function alertInvalidDeepLink(intl: IntlShape) {
|
||||
const message = {
|
||||
id: t('mobile.deep_link.invalid'),
|
||||
defaultMessage: 'This link you are trying to open is invalid.',
|
||||
};
|
||||
|
||||
return alertErrorWithFallback(intl, {}, message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {createIntl} from 'react-intl';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import ReactNativeHapticFeedback, {HapticFeedbackTypes} from 'react-native-haptic-feedback';
|
||||
|
||||
import {getTranslations} from '@i18n';
|
||||
|
||||
type SortByCreatAt = (Session | Channel | Team | Post) & {
|
||||
create_at: number;
|
||||
}
|
||||
|
||||
export function getIntlShape(locale = 'en') {
|
||||
return createIntl({
|
||||
locale,
|
||||
messages: getTranslations(locale),
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function emptyFunction(..._args: any[]) {
|
||||
// do nothing
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {Alert} from 'react-native';
|
||||
|
||||
import {getIntlShape} from '@test/intl-test-helper';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
|
||||
import {unsupportedServer} from '.';
|
||||
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@
|
|||
"mobile.custom_status.clear_after": "Clear After",
|
||||
"mobile.custom_status.clear_after.title": "Clear Custom Status After",
|
||||
"mobile.custom_status.modal_confirm": "Done",
|
||||
"mobile.deep_link.invalid": "This link you are trying to open is invalid.",
|
||||
"mobile.diagnostic_id.empty": "A DiagnosticId value is missing for this server. Contact your system admin to review this value and restart the server.",
|
||||
"mobile.direct_message.error": "We couldn't open a DM with {displayName}.",
|
||||
"mobile.display_settings.clockDisplay": "Clock Display",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import DatabaseProvider from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import {render} from '@testing-library/react-native';
|
||||
import React, {type ReactElement} from 'react';
|
||||
import {createIntl, IntlProvider} from 'react-intl';
|
||||
import {IntlProvider} from 'react-intl';
|
||||
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
||||
|
||||
import {ThemeContext, getDefaultThemeByAppearance} from '@context/theme';
|
||||
|
|
@ -12,13 +12,6 @@ import {getTranslations} from '@i18n';
|
|||
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
|
||||
export function getIntlShape(locale = 'en') {
|
||||
return createIntl({
|
||||
locale,
|
||||
messages: getTranslations(locale),
|
||||
});
|
||||
}
|
||||
|
||||
export function renderWithIntl(ui: ReactElement, {locale = 'en', ...renderOptions} = {}) {
|
||||
function Wrapper({children}: {children: ReactElement}) {
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in a new issue