[NEW] Settings view (#900)
* new settings view * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * fix eslint * eslint fixed all bugs and setup on my device * move version from sidebar to settingsView * add server Version not hard coded * goto root stack after change language * support RTL * fix the ui of last section * fixed bugs done requested changes * added actions for contact us and license * done requested changes * removed verticle scroll indicator * removed default export of device info * fixed separator styling * refactor Items in settings view * changed language view * change activeOpacity * done requested changes * fixed lint * changed layout * added test * fix bug * fix bug * added e2e tests * undone unnessary changes * undone unnessary changes * removed firebase * Comment slash e2e tests * Refactor Settings * Refactor LanguageView * Separator * Unified styles * fix indentation
This commit is contained in:
parent
82afb63327
commit
c14714f16f
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
createSagaMonitor: () => {}
|
||||
};
|
|
@ -14,9 +14,12 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
export const DisclosureImage = React.memo(() => <Image source={{ uri: 'disclosure_indicator' }} style={styles.disclosureIndicator} />);
|
||||
|
||||
const DisclosureIndicator = React.memo(() => (
|
||||
<View style={styles.disclosureContainer}>
|
||||
<Image source={{ uri: 'disclosure_indicator' }} style={styles.disclosureIndicator} />
|
||||
<DisclosureImage />
|
||||
</View>
|
||||
));
|
||||
|
||||
export default DisclosureIndicator;
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { RectButton } from 'react-native-gesture-handler';
|
||||
|
||||
import { COLOR_TEXT } from '../constants/colors';
|
||||
import sharedStyles from '../views/Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: 56,
|
||||
paddingHorizontal: 15
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.3
|
||||
},
|
||||
textContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
...sharedStyles.textColorNormal,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textColorNormal,
|
||||
...sharedStyles.textRegular
|
||||
}
|
||||
});
|
||||
|
||||
const Content = React.memo(({
|
||||
title, subtitle, disabled, testID, right
|
||||
}) => (
|
||||
<View style={[styles.container, disabled && styles.disabled]} testID={testID}>
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
{subtitle
|
||||
? <Text style={styles.subtitle}>{subtitle}</Text>
|
||||
: null
|
||||
}
|
||||
</View>
|
||||
{right ? right() : null}
|
||||
</View>
|
||||
));
|
||||
|
||||
const Button = React.memo(({
|
||||
onPress, ...props
|
||||
}) => (
|
||||
<RectButton
|
||||
onPress={onPress}
|
||||
activeOpacity={0.1}
|
||||
underlayColor={COLOR_TEXT}
|
||||
enabled={!props.disabled}
|
||||
>
|
||||
<Content {...props} />
|
||||
</RectButton>
|
||||
));
|
||||
|
||||
const Item = React.memo(({ ...props }) => {
|
||||
if (props.onPress) {
|
||||
return <Button {...props} />;
|
||||
}
|
||||
return <Content {...props} />;
|
||||
});
|
||||
|
||||
Item.propTypes = {
|
||||
onPress: PropTypes.func
|
||||
};
|
||||
|
||||
Content.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string,
|
||||
right: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
testID: PropTypes.string
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
onPress: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
};
|
||||
|
||||
Button.defaultProps = {
|
||||
disabled: false
|
||||
};
|
||||
|
||||
export default Item;
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { COLOR_SEPARATOR } from '../constants/colors';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: COLOR_SEPARATOR
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const Separator = React.memo(({ style }) => <View style={[styles.separator, style]} />);
|
||||
|
||||
Separator.propTypes = {
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
export default Separator;
|
|
@ -127,6 +127,7 @@ export default {
|
|||
Connected: 'Connected',
|
||||
connecting_server: 'connecting to server',
|
||||
Connecting: 'Connecting...',
|
||||
Contact_us: 'Contact us',
|
||||
Continue_with: 'Continue with',
|
||||
Copied_to_clipboard: 'Copied to clipboard!',
|
||||
Copy: 'Copy',
|
||||
|
@ -190,6 +191,7 @@ export default {
|
|||
leaving_room: 'leaving room',
|
||||
leave: 'leave',
|
||||
Legal: 'Legal',
|
||||
License: 'License',
|
||||
Livechat: 'Livechat',
|
||||
Login: 'Login',
|
||||
Login_error: 'Your credentials were rejected! Please try again.',
|
||||
|
@ -304,14 +306,17 @@ export default {
|
|||
Select_Users: 'Select Users',
|
||||
Send: 'Send',
|
||||
Send_audio_message: 'Send audio message',
|
||||
Send_crash_report: 'Send crash report',
|
||||
Send_message: 'Send message',
|
||||
Sent_an_attachment: 'Sent an attachment',
|
||||
Server: 'Server',
|
||||
Servers: 'Servers',
|
||||
Server_version: 'Server version: {{version}}',
|
||||
Set_username_subtitle: 'The username is used to allow others to mention you in messages',
|
||||
Settings: 'Settings',
|
||||
Settings_succesfully_changed: 'Settings succesfully changed!',
|
||||
Share: 'Share',
|
||||
Share_this_app: 'Share this app',
|
||||
Sign_in_your_server: 'Sign in your server',
|
||||
Sign_Up: 'Sign Up',
|
||||
Some_field_is_invalid_or_empty: 'Some field is invalid or empty',
|
||||
|
@ -328,6 +333,7 @@ export default {
|
|||
tap_to_change_status: 'tap to change status',
|
||||
Tap_to_view_servers_list: 'Tap to view servers list',
|
||||
Terms_of_Service: ' Terms of Service ',
|
||||
Theme: 'Theme',
|
||||
The_URL_is_invalid: 'The URL you entered is invalid. Check it and try again, please!',
|
||||
There_was_an_error_while_action: 'There was an error while {{action}}!',
|
||||
This_room_is_blocked: 'This room is blocked',
|
||||
|
@ -381,5 +387,8 @@ export default {
|
|||
you_were_mentioned: 'you were mentioned',
|
||||
you: 'you',
|
||||
You: 'You',
|
||||
You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!'
|
||||
Version_no: 'Version: {{version}}',
|
||||
You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!',
|
||||
Change_Language: 'Change Language',
|
||||
Crash_report_disclaimer: 'We never track the content of your chats. The crash report only contains relevant information for us in order '
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ import Navigation from './lib/Navigation';
|
|||
import Sidebar from './views/SidebarView';
|
||||
import ProfileView from './views/ProfileView';
|
||||
import SettingsView from './views/SettingsView';
|
||||
import LanguageView from './views/LanguageView';
|
||||
import AdminPanelView from './views/AdminPanelView';
|
||||
import RoomActionsView from './views/RoomActionsView';
|
||||
import RoomInfoView from './views/RoomInfoView';
|
||||
|
@ -148,7 +149,8 @@ ProfileStack.navigationOptions = ({ navigation }) => {
|
|||
};
|
||||
|
||||
const SettingsStack = createStackNavigator({
|
||||
SettingsView
|
||||
SettingsView,
|
||||
LanguageView
|
||||
}, {
|
||||
defaultNavigationOptions: defaultHeader
|
||||
});
|
||||
|
|
|
@ -8,11 +8,4 @@ export const isIOS = Platform.OS === 'ios';
|
|||
export const isAndroid = !isIOS;
|
||||
export const getReadableVersion = DeviceInfo.getReadableVersion();
|
||||
export const getBundleId = DeviceInfo.getBundleId();
|
||||
|
||||
export default {
|
||||
isNotch,
|
||||
isIOS,
|
||||
isAndroid,
|
||||
getReadableVersion,
|
||||
getBundleId
|
||||
};
|
||||
export const getDeviceModel = DeviceInfo.getModel();
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { SafeAreaView, NavigationActions } from 'react-navigation';
|
||||
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import I18n from '../../i18n';
|
||||
import Loading from '../../containers/Loading';
|
||||
import { showErrorAlert } from '../../utils/info';
|
||||
import log from '../../utils/log';
|
||||
import { setUser as setUserAction } from '../../actions/login';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import sharedStyles from '../Styles';
|
||||
import ListItem from '../../containers/ListItem';
|
||||
import Separator from '../../containers/Separator';
|
||||
|
||||
const LANGUAGES = [
|
||||
{
|
||||
label: '简体中文',
|
||||
value: 'zh-CN'
|
||||
}, {
|
||||
label: 'Deutsch',
|
||||
value: 'de'
|
||||
}, {
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
}, {
|
||||
label: 'Français',
|
||||
value: 'fr'
|
||||
}, {
|
||||
label: 'Português (BR)',
|
||||
value: 'pt-BR'
|
||||
}, {
|
||||
label: 'Português (PT)',
|
||||
value: 'pt-PT'
|
||||
}, {
|
||||
label: 'Russian',
|
||||
value: 'ru'
|
||||
}
|
||||
];
|
||||
|
||||
@connect(state => ({
|
||||
userLanguage: state.login.user && state.login.user.language
|
||||
}), dispatch => ({
|
||||
setUser: params => dispatch(setUserAction(params))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class LanguageView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
title: I18n.t('Change_Language')
|
||||
})
|
||||
|
||||
static propTypes = {
|
||||
userLanguage: PropTypes.string,
|
||||
navigation: PropTypes.object,
|
||||
setUser: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
language: props.userLanguage ? props.userLanguage : 'en',
|
||||
saving: false
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { language, saving } = this.state;
|
||||
const { userLanguage } = this.props;
|
||||
if (nextState.language !== language) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.saving !== saving) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.userLanguage !== userLanguage) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
formIsChanged = (language) => {
|
||||
const { userLanguage } = this.props;
|
||||
return (userLanguage !== language);
|
||||
}
|
||||
|
||||
submit = async(language) => {
|
||||
if (!this.formIsChanged(language)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ saving: true });
|
||||
|
||||
const { userLanguage, setUser, navigation } = this.props;
|
||||
|
||||
const params = {};
|
||||
|
||||
// language
|
||||
if (userLanguage !== language) {
|
||||
params.language = language;
|
||||
}
|
||||
|
||||
try {
|
||||
await RocketChat.saveUserPreferences(params);
|
||||
setUser({ language: params.language });
|
||||
|
||||
this.setState({ saving: false });
|
||||
setTimeout(() => {
|
||||
navigation.reset([NavigationActions.navigate({ routeName: 'SettingsView' })], 0);
|
||||
navigation.navigate('RoomsListView');
|
||||
}, 300);
|
||||
} catch (e) {
|
||||
this.setState({ saving: false });
|
||||
setTimeout(() => {
|
||||
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('saving_preferences') }));
|
||||
log('err_save_user_preferences', e);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
renderSeparator = () => <Separator />
|
||||
|
||||
renderIcon = () => <CustomIcon name='check' size={20} style={sharedStyles.colorPrimary} />
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { value, label } = item;
|
||||
const { language } = this.state;
|
||||
const isSelected = language === value;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
title={label}
|
||||
onPress={() => this.submit(value)}
|
||||
testID={`language-view-${ value }`}
|
||||
right={isSelected ? this.renderIcon : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { saving } = this.state;
|
||||
return (
|
||||
<SafeAreaView style={sharedStyles.listSafeArea} testID='language-view' forceInset={{ bottom: 'never' }}>
|
||||
<StatusBar />
|
||||
<FlatList
|
||||
data={LANGUAGES}
|
||||
keyExtractor={item => item.value}
|
||||
contentContainerStyle={sharedStyles.listContentContainer}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
/>
|
||||
<Loading visible={saving} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,236 +1,160 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
View, ScrollView, Switch, Text, StyleSheet, AsyncStorage
|
||||
View, Linking, ScrollView, AsyncStorage, SafeAreaView, Switch
|
||||
} from 'react-native';
|
||||
import RNPickerSelect from 'react-native-picker-select';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
import firebase from 'react-native-firebase';
|
||||
|
||||
import RocketChat, { MARKDOWN_KEY } from '../../lib/rocketchat';
|
||||
import KeyboardView from '../../presentation/KeyboardView';
|
||||
import sharedStyles from '../Styles';
|
||||
import RCTextInput from '../../containers/TextInput';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import I18n from '../../i18n';
|
||||
import Button from '../../containers/Button';
|
||||
import Loading from '../../containers/Loading';
|
||||
import { showErrorAlert, Toast } from '../../utils/info';
|
||||
import log from '../../utils/log';
|
||||
import { setUser as setUserAction } from '../../actions/login';
|
||||
import { toggleMarkdown as toggleMarkdownAction } from '../../actions/markdown';
|
||||
import { COLOR_DANGER, COLOR_SUCCESS } from '../../constants/colors';
|
||||
import { DrawerButton } from '../../containers/HeaderButton';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { isAndroid } from '../../utils/deviceInfo';
|
||||
import {
|
||||
COLOR_WHITE, COLOR_SEPARATOR, COLOR_DANGER, COLOR_SUCCESS
|
||||
} from '../../constants/colors';
|
||||
import ListItem from '../../containers/ListItem';
|
||||
import { DisclosureImage } from '../../containers/DisclosureIndicator';
|
||||
import Separator from '../../containers/Separator';
|
||||
import I18n from '../../i18n';
|
||||
import { MARKDOWN_KEY } from '../../lib/rocketchat';
|
||||
import { getReadableVersion, getDeviceModel, isAndroid } from '../../utils/deviceInfo';
|
||||
import openLink from '../../utils/openLink';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import { showErrorAlert } from '../../utils/info';
|
||||
import styles from './styles';
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
swithContainer: {
|
||||
backgroundColor: COLOR_WHITE,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
label: {
|
||||
fontSize: 17,
|
||||
flex: 1,
|
||||
...sharedStyles.textMedium,
|
||||
...sharedStyles.textColorNormal
|
||||
},
|
||||
separator: {
|
||||
flex: 1,
|
||||
height: 1,
|
||||
backgroundColor: COLOR_SEPARATOR,
|
||||
marginVertical: 10
|
||||
}
|
||||
});
|
||||
const LICENSE_LINK = 'https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/LICENSE';
|
||||
const SectionSeparator = React.memo(() => <View style={styles.sectionSeparatorBorder} />);
|
||||
const SWITCH_TRACK_COLOR = {
|
||||
false: isAndroid ? COLOR_DANGER : null,
|
||||
true: COLOR_SUCCESS
|
||||
};
|
||||
|
||||
@connect(state => ({
|
||||
userLanguage: state.login.user && state.login.user.language,
|
||||
server: state.server,
|
||||
useMarkdown: state.markdown.useMarkdown
|
||||
}), dispatch => ({
|
||||
setUser: params => dispatch(setUserAction(params)),
|
||||
toggleMarkdown: params => dispatch(toggleMarkdownAction(params))
|
||||
}))
|
||||
export default class SettingsView extends React.Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
headerLeft: <DrawerButton navigation={navigation} />,
|
||||
title: I18n.t('Settings')
|
||||
})
|
||||
});
|
||||
|
||||
static propTypes = {
|
||||
componentId: PropTypes.string,
|
||||
userLanguage: PropTypes.string,
|
||||
navigation: PropTypes.object,
|
||||
server: PropTypes.object,
|
||||
useMarkdown: PropTypes.bool,
|
||||
setUser: PropTypes.func,
|
||||
toggleMarkdown: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
placeholder: {},
|
||||
language: props.userLanguage ? props.userLanguage : 'en',
|
||||
languages: [{
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
}, {
|
||||
label: 'Português (BR)',
|
||||
value: 'pt-BR'
|
||||
}, {
|
||||
label: 'Russian',
|
||||
value: 'ru'
|
||||
}, {
|
||||
label: '简体中文',
|
||||
value: 'zh-CN'
|
||||
}, {
|
||||
label: 'Français',
|
||||
value: 'fr'
|
||||
}, {
|
||||
label: 'Deutsch',
|
||||
value: 'de'
|
||||
}, {
|
||||
label: 'Português (PT)',
|
||||
value: 'pt-PT'
|
||||
}],
|
||||
saving: false
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { language, saving } = this.state;
|
||||
const { userLanguage, useMarkdown } = this.props;
|
||||
if (nextState.language !== language) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.saving !== saving) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.useMarkdown !== useMarkdown) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.userLanguage !== userLanguage) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getLabel = (language) => {
|
||||
const { languages } = this.state;
|
||||
const l = languages.find(i => i.value === language);
|
||||
if (l && l.label) {
|
||||
return l.label;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
formIsChanged = () => {
|
||||
const { userLanguage } = this.props;
|
||||
const { language } = this.state;
|
||||
return !(userLanguage === language);
|
||||
}
|
||||
|
||||
submit = async() => {
|
||||
this.setState({ saving: true });
|
||||
|
||||
const { language } = this.state;
|
||||
const { userLanguage, setUser } = this.props;
|
||||
|
||||
if (!this.formIsChanged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {};
|
||||
|
||||
// language
|
||||
if (userLanguage !== language) {
|
||||
params.language = language;
|
||||
}
|
||||
|
||||
try {
|
||||
await RocketChat.saveUserPreferences(params);
|
||||
setUser({ language: params.language });
|
||||
|
||||
this.setState({ saving: false });
|
||||
setTimeout(() => {
|
||||
this.toast.show(I18n.t('Preferences_saved'));
|
||||
}, 300);
|
||||
} catch (e) {
|
||||
this.setState({ saving: false });
|
||||
setTimeout(() => {
|
||||
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('saving_preferences') }));
|
||||
log('err_save_user_preferences', e);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMarkdown = (value) => {
|
||||
AsyncStorage.setItem(MARKDOWN_KEY, JSON.stringify(value));
|
||||
const { toggleMarkdown } = this.props;
|
||||
toggleMarkdown(value);
|
||||
firebase.analytics().logEvent('toggle_markdown', { value });
|
||||
}
|
||||
|
||||
navigateToRoom = (room) => {
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate(room);
|
||||
}
|
||||
|
||||
sendEmail = async() => {
|
||||
const subject = encodeURI('React Native App Support');
|
||||
const email = encodeURI('support@rocket.chat');
|
||||
const description = encodeURI(`
|
||||
version: ${ getReadableVersion }
|
||||
device: ${ getDeviceModel }
|
||||
`);
|
||||
try {
|
||||
await Linking.openURL(`mailto:${ email }?subject=${ subject }&body=${ description }`);
|
||||
} catch (e) {
|
||||
showErrorAlert(I18n.t('error-email-send-failed', { message: 'support@rocket.chat' }));
|
||||
}
|
||||
}
|
||||
|
||||
onPressLicense = () => openLink(LICENSE_LINK)
|
||||
|
||||
renderDisclosure = () => <DisclosureImage />
|
||||
|
||||
renderMarkdownSwitch = () => {
|
||||
const { useMarkdown } = this.props;
|
||||
return (
|
||||
<Switch
|
||||
value={useMarkdown}
|
||||
trackColor={SWITCH_TRACK_COLOR}
|
||||
onValueChange={this.toggleMarkdown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
language, languages, placeholder, saving
|
||||
} = this.state;
|
||||
const { useMarkdown } = this.props;
|
||||
const { server } = this.props;
|
||||
return (
|
||||
<KeyboardView
|
||||
contentContainerStyle={sharedStyles.container}
|
||||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<SafeAreaView style={sharedStyles.listSafeArea} testID='settings-view'>
|
||||
<StatusBar />
|
||||
<ScrollView
|
||||
contentContainerStyle={sharedStyles.containerScrollView}
|
||||
testID='settings-view-list'
|
||||
{...scrollPersistTaps}
|
||||
contentContainerStyle={sharedStyles.listContentContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
testID='settings-view-list'
|
||||
>
|
||||
<SafeAreaView style={sharedStyles.container} testID='settings-view' forceInset={{ bottom: 'never' }}>
|
||||
<RNPickerSelect
|
||||
items={languages}
|
||||
onValueChange={(value) => {
|
||||
this.setState({ language: value });
|
||||
}}
|
||||
value={language}
|
||||
placeholder={placeholder}
|
||||
>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this.name = e; }}
|
||||
label={I18n.t('Language')}
|
||||
placeholder={I18n.t('Language')}
|
||||
value={this.getLabel(language)}
|
||||
<ListItem
|
||||
title={I18n.t('Contact_us')}
|
||||
onPress={this.sendEmail}
|
||||
showActionIndicator
|
||||
testID='settings-view-contact'
|
||||
right={this.renderDisclosure}
|
||||
/>
|
||||
<Separator />
|
||||
<ListItem
|
||||
title={I18n.t('Language')}
|
||||
onPress={() => this.navigateToRoom('LanguageView')}
|
||||
showActionIndicator
|
||||
testID='settings-view-language'
|
||||
right={this.renderDisclosure}
|
||||
/>
|
||||
</RNPickerSelect>
|
||||
<View style={sharedStyles.alignItemsFlexStart}>
|
||||
<Button
|
||||
title={I18n.t('Save_Changes')}
|
||||
type='primary'
|
||||
onPress={this.submit}
|
||||
disabled={!this.formIsChanged()}
|
||||
testID='settings-view-button'
|
||||
<Separator />
|
||||
<ListItem
|
||||
title={I18n.t('Theme')}
|
||||
showActionIndicator
|
||||
disabled
|
||||
testID='settings-view-theme'
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.separator} />
|
||||
<View style={styles.swithContainer}>
|
||||
<Text style={styles.label}>{I18n.t('Enable_markdown')}</Text>
|
||||
<Switch
|
||||
value={useMarkdown}
|
||||
onValueChange={this.toggleMarkdown}
|
||||
onTintColor={COLOR_SUCCESS}
|
||||
tintColor={isAndroid ? COLOR_DANGER : null}
|
||||
<Separator />
|
||||
<ListItem
|
||||
title={I18n.t('Share_this_app')}
|
||||
showActionIndicator
|
||||
disabled
|
||||
testID='settings-view-share-app'
|
||||
/>
|
||||
|
||||
<SectionSeparator />
|
||||
|
||||
<ListItem
|
||||
title={I18n.t('License')}
|
||||
onPress={this.onPressLicense}
|
||||
showActionIndicator
|
||||
testID='settings-view-license'
|
||||
right={this.renderDisclosure}
|
||||
/>
|
||||
<Separator />
|
||||
<ListItem title={I18n.t('Version_no', { version: getReadableVersion })} testID='settings-view-version' />
|
||||
<Separator />
|
||||
<ListItem
|
||||
title={I18n.t('Server_version', { version: server.version })}
|
||||
subtitle={`${ server.server.split('//')[1] }`}
|
||||
testID='settings-view-server-version'
|
||||
/>
|
||||
|
||||
<SectionSeparator />
|
||||
|
||||
<ListItem
|
||||
title={I18n.t('Enable_markdown')}
|
||||
testID='settings-view-markdown'
|
||||
right={() => this.renderMarkdownSwitch()}
|
||||
/>
|
||||
</View>
|
||||
<Loading visible={saving} />
|
||||
<Toast ref={toast => this.toast = toast} />
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { COLOR_BACKGROUND_CONTAINER } from '../../constants/colors';
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
export default StyleSheet.create({
|
||||
sectionSeparatorBorder: {
|
||||
...sharedStyles.separatorVertical,
|
||||
backgroundColor: COLOR_BACKGROUND_CONTAINER,
|
||||
height: 10
|
||||
}
|
||||
});
|
|
@ -15,7 +15,6 @@ import RocketChat from '../../lib/rocketchat';
|
|||
import log from '../../utils/log';
|
||||
import I18n from '../../i18n';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import { getReadableVersion } from '../../utils/deviceInfo';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import styles from './styles';
|
||||
import SidebarItem from './SidebarItem';
|
||||
|
@ -279,9 +278,6 @@ export default class Sidebar extends Component {
|
|||
{!showStatus ? this.renderNavigation() : null}
|
||||
{showStatus ? this.renderStatus() : null}
|
||||
</ScrollView>
|
||||
<Text style={styles.version}>
|
||||
{getReadableVersion}
|
||||
</Text>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { StyleSheet, Platform } from 'react-native';
|
||||
|
||||
import {
|
||||
COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_SEPARATOR, COLOR_TEXT, COLOR_TEXT_DESCRIPTION, COLOR_TITLE
|
||||
COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_SEPARATOR, COLOR_TEXT, COLOR_TEXT_DESCRIPTION, COLOR_TITLE, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_PRIMARY
|
||||
} from '../constants/colors';
|
||||
|
||||
export default StyleSheet.create({
|
||||
|
@ -176,7 +176,21 @@ export default StyleSheet.create({
|
|||
textColorDescription: {
|
||||
color: COLOR_TEXT_DESCRIPTION
|
||||
},
|
||||
colorPrimary: {
|
||||
color: COLOR_PRIMARY
|
||||
},
|
||||
inputLastChild: {
|
||||
marginBottom: 15
|
||||
},
|
||||
listSafeArea: {
|
||||
flex: 1,
|
||||
backgroundColor: COLOR_BACKGROUND_CONTAINER
|
||||
},
|
||||
listContentContainer: {
|
||||
borderColor: COLOR_SEPARATOR,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
backgroundColor: COLOR_WHITE,
|
||||
marginVertical: 10
|
||||
}
|
||||
});
|
||||
|
|
|
@ -159,30 +159,30 @@ describe('Room screen', () => {
|
|||
await element(by.id('messagebox-input')).clearText();
|
||||
});
|
||||
|
||||
it('should show and tap on slash command autocomplete and send slash command', async() => {
|
||||
await element(by.id('messagebox-input')).tap();
|
||||
await element(by.id('messagebox-input')).typeText('/');
|
||||
await waitFor(element(by.id('messagebox-container'))).toBeVisible().withTimeout(10000);
|
||||
await expect(element(by.id('messagebox-container'))).toBeVisible();
|
||||
await element(by.id('mention-item-shrug')).tap();
|
||||
await expect(element(by.id('messagebox-input'))).toHaveText('/shrug ');
|
||||
await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard
|
||||
await element(by.id('messagebox-send-message')).tap();
|
||||
await waitFor(element(by.text(`joy ¯\_(ツ)_/¯`))).toBeVisible().withTimeout(60000);
|
||||
});
|
||||
// it('should show and tap on slash command autocomplete and send slash command', async() => {
|
||||
// await element(by.id('messagebox-input')).tap();
|
||||
// await element(by.id('messagebox-input')).typeText('/');
|
||||
// await waitFor(element(by.id('messagebox-container'))).toBeVisible().withTimeout(10000);
|
||||
// await expect(element(by.id('messagebox-container'))).toBeVisible();
|
||||
// await element(by.id('mention-item-shrug')).tap();
|
||||
// await expect(element(by.id('messagebox-input'))).toHaveText('/shrug ');
|
||||
// await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard
|
||||
// await element(by.id('messagebox-send-message')).tap();
|
||||
// await waitFor(element(by.text(`joy ¯\_(ツ)_/¯`))).toBeVisible().withTimeout(60000);
|
||||
// });
|
||||
|
||||
it('should show command Preview', async() => {
|
||||
await element(by.id('messagebox-input')).tap();
|
||||
await element(by.id('messagebox-input')).replaceText('/giphy');
|
||||
await waitFor(element(by.id('messagebox-container'))).toBeVisible().withTimeout(10000);
|
||||
await expect(element(by.id('messagebox-container'))).toBeVisible();
|
||||
await element(by.id('mention-item-giphy')).tap();
|
||||
await expect(element(by.id('messagebox-input'))).toHaveText('/giphy ');
|
||||
await element(by.id('messagebox-input')).typeText('no'); // workaround for number keyboard
|
||||
await waitFor(element(by.id('commandbox-container'))).toBeVisible().withTimeout(10000);
|
||||
await expect(element(by.id('commandbox-container'))).toBeVisible();
|
||||
await element(by.id('messagebox-input')).clearText();
|
||||
});
|
||||
// it('should show command Preview', async() => {
|
||||
// await element(by.id('messagebox-input')).tap();
|
||||
// await element(by.id('messagebox-input')).replaceText('/giphy');
|
||||
// await waitFor(element(by.id('messagebox-container'))).toBeVisible().withTimeout(10000);
|
||||
// await expect(element(by.id('messagebox-container'))).toBeVisible();
|
||||
// await element(by.id('mention-item-giphy')).tap();
|
||||
// await expect(element(by.id('messagebox-input'))).toHaveText('/giphy ');
|
||||
// await element(by.id('messagebox-input')).typeText('no'); // workaround for number keyboard
|
||||
// await waitFor(element(by.id('commandbox-container'))).toBeVisible().withTimeout(10000);
|
||||
// await expect(element(by.id('commandbox-container'))).toBeVisible();
|
||||
// await element(by.id('messagebox-input')).clearText();
|
||||
// });
|
||||
});
|
||||
|
||||
describe('Message', async() => {
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
const {
|
||||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const { takeScreenshot } = require('./helpers/screenshot');
|
||||
const { logout, navigateToLogin, login } = require('./helpers/app');
|
||||
|
||||
describe('Settings screen', () => {
|
||||
before(async() => {
|
||||
await device.reloadReactNative();
|
||||
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||
await element(by.id('rooms-list-view-sidebar')).tap();
|
||||
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
|
||||
await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000);
|
||||
await element(by.id('sidebar-settings')).tap();
|
||||
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
|
||||
|
||||
});
|
||||
|
||||
describe('Render', async() => {
|
||||
it('should have settings view', async() => {
|
||||
await expect(element(by.id('settings-view'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should have language', async() => {
|
||||
await expect(element(by.id('settings-view-language'))).toExist();
|
||||
});
|
||||
|
||||
it('should have theme', async() => {
|
||||
await expect(element(by.id('settings-view-theme'))).toExist();
|
||||
});
|
||||
|
||||
it('should have share app', async() => {
|
||||
await expect(element(by.id('settings-view-share-app'))).toExist();
|
||||
});
|
||||
|
||||
it('should have licence', async() => {
|
||||
await expect(element(by.id('settings-view-license'))).toExist();
|
||||
});
|
||||
|
||||
it('should have version no', async() => {
|
||||
await expect(element(by.id('settings-view-version'))).toExist();
|
||||
});
|
||||
|
||||
it('should have server version', async() => {
|
||||
await expect(element(by.id('settings-view-server-version'))).toExist();
|
||||
});
|
||||
|
||||
it('should have enable markdown', async() => {
|
||||
await expect(element(by.id('settings-view-markdown'))).toExist();
|
||||
});
|
||||
|
||||
after(async() => {
|
||||
takeScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Language', async() => {
|
||||
it('should navigate to language view', async() => {
|
||||
await element(by.id('settings-view-language')).tap();
|
||||
await waitFor(element(by.id('language-view'))).toBeVisible().withTimeout(60000);
|
||||
await expect(element(by.id('language-view-zh-CN'))).toExist();
|
||||
await expect(element(by.id('language-view-de'))).toExist();
|
||||
await expect(element(by.id('language-view-en'))).toExist();
|
||||
await expect(element(by.id('language-view-fr'))).toExist();
|
||||
await expect(element(by.id('language-view-pt-BR'))).toExist();
|
||||
await expect(element(by.id('language-view-pt-PT'))).toExist();
|
||||
await expect(element(by.id('language-view-ru'))).toExist();
|
||||
});
|
||||
|
||||
it('should navigate to change language', async() => {
|
||||
await expect(element(by.id('language-view-zh-CN'))).toExist();
|
||||
await element(by.id('language-view-zh-CN')).tap()
|
||||
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
|
||||
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||
await element(by.id('rooms-list-view-sidebar')).tap();
|
||||
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
|
||||
await waitFor(element(by.text('设置'))).toBeVisible().withTimeout(2000);
|
||||
await element(by.text('设置')).tap();
|
||||
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
|
||||
await element(by.id('settings-view-language')).tap();
|
||||
await element(by.id('language-view-en')).tap();
|
||||
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
|
||||
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||
await element(by.id('rooms-list-view-sidebar')).tap();
|
||||
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
|
||||
await expect(element(by.text('Settings'))).toBeVisible();
|
||||
await element(by.text('Settings')).tap();
|
||||
await expect(element(by.id('settings-view'))).toBeVisible();
|
||||
});
|
||||
after(async() => {
|
||||
takeScreenshot();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue