[NEW] Default browser (#1752)

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Djorkaeff Alexandre 2020-02-19 17:52:05 -03:00 committed by GitHub
parent e383271998
commit 06cca9c615
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 284 additions and 17 deletions

View File

@ -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 | ✅ |

View File

@ -0,0 +1,4 @@
export default {
set: () => '',
get: () => ''
};

View File

@ -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',
@ -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',
@ -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',
@ -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',

View File

@ -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',
@ -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',
@ -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',
@ -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',

View File

@ -217,6 +217,9 @@ const SettingsStack = createStackNavigator({
},
ThemeView: {
getScreen: () => require('./views/ThemeView').default
},
DefaultBrowserView: {
getScreen: () => require('./views/DefaultBrowserView').default
}
}, {
defaultNavigationOptions: defaultHeader,

View File

@ -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;

View File

@ -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);

View File

@ -138,9 +138,9 @@ class SettingsView extends React.Component {
}
}
navigateToRoom = (room) => {
navigateToScreen = (screen) => {
const { navigation } = this.props;
navigation.navigate(room);
navigation.navigate(screen);
}
sendEmail = async() => {
@ -175,11 +175,6 @@ class SettingsView extends React.Component {
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
}
changeTheme = () => {
const { navigation } = this.props;
navigation.navigate('ThemeView');
}
onPressLicense = () => {
const { theme } = this.props;
openLink(LICENSE_LINK, theme);
@ -234,7 +229,7 @@ class SettingsView extends React.Component {
<SectionSeparator theme={theme} />
<ListItem
title={I18n.t('Profile')}
onPress={() => this.navigateToRoom('ProfileView')}
onPress={() => this.navigateToScreen('ProfileView')}
showActionIndicator
testID='settings-profile'
right={this.renderDisclosure}
@ -255,7 +250,7 @@ class SettingsView extends React.Component {
<Separator theme={theme} />
<ListItem
title={I18n.t('Language')}
onPress={() => this.navigateToRoom('LanguageView')}
onPress={() => this.navigateToScreen('LanguageView')}
showActionIndicator
testID='settings-view-language'
right={this.renderDisclosure}
@ -280,10 +275,19 @@ class SettingsView extends React.Component {
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Default_browser')}
showActionIndicator
onPress={() => this.navigateToScreen('DefaultBrowserView')}
testID='settings-view-default-browser'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Theme')}
showActionIndicator
onPress={this.changeTheme}
onPress={() => this.navigateToScreen('ThemeView')}
testID='settings-view-theme'
right={this.renderDisclosure}
theme={theme}

View File

@ -103,5 +103,12 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechrome</string>
<string>googlechromes</string>
<string>firefox</string>
<string>brave</string>
</array>
</dict>
</plist>