Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Default browser #1752

Merged
merged 13 commits into from
Feb 19, 2020
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Readme will guide you on how to config.
| Report message | ✅ |
| Theming | ✅ |
| Settings -> Review the App | ✅ |
| Settings -> Default Browser | |
| Settings -> Default Browser | |
| Admin panel | ✅ |
| Reply message from notification | ✅ |
| Unread counter banner on message list | ✅ |
Expand Down
4 changes: 4 additions & 0 deletions __mocks__/rn-user-defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
set: () => '',
get: () => ''
};
4 changes: 4 additions & 0 deletions app/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export default {
Back: 'Back',
Black: 'Black',
Block_user: 'Block user',
Browser: 'Browser',
Broadcast_channel_Description: 'Only authorized users can write new messages, but the other users will be able to reply',
Broadcast_Channel: 'Broadcast Channel',
Busy: 'Busy',
Expand All @@ -132,6 +133,7 @@ export default {
Choose: 'Choose',
Choose_from_library: 'Choose from library',
Choose_file: 'Choose file',
Choose_where_you_want_links_be_opened: 'Choose where you want links be opened',
Code: 'Code',
Collaborative: 'Collaborative',
Confirm: 'Confirm',
Expand All @@ -157,6 +159,7 @@ export default {
Dark: 'Dark',
Dark_level: 'Dark Level',
Default: 'Default',
Default_browser: 'Default browser',
Delete_Room_Warning: 'Deleting a room will delete all messages posted within the room. This cannot be undone.',
delete: 'delete',
Delete: 'Delete',
Expand Down Expand Up @@ -206,6 +209,7 @@ export default {
Has_joined_the_channel: 'Has joined the channel',
Has_joined_the_conversation: 'Has joined the conversation',
Has_left_the_channel: 'Has left the channel',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP',
In_App_and_Desktop_Alert_info: 'Displays a banner at the top of the screen when app is open, and displays a notification on desktop',
Invisible: 'Invisible',
Expand Down
4 changes: 4 additions & 0 deletions app/i18n/locales/pt-BR.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default {
Back: 'Voltar',
Black: 'Preto',
Block_user: 'Bloquear usuário',
Browser: 'Navegador',
Broadcast_channel_Description: 'Somente usuários autorizados podem escrever novas mensagens, mas os outros usuários poderão responder',
Broadcast_Channel: 'Canal de Transmissão',
Busy: 'Ocupado',
Expand All @@ -134,6 +135,7 @@ export default {
Choose: 'Escolher',
Choose_from_library: 'Escolha da biblioteca',
Choose_file: 'Enviar arquivo',
Choose_where_you_want_links_be_opened: 'Escolha onde deseja que os links sejam abertos',
Code: 'Código',
Collaborative: 'Colaborativo',
Confirm: 'Confirmar',
Expand All @@ -154,6 +156,7 @@ export default {
Create: 'Criar',
Dark: 'Escuro',
Dark_level: 'Nível escuro',
Default_browser: 'Navegador padrão',
Delete_Room_Warning: 'A exclusão de uma sala irá apagar todas as mensagens postadas na sala. Isso não pode ser desfeito.',
delete: 'excluir',
Delete: 'Excluir',
Expand Down Expand Up @@ -197,6 +200,7 @@ export default {
Has_joined_the_channel: 'Entrou no canal',
Has_joined_the_conversation: 'Entrou na conversa',
Has_left_the_channel: 'Saiu da conversa',
In_app: 'No app',
Invisible: 'Invisível',
Invite: 'Convidar',
is_typing: 'está digitando',
Expand Down
3 changes: 3 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ const SettingsStack = createStackNavigator({
},
ThemeView: {
getScreen: () => require('./views/ThemeView').default
},
DefaultBrowserView: {
getScreen: () => require('./views/DefaultBrowserView').default
}
}, {
defaultNavigationOptions: defaultHeader,
Expand Down
62 changes: 56 additions & 6 deletions app/utils/openLink.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
import { Linking } from 'react-native';
import * as WebBrowser from 'expo-web-browser';
import RNUserDefaults from 'rn-user-defaults';
import parse from 'url-parse';

import { themes } from '../constants/colors';

const openLink = (url, theme = 'light') => WebBrowser.openBrowserAsync(url, {
toolbarColor: themes[theme].headerBackground,
controlsColor: themes[theme].headerTintColor,
collapseToolbar: true,
showTitle: true
});
export const DEFAULT_BROWSER_KEY = 'DEFAULT_BROWSER_KEY';

const scheme = {
chrome: 'googlechrome:',
chromeSecure: 'googlechromes:',
firefox: 'firefox:',
brave: 'brave:'
};

const appSchemeURL = (url, browser) => {
let schemeUrl = url;
const parsedUrl = parse(url, true);
const { protocol } = parsedUrl;
const isSecure = ['https:'].includes(protocol);

if (browser === 'googlechrome') {
if (!isSecure) {
schemeUrl = url.replace(protocol, scheme.chrome);
} else {
schemeUrl = url.replace(protocol, scheme.chromeSecure);
}
} else if (browser === 'firefox') {
schemeUrl = `${ scheme.firefox }//open-url?url=${ url }`;
} else if (browser === 'brave') {
schemeUrl = `${ scheme.brave }//open-url?url=${ url }`;
}

return schemeUrl;
};

const openLink = async(url, theme = 'light') => {
try {
const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);

if (browser) {
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
await Linking.openURL(schemeUrl);
} else {
await WebBrowser.openBrowserAsync(url, {
toolbarColor: themes[theme].headerBackground,
controlsColor: themes[theme].headerTintColor,
collapseToolbar: true,
showTitle: true
});
}
} catch {
try {
await Linking.openURL(url);
} catch {
// do nothing
}
}
};

export default openLink;
191 changes: 191 additions & 0 deletions app/views/DefaultBrowserView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
StyleSheet, FlatList, View, Text, Linking
} from 'react-native';
import { SafeAreaView } from 'react-navigation';
import RNUserDefaults from 'rn-user-defaults';

import I18n from '../i18n';
import { themedHeader } from '../utils/navigation';
import { withTheme } from '../theme';
import { themes } from '../constants/colors';
import sharedStyles from './Styles';
import StatusBar from '../containers/StatusBar';
import Separator from '../containers/Separator';
import ListItem from '../containers/ListItem';
import { CustomIcon } from '../lib/Icons';
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
import { isIOS } from '../utils/deviceInfo';

const DEFAULT_BROWSERS = [
{
title: I18n.t('In_app'),
value: 'inApp'
},
{
title: isIOS ? 'Safari' : I18n.t('Browser'),
value: 'systemDefault:'
}
];

const BROWSERS = [
{
title: 'Chrome',
value: 'googlechrome:'
},
{
title: 'Firefox',
value: 'firefox:'
},
{
title: 'Brave',
value: 'brave:'
}
];

const styles = StyleSheet.create({
list: {
paddingBottom: 18
},
info: {
paddingTop: 25,
paddingBottom: 18,
paddingHorizontal: 16
},
infoText: {
fontSize: 16,
...sharedStyles.textRegular
}
});

class DefaultBrowserView extends React.Component {
static navigationOptions = ({ screenProps }) => ({
title: I18n.t('Default_browser'),
...themedHeader(screenProps.theme)
})

static propTypes = {
theme: PropTypes.string
}

state = {
browser: null,
supported: []
}

constructor(props) {
super(props);
if (isIOS) {
this.init();
}
}

async componentDidMount() {
this.mounted = true;
try {
const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);
this.setState({ browser });
} catch {
// do nothing
}
}

init = () => {
BROWSERS.forEach((browser) => {
const { value } = browser;
Linking.canOpenURL(value).then((installed) => {
if (installed) {
if (this.mounted) {
this.setState(({ supported }) => ({ supported: [...supported, browser] }));
} else {
const { supported } = this.state;
this.state.supported = [...supported, browser];
}
}
});
});
}

isSelected = (value) => {
const { browser } = this.state;
if (!browser && value === 'inApp') {
return true;
}
return browser === value;
}

changeDefaultBrowser = async(newBrowser) => {
try {
const browser = newBrowser !== 'inApp' ? newBrowser : null;
await RNUserDefaults.set(DEFAULT_BROWSER_KEY, browser);
this.setState({ browser });
} catch {
// do nothing
}
}

renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}

renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} color={themes[theme].tintColor} />;
}

renderItem = ({ item }) => {
const { theme } = this.props;
const { title, value } = item;
return (
<ListItem
title={title}
onPress={() => this.changeDefaultBrowser(value)}
testID={`default-browser-view-${ title }`}
right={this.isSelected(value) ? this.renderIcon : null}
theme={theme}
/>
);
}

renderHeader = () => {
const { theme } = this.props;
return (
<>
<View style={styles.info}>
<Text style={[styles.infoText, { color: themes[theme].infoText }]}>{I18n.t('Choose_where_you_want_links_be_opened')}</Text>
</View>
{this.renderSeparator()}
</>
);
}

render() {
const { supported } = this.state;
const { theme } = this.props;
return (
<SafeAreaView
style={[sharedStyles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}
forceInset={{ vertical: 'never' }}
testID='default-browser-view'
>
<StatusBar theme={theme} />
<FlatList
data={DEFAULT_BROWSERS.concat(supported)}
keyExtractor={item => item.value}
contentContainerStyle={[
styles.list,
{ borderColor: themes[theme].separatorColor }
]}
renderItem={this.renderItem}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderSeparator}
ItemSeparatorComponent={this.renderSeparator}
/>
</SafeAreaView>
);
}
}

export default withTheme(DefaultBrowserView);
Loading