Skip to content

Commit

Permalink
intercept app store links and open link natively
Browse files Browse the repository at this point in the history
  • Loading branch information
maxbbb committed Nov 14, 2024
1 parent 35daa8b commit 4f9aae6
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 11 deletions.
10 changes: 8 additions & 2 deletions src/components/DappBrowser/BrowserTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
import { Freeze } from 'react-freeze';
import { StyleSheet } from 'react-native';
import { Linking, StyleSheet } from 'react-native';
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
Expand Down Expand Up @@ -52,7 +52,7 @@ import { useAnimatedTab } from './hooks/useAnimatedTab';
import { useTabScreenshotProvider } from './hooks/useTabScreenshotProvider';
import { freezeWebsite, getWebsiteMetadata, unfreezeWebsite } from './scripts';
import { BrowserTabProps, ScreenshotType } from './types';
import { normalizeUrlForRecents } from './utils';
import { isValidAppStoreUrl } from './utils';

export const BrowserTab = React.memo(function BrowserTab({ addRecent, setLogo, setTitle, tabId }: BrowserTabProps) {
const { isDarkMode } = useColorMode();
Expand Down Expand Up @@ -309,6 +309,12 @@ const FreezableWebViewComponent = ({
(syntheticEvent: { nativeEvent: { targetUrl: string } }) => {
const { nativeEvent } = syntheticEvent;
const { targetUrl } = nativeEvent;

if (isValidAppStoreUrl(targetUrl)) {
Linking.openURL(targetUrl);
return;
}

setParams({ url: targetUrl });
},
[setParams]
Expand Down
5 changes: 5 additions & 0 deletions src/components/DappBrowser/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export const HTTP = 'http://';
export const HTTPS = 'https://';
export const RAINBOW_HOME = 'RAINBOW_HOME';

export const IOS_APP_STORE_URL_PREFIXES = ['itms-apps://', 'itms-appss://', 'https://itunes.apple.com', 'https://apps.apple.com'];
export const ANDROID_APP_STORE_URL_PREFIXES = ['market://', 'https://play.google.com/store', 'https://play.google.com/store/apps'];

export const APP_STORE_URL_PREFIXES = [...IOS_APP_STORE_URL_PREFIXES, ...ANDROID_APP_STORE_URL_PREFIXES];

export const USER_AGENT = {
IOS: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1',
ANDROID: 'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.103 Mobile Safari/537.36',
Expand Down
8 changes: 4 additions & 4 deletions src/components/DappBrowser/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import React, { useCallback } from 'react';
import { StyleSheet, View } from 'react-native';
import Animated, { SharedValue, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { useBrowserContext } from '../BrowserContext';
import { GOOGLE_SEARCH_URL, HTTP, HTTPS } from '../constants';
import { GOOGLE_SEARCH_URL, HTTPS } from '../constants';
import { AccountIcon } from '../search-input/AccountIcon';
import { SearchInput } from '../search-input/SearchInput';
import { TabButton } from '../search-input/TabButton';
import { isValidURL, isValidURLWorklet } from '../utils';
import { isMissingValidProtocol, isMissingValidProtocolWorklet, isValidURL, isValidURLWorklet } from '../utils';
import { DEVICE_WIDTH } from '@/utils/deviceUtils';
import { useBrowserWorkletsContext } from '../BrowserWorkletsContext';
import { SearchResults } from './results/SearchResults';
Expand Down Expand Up @@ -72,7 +72,7 @@ export const Search = () => {

if (!isValidURL(newUrl)) {
newUrl = GOOGLE_SEARCH_URL + newUrl;
} else if (!newUrl.startsWith(HTTP) && !newUrl.startsWith(HTTPS)) {
} else if (isMissingValidProtocol(newUrl)) {
newUrl = HTTPS + newUrl;
}

Expand All @@ -94,7 +94,7 @@ export const Search = () => {

if (!isValidURLWorklet(newUrl)) {
newUrl = GOOGLE_SEARCH_URL + newUrl;
} else if (!newUrl.startsWith(HTTP) && !newUrl.startsWith(HTTPS)) {
} else if (isMissingValidProtocolWorklet(newUrl)) {
newUrl = HTTPS + newUrl;
}

Expand Down
23 changes: 18 additions & 5 deletions src/components/DappBrowser/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Share } from 'react-native';
import { WebViewNavigationEvent } from 'react-native-webview/lib/RNCWebViewNativeComponent';
import { RainbowError, logger } from '@/logger';
import { HTTP, HTTPS, RAINBOW_HOME } from './constants';
import { HTTP, HTTPS, RAINBOW_HOME, APP_STORE_URL_PREFIXES } from './constants';

// ---------------------------------------------------------------------------- //
// URL validation regex breakdown here: https://mathiasbynens.be/demo/url-regex
Expand All @@ -12,9 +12,22 @@ import { HTTP, HTTPS, RAINBOW_HOME } from './constants';
const URL_PATTERN_REGEX =
/^(?:(?:(?:https?):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i;

export function isMissingValidProtocol(url: string): boolean {
return !url.startsWith(HTTP) && !url.startsWith(HTTPS);
}

export function isMissingValidProtocolWorklet(url: string): boolean {
'worklet';
return !url.startsWith(HTTP) && !url.startsWith(HTTPS);
}

export function isValidAppStoreUrl(url: string): boolean {
return APP_STORE_URL_PREFIXES.some(prefix => url.startsWith(prefix));
}

export function isValidURL(url: string): boolean {
let urlForValidation = url.trim();
if (!urlForValidation.startsWith(HTTP) && !urlForValidation.startsWith(HTTPS)) {
if (isMissingValidProtocol(urlForValidation)) {
urlForValidation = HTTPS + urlForValidation;
}
return URL_PATTERN_REGEX.test(urlForValidation);
Expand All @@ -23,7 +36,7 @@ export function isValidURL(url: string): boolean {
export function isValidURLWorklet(url: string): boolean {
'worklet';
let urlForValidation = url.trim();
if (!urlForValidation.startsWith(HTTP) && !urlForValidation.startsWith(HTTPS)) {
if (isMissingValidProtocolWorklet(urlForValidation)) {
urlForValidation = HTTPS + urlForValidation;
}
return URL_PATTERN_REGEX.test(urlForValidation);
Expand All @@ -34,7 +47,7 @@ export const normalizeUrl = (url: string): string => {
return '';
}
let normalizedUrl = url;
if (!normalizedUrl.startsWith(HTTP) && !normalizedUrl.startsWith(HTTPS)) {
if (isMissingValidProtocol(normalizedUrl)) {
normalizedUrl = HTTPS + normalizedUrl;
}
if (!normalizedUrl.endsWith('/') && !normalizedUrl.includes('?')) {
Expand All @@ -49,7 +62,7 @@ export const normalizeUrlWorklet = (url: string): string => {
return '';
}
let normalizedUrl = url;
if (!normalizedUrl.startsWith(HTTP) && !normalizedUrl.startsWith(HTTPS)) {
if (isMissingValidProtocolWorklet(normalizedUrl)) {
normalizedUrl = HTTPS + normalizedUrl;
}
if (!normalizedUrl.endsWith('/') && !normalizedUrl.includes('?')) {
Expand Down

0 comments on commit 4f9aae6

Please sign in to comment.