Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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