[NEW] Onboarding layout (#1954)

* Onboarding texts

* OnboardingView

* FormContainer

* Minor fixes

* NewServerView

* Remove code

* Refactor

* WorkspaceView

* Stash

* Login with email working

* Login with

* Join open

* Revert "Login with"

This reverts commit d05dc507d2.

* Fix create account styles

* Register

* Refactor

* LoginServices component

* Refactor

* Multiple servers

* Remove native images

* Refactor styles

* Fix testid

* Fix add server on tablet

* i18n

* Fix close modal

* Fix TOTP

* [FIX] Registration disabled

* [FIX] Login Services separator

* Fix logos

* Fix AppVersion name

* I18n

* Minor fixes

* [FIX] Custom Fields

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
This commit is contained in:
Diego Mello 2020-03-30 16:20:50 -03:00 committed by GitHub
parent 1ce0f6db59
commit acdf39b32d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 945 additions and 1112 deletions

BIN
android/app/src/main/res/drawable-hdpi/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
android/app/src/main/res/drawable-mdpi/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

BIN
android/app/src/main/res/drawable-xhdpi/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
android/app/src/main/res/drawable-xxhdpi/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

BIN
android/app/src/main/res/drawable-xxxhdpi/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,4 +1,4 @@
export const MAX_SIDEBAR_WIDTH = 321; export const MAX_SIDEBAR_WIDTH = 321;
export const MAX_CONTENT_WIDTH = '90%'; export const MAX_CONTENT_WIDTH = '90%';
export const MAX_SCREEN_CONTENT_WIDTH = '45%'; export const MAX_SCREEN_CONTENT_WIDTH = '50%';
export const MIN_WIDTH_SPLIT_LAYOUT = 700; export const MIN_WIDTH_SPLIT_LAYOUT = 700;

View File

@ -0,0 +1,34 @@
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../constants/colors';
import sharedStyles from '../views/Styles';
import { getReadableVersion } from '../utils/deviceInfo';
import I18n from '../i18n';
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'flex-end'
},
text: {
...sharedStyles.textRegular,
fontSize: 13
},
bold: {
...sharedStyles.textSemibold
}
});
const AppVersion = React.memo(({ theme }) => (
<View style={styles.container}>
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{I18n.t('Version_no', { version: '' })}<Text style={styles.bold}>{getReadableVersion}</Text></Text>
</View>
));
AppVersion.propTypes = {
theme: PropTypes.string
};
export default AppVersion;

View File

@ -9,15 +9,19 @@ import ActivityIndicator from '../ActivityIndicator';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
paddingHorizontal: 15, paddingHorizontal: 14,
justifyContent: 'center', justifyContent: 'center',
height: 48, height: 48,
borderRadius: 2, borderRadius: 2,
marginBottom: 10 marginBottom: 12
}, },
text: { text: {
fontSize: 18, fontSize: 16,
textAlign: 'center' textAlign: 'center',
...sharedStyles.textMedium
},
disabled: {
opacity: 0.3
} }
}); });
@ -30,6 +34,8 @@ export default class Button extends React.PureComponent {
backgroundColor: PropTypes.string, backgroundColor: PropTypes.string,
loading: PropTypes.bool, loading: PropTypes.bool,
theme: PropTypes.string, theme: PropTypes.string,
color: PropTypes.string,
fontSize: PropTypes.string,
style: PropTypes.any style: PropTypes.any
} }
@ -43,9 +49,15 @@ export default class Button extends React.PureComponent {
render() { render() {
const { const {
title, type, onPress, disabled, backgroundColor, loading, style, theme, ...otherProps title, type, onPress, disabled, backgroundColor, color, loading, style, theme, fontSize, ...otherProps
} = this.props; } = this.props;
const isPrimary = type === 'primary'; const isPrimary = type === 'primary';
let textColor = isPrimary ? themes[theme].buttonText : themes[theme].bodyText;
if (color) {
textColor = color;
}
return ( return (
<Touchable <Touchable
onPress={onPress} onPress={onPress}
@ -55,20 +67,20 @@ export default class Button extends React.PureComponent {
backgroundColor backgroundColor
? { backgroundColor } ? { backgroundColor }
: { backgroundColor: isPrimary ? themes[theme].actionTintColor : themes[theme].backgroundColor }, : { backgroundColor: isPrimary ? themes[theme].actionTintColor : themes[theme].backgroundColor },
disabled && { backgroundColor: themes[theme].borderColor }, disabled && styles.disabled,
style style
]} ]}
{...otherProps} {...otherProps}
> >
{ {
loading loading
? <ActivityIndicator color={isPrimary ? themes[theme].buttonText : themes[theme].actionTintColor} /> ? <ActivityIndicator color={textColor} />
: ( : (
<Text <Text
style={[ style={[
styles.text, styles.text,
isPrimary ? sharedStyles.textMedium : sharedStyles.textBold, { color: textColor },
{ color: isPrimary ? themes[theme].buttonText : themes[theme].actionTintColor } fontSize && { fontSize }
]} ]}
> >
{title} {title}

View File

@ -0,0 +1,51 @@
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';
import { SafeAreaView } from 'react-navigation';
import { themes } from '../constants/colors';
import sharedStyles from '../views/Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import KeyboardView from '../presentation/KeyboardView';
import StatusBar from './StatusBar';
import AppVersion from './AppVersion';
import { isTablet } from '../utils/deviceInfo';
const styles = StyleSheet.create({
scrollView: {
minHeight: '100%'
}
});
export const FormContainerInner = ({ children }) => (
<View style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]}>
{children}
</View>
);
const FormContainer = ({ children, theme }) => (
<KeyboardView
style={{ backgroundColor: themes[theme].backgroundColor }}
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}>
<SafeAreaView style={sharedStyles.container} forceInset={{ top: 'never' }}>
{children}
<AppVersion theme={theme} />
</SafeAreaView>
</ScrollView>
</KeyboardView>
);
FormContainer.propTypes = {
theme: PropTypes.string,
children: PropTypes.element
};
FormContainerInner.propTypes = {
children: PropTypes.element
};
export default FormContainer;

View File

@ -36,9 +36,9 @@ export const DrawerButton = React.memo(({ navigation, testID, ...otherProps }) =
</CustomHeaderButtons> </CustomHeaderButtons>
)); ));
export const CloseModalButton = React.memo(({ navigation, testID }) => ( export const CloseModalButton = React.memo(({ navigation, testID, onPress = () => navigation.pop() }) => (
<CustomHeaderButtons left> <CustomHeaderButtons left>
<Item title='close' iconName='cross' onPress={() => navigation.pop()} testID={testID} /> <Item title='close' iconName='cross' onPress={onPress} testID={testID} />
</CustomHeaderButtons> </CustomHeaderButtons>
)); ));
@ -76,7 +76,8 @@ DrawerButton.propTypes = {
}; };
CloseModalButton.propTypes = { CloseModalButton.propTypes = {
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,
testID: PropTypes.string.isRequired testID: PropTypes.string.isRequired,
onPress: PropTypes.func
}; };
CloseShareExtensionButton.propTypes = { CloseShareExtensionButton.propTypes = {
onPress: PropTypes.func.isRequired, onPress: PropTypes.func.isRequired,

View File

@ -1,42 +1,32 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { import {
Text, View, ScrollView, Image, StyleSheet, Animated, Easing View, StyleSheet, Text, Animated, Easing, Image
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
import { SafeAreaView } from 'react-navigation'; import { withNavigation } from 'react-navigation';
import { BorderlessButton } from 'react-native-gesture-handler';
import equal from 'deep-equal';
import Touch from '../utils/touch';
import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import random from '../utils/random';
import Button from '../containers/Button';
import I18n from '../i18n';
import { LegalButton } from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { themedHeader } from '../utils/navigation'; import sharedStyles from '../views/Styles';
import { isTablet } from '../utils/deviceInfo'; import { themes } from '../constants/colors';
import { loginRequest as loginRequestAction } from '../actions/login';
import Button from './Button';
import OnboardingSeparator from './OnboardingSeparator';
import Touch from '../utils/touch';
import I18n from '../i18n';
import random from '../utils/random';
const SERVICE_HEIGHT = 58;
const SERVICES_COLLAPSED_HEIGHT = 174;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: {
paddingVertical: 30
},
safeArea: {
paddingBottom: 30,
flex: 1
},
serviceButton: { serviceButton: {
borderRadius: 2, borderRadius: 2,
marginBottom: 10 marginBottom: 10
}, },
serviceButtonContainer: { serviceButtonContainer: {
borderRadius: 2, borderRadius: 2,
borderWidth: 1,
width: '100%', width: '100%',
height: 48, height: 48,
flexDirection: 'row', flexDirection: 'row',
@ -56,124 +46,32 @@ const styles = StyleSheet.create({
fontSize: 16 fontSize: 16
}, },
serviceName: { serviceName: {
...sharedStyles.textBold ...sharedStyles.textSemibold
}, },
registerDisabled: { options: {
...sharedStyles.textRegular, marginBottom: 0
...sharedStyles.textAlignCenter,
fontSize: 16
},
servicesTogglerContainer: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 5,
marginBottom: 30
},
servicesToggler: {
width: 32,
height: 31
},
separatorContainer: {
marginTop: 5,
marginBottom: 15
},
separatorLine: {
flex: 1,
height: 1
},
separatorLineLeft: {
marginRight: 15
},
separatorLineRight: {
marginLeft: 15
},
inverted: {
transform: [{ scaleY: -1 }]
} }
}); });
const SERVICE_HEIGHT = 58; class LoginServices extends React.PureComponent {
const SERVICES_COLLAPSED_HEIGHT = 174;
class LoginSignupView extends React.Component {
static navigationOptions = ({ navigation, screenProps }) => {
const title = navigation.getParam('title', 'Rocket.Chat');
return {
...themedHeader(screenProps.theme),
title,
headerRight: <LegalButton testID='welcome-view-more' navigation={navigation} />
};
}
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
server: PropTypes.string, server: PropTypes.string,
services: PropTypes.object, services: PropTypes.object,
Site_Name: PropTypes.string,
Gitlab_URL: PropTypes.string, Gitlab_URL: PropTypes.string,
CAS_enabled: PropTypes.bool, CAS_enabled: PropTypes.bool,
CAS_login_url: PropTypes.string, CAS_login_url: PropTypes.string,
Accounts_ShowFormLogin: PropTypes.bool, separator: PropTypes.bool,
Accounts_RegistrationForm: PropTypes.string,
Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
theme: PropTypes.string theme: PropTypes.string
} }
constructor(props) { static defaultProps = {
super(props); separator: true
this.state = {
collapsed: true,
servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT)
};
const { Site_Name } = this.props;
this.setTitle(Site_Name);
} }
shouldComponentUpdate(nextProps, nextState) { state = {
const { collapsed, servicesHeight } = this.state; collapsed: true,
const { servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT)
server, Site_Name, services, Accounts_ShowFormLogin, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme
} = this.props;
if (nextState.collapsed !== collapsed) {
return true;
}
if (nextState.servicesHeight !== servicesHeight) {
return true;
}
if (nextProps.server !== server) {
return true;
}
if (nextProps.Site_Name !== Site_Name) {
return true;
}
if (nextProps.theme !== theme) {
return true;
}
if (nextProps.Accounts_ShowFormLogin !== Accounts_ShowFormLogin) {
return true;
}
if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) {
return true;
}
if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) {
return true;
}
if (!equal(nextProps.services, services)) {
return true;
}
return false;
}
componentDidUpdate(prevProps) {
const { Site_Name } = this.props;
if (Site_Name && prevProps.Site_Name !== Site_Name) {
this.setTitle(Site_Name);
}
}
setTitle = (title) => {
const { navigation } = this.props;
navigation.setParams({ title });
} }
onPressFacebook = () => { onPressFacebook = () => {
@ -300,16 +198,6 @@ class LoginSignupView extends React.Component {
navigation.navigate('AuthenticationWebView', { url, authType, ssoToken }); navigation.navigate('AuthenticationWebView', { url, authType, ssoToken });
} }
login = () => {
const { navigation, Site_Name } = this.props;
navigation.navigate('LoginView', { title: Site_Name });
}
register = () => {
const { navigation, Site_Name } = this.props;
navigation.navigate('RegisterView', { title: Site_Name });
}
transitionServicesTo = (height) => { transitionServicesTo = (height) => {
const { servicesHeight } = this.state; const { servicesHeight } = this.state;
if (this._animation) { if (this._animation) {
@ -350,27 +238,28 @@ class LoginSignupView extends React.Component {
renderServicesSeparator = () => { renderServicesSeparator = () => {
const { collapsed } = this.state; const { collapsed } = this.state;
const { const { services, separator, theme } = this.props;
services, theme, Accounts_ShowFormLogin, Accounts_RegistrationForm
} = this.props;
const { length } = Object.values(services); const { length } = Object.values(services);
if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) { if (length > 3 && separator) {
return ( return (
<View style={styles.servicesTogglerContainer}> <>
<View style={[styles.separatorLine, styles.separatorLineLeft, { backgroundColor: themes[theme].auxiliaryText }]} /> <Button
<BorderlessButton onPress={this.toggleServices}> title={collapsed ? I18n.t('Onboarding_more_options') : I18n.t('Onboarding_less_options')}
<Image source={{ uri: 'options' }} style={[styles.servicesToggler, !collapsed && styles.inverted]} /> type='secondary'
</BorderlessButton> onPress={this.toggleServices}
<View style={[styles.separatorLine, styles.separatorLineRight, { backgroundColor: themes[theme].auxiliaryText }]} /> theme={theme}
</View> style={styles.options}
color={themes[theme].actionTintColor}
/>
<OnboardingSeparator theme={theme} />
</>
); );
} }
return ( if (length > 0 && separator) {
<View style={styles.separatorContainer}> return <OnboardingSeparator theme={theme} />;
<View style={styles.separatorLine} /> }
</View> return null;
);
} }
renderItem = (service) => { renderItem = (service) => {
@ -412,14 +301,19 @@ class LoginSignupView extends React.Component {
</> </>
); );
} }
const backgroundColor = isSaml && service.buttonColor ? service.buttonColor : themes[theme].chatComponentBackground;
return ( return (
<Touch <Touch
key={service.name} key={service.name}
onPress={onPress} onPress={onPress}
style={[styles.serviceButton, isSaml && { backgroundColor: service.buttonColor }]} style={[styles.serviceButton, { backgroundColor }]}
theme={theme} theme={theme}
activeOpacity={0.5}
underlayColor={themes[theme].buttonText}
> >
<View style={[styles.serviceButtonContainer, { borderColor: themes[theme].borderColor }]}> <View style={styles.serviceButtonContainer}>
{service.authType === 'oauth' ? <Image source={{ uri: icon }} style={styles.serviceIcon} /> : null} {service.authType === 'oauth' ? <Image source={{ uri: icon }} style={styles.serviceIcon} /> : null}
<Text style={[styles.serviceText, { color: themes[theme].titleText }]}>{buttonText}</Text> <Text style={[styles.serviceText, { color: themes[theme].titleText }]}>{buttonText}</Text>
</View> </View>
@ -427,100 +321,44 @@ class LoginSignupView extends React.Component {
); );
} }
renderServices = () => { render() {
const { servicesHeight } = this.state; const { servicesHeight } = this.state;
const { services, Accounts_ShowFormLogin, Accounts_RegistrationForm } = this.props; const { services, separator } = this.props;
const { length } = Object.values(services); const { length } = Object.values(services);
const style = { const style = {
overflow: 'hidden', overflow: 'hidden',
height: servicesHeight height: servicesHeight
}; };
if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) { if (length > 3 && separator) {
return ( return (
<Animated.View style={style}> <>
{Object.values(services).map(service => this.renderItem(service))} <Animated.View style={style}>
</Animated.View> {Object.values(services).map(service => this.renderItem(service))}
</Animated.View>
{this.renderServicesSeparator()}
</>
); );
} }
return ( return (
<View> <>
{Object.values(services).map(service => this.renderItem(service))} {Object.values(services).map(service => this.renderItem(service))}
</View> {this.renderServicesSeparator()}
); </>
}
renderLogin = () => {
const { Accounts_ShowFormLogin, theme } = this.props;
if (!Accounts_ShowFormLogin) {
return null;
}
return (
<Button
title={<Text>{I18n.t('Login_with')} <Text style={{ ...sharedStyles.textBold }}>{I18n.t('email')}</Text></Text>}
type='primary'
onPress={() => this.login()}
theme={theme}
testID='welcome-view-login'
/>
);
}
renderRegister = () => {
const { Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme } = this.props;
if (Accounts_RegistrationForm !== 'Public') {
return <Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>;
}
return (
<Button
title={I18n.t('Create_account')}
type='secondary'
onPress={() => this.register()}
theme={theme}
testID='welcome-view-register'
/>
);
}
render() {
const { theme } = this.props;
return (
<SafeAreaView
testID='welcome-view'
forceInset={{ vertical: 'never' }}
style={[styles.safeArea, { backgroundColor: themes[theme].backgroundColor }]}
>
<ScrollView
style={[
sharedStyles.containerScrollView,
sharedStyles.container,
styles.container,
{ backgroundColor: themes[theme].backgroundColor },
isTablet && sharedStyles.tabletScreenContent
]}
{...scrollPersistTaps}
>
<StatusBar theme={theme} />
{this.renderServices()}
{this.renderServicesSeparator()}
{this.renderLogin()}
{this.renderRegister()}
</ScrollView>
</SafeAreaView>
); );
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
server: state.server.server, server: state.server.server,
Site_Name: state.settings.Site_Name,
Gitlab_URL: state.settings.API_Gitlab_URL, Gitlab_URL: state.settings.API_Gitlab_URL,
CAS_enabled: state.settings.CAS_enabled, CAS_enabled: state.settings.CAS_enabled,
CAS_login_url: state.settings.CAS_login_url, CAS_login_url: state.settings.CAS_login_url,
Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin,
Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm,
Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText,
services: state.login.services services: state.login.services
}); });
export default connect(mapStateToProps)(withTheme(LoginSignupView)); const mapDispatchToProps = dispatch => ({
loginRequest: params => dispatch(loginRequestAction(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(withNavigation(LoginServices)));

View File

@ -0,0 +1,43 @@
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import I18n from '../i18n';
import sharedStyles from '../views/Styles';
import { themes } from '../constants/colors';
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 24
},
line: {
height: 1,
flex: 1
},
text: {
fontSize: 14,
marginLeft: 14,
marginRight: 14,
...sharedStyles.textMedium
}
});
const DateSeparator = React.memo(({ theme }) => {
const line = { backgroundColor: themes[theme].borderColor };
const text = { color: themes[theme].auxiliaryText };
return (
<View style={styles.container}>
<View style={[styles.line, line]} />
<Text style={[styles.text, styles.marginRight, styles.marginLeft, text]}>{I18n.t('OR')}</Text>
<View style={[styles.line, line]} />
</View>
);
});
DateSeparator.propTypes = {
theme: PropTypes.string
};
export default DateSeparator;

View File

@ -139,7 +139,6 @@ export default {
Collaborative: 'Kollaborativ', Collaborative: 'Kollaborativ',
Confirm: 'Bestätigen', Confirm: 'Bestätigen',
Connect: 'Verbinden', Connect: 'Verbinden',
Connect_to_a_server: 'Verbinden Sie sich mit einem Server',
Connected: 'Verbunden', Connected: 'Verbunden',
connecting_server: 'verbinde zum Server', connecting_server: 'verbinde zum Server',
Connecting: 'Verbinden ...', Connecting: 'Verbinden ...',
@ -198,7 +197,6 @@ export default {
Finish_recording: 'Beenden Sie die Aufnahme', Finish_recording: 'Beenden Sie die Aufnahme',
Following_thread: 'Thread folgen', Following_thread: 'Thread folgen',
For_your_security_you_must_enter_your_current_password_to_continue: 'Zu Ihrer Sicherheit müssen Sie Ihr aktuelles Passwort eingeben, um fortzufahren', For_your_security_you_must_enter_your_current_password_to_continue: 'Zu Ihrer Sicherheit müssen Sie Ihr aktuelles Passwort eingeben, um fortzufahren',
Forgot_my_password: 'Ich habe mein Passwort vergessen',
Forgot_password_If_this_email_is_registered: 'Wenn diese E-Mail registriert ist, senden wir Anweisungen zum Zurücksetzen Ihres Passworts. Wenn Sie in Kürze keine E-Mail erhalten, kommen Sie bitte zurück und versuchen Sie es erneut.', Forgot_password_If_this_email_is_registered: 'Wenn diese E-Mail registriert ist, senden wir Anweisungen zum Zurücksetzen Ihres Passworts. Wenn Sie in Kürze keine E-Mail erhalten, kommen Sie bitte zurück und versuchen Sie es erneut.',
Forgot_password: 'Passwort vergessen', Forgot_password: 'Passwort vergessen',
Forgot_Password: 'Passwort vergessen', Forgot_Password: 'Passwort vergessen',
@ -237,7 +235,6 @@ export default {
Invalid_server_version: 'Der Server, zu dem Sie eine Verbindung herstellen möchten, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{MinVersion}}.', Invalid_server_version: 'Der Server, zu dem Sie eine Verbindung herstellen möchten, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{MinVersion}}.',
Invite_Link: 'Einladungs-Link', Invite_Link: 'Einladungs-Link',
Invite_users: 'Benutzer einladen', Invite_users: 'Benutzer einladen',
Join_the_community: 'Trete der Community bei',
Join: 'Beitreten', Join: 'Beitreten',
Just_invited_people_can_access_this_channel: 'Nur eingeladene Personen können auf diesen Kanal zugreifen', Just_invited_people_can_access_this_channel: 'Nur eingeladene Personen können auf diesen Kanal zugreifen',
Language: 'Sprache', Language: 'Sprache',
@ -308,6 +305,7 @@ export default {
Notification_Preferences: 'Benachrichtigungseinstellungen', Notification_Preferences: 'Benachrichtigungseinstellungen',
Offline: 'Offline', Offline: 'Offline',
Oops: 'Hoppla!', Oops: 'Hoppla!',
Onboarding_title: 'Willkommen bei Rocket.Chat',
Online: 'Online', Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Nur autorisierte Benutzer können neue Nachrichten schreiben', Only_authorized_users_can_write_new_messages: 'Nur autorisierte Benutzer können neue Nachrichten schreiben',
Open_emoji_selector: 'Öffne die Emoji-Auswahl', Open_emoji_selector: 'Öffne die Emoji-Auswahl',
@ -481,7 +479,6 @@ export default {
Voice_call: 'Sprachanruf', Voice_call: 'Sprachanruf',
Websocket_disabled: 'Websockets sind auf diesem Server nicht aktiviert.\n{{contact}}', Websocket_disabled: 'Websockets sind auf diesem Server nicht aktiviert.\n{{contact}}',
Welcome: 'Herzlich willkommen', Welcome: 'Herzlich willkommen',
Welcome_to_RocketChat: 'Willkommen bei Rocket.Chat',
Whats_your_2fa: 'Wie lautet Ihr 2FA-Code?', Whats_your_2fa: 'Wie lautet Ihr 2FA-Code?',
Without_Servers: 'Ohne Server', Without_Servers: 'Ohne Server',
Write_External_Permission_Message: 'Rocket.Chat benötigt Zugriff auf Ihre Galerie um Bilder speichern zu können.', Write_External_Permission_Message: 'Rocket.Chat benötigt Zugriff auf Ihre Galerie um Bilder speichern zu können.',

View File

@ -139,7 +139,6 @@ export default {
Collaborative: 'Collaborative', Collaborative: 'Collaborative',
Confirm: 'Confirm', Confirm: 'Confirm',
Connect: 'Connect', Connect: 'Connect',
Connect_to_a_server: 'Connect to a server',
Connected: 'Connected', Connected: 'Connected',
connecting_server: 'connecting to server', connecting_server: 'connecting to server',
Connecting: 'Connecting...', Connecting: 'Connecting...',
@ -174,7 +173,8 @@ export default {
Direct_Messages: 'Direct Messages', Direct_Messages: 'Direct Messages',
Disable_notifications: 'Disable notifications', Disable_notifications: 'Disable notifications',
Discussions: 'Discussions', Discussions: 'Discussions',
Dont_Have_An_Account: 'Don\'t have an account?', Dont_Have_An_Account: 'Don\'t you have an account?',
Do_you_have_an_account: 'Do you have an account?',
Do_you_have_a_certificate: 'Do you have a certificate?', Do_you_have_a_certificate: 'Do you have a certificate?',
Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?', Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
edit: 'edit', edit: 'edit',
@ -198,9 +198,8 @@ export default {
Finish_recording: 'Finish recording', Finish_recording: 'Finish recording',
Following_thread: 'Following thread', Following_thread: 'Following thread',
For_your_security_you_must_enter_your_current_password_to_continue: 'For your security, you must enter your current password to continue', For_your_security_you_must_enter_your_current_password_to_continue: 'For your security, you must enter your current password to continue',
Forgot_my_password: 'Forgot my password',
Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.', Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.',
Forgot_password: 'Forgot password', Forgot_password: 'Forgot your password?',
Forgot_Password: 'Forgot Password', Forgot_Password: 'Forgot Password',
Full_table: 'Click to see full table', Full_table: 'Click to see full table',
Generate_New_Link: 'Generate New Link', Generate_New_Link: 'Generate New Link',
@ -237,8 +236,9 @@ export default {
Invalid_server_version: 'The server you\'re trying to connect is using a version that\'s not supported by the app anymore: {{currentVersion}}.\n\nWe require version {{minVersion}}', Invalid_server_version: 'The server you\'re trying to connect is using a version that\'s not supported by the app anymore: {{currentVersion}}.\n\nWe require version {{minVersion}}',
Invite_Link: 'Invite Link', Invite_Link: 'Invite Link',
Invite_users: 'Invite users', Invite_users: 'Invite users',
Join_the_community: 'Join the community',
Join: 'Join', Join: 'Join',
Join_our_open_workspace: 'Join our open workspace',
Join_your_workspace: 'Join your workspace',
Just_invited_people_can_access_this_channel: 'Just invited people can access this channel', Just_invited_people_can_access_this_channel: 'Just invited people can access this channel',
Language: 'Language', Language: 'Language',
last_message: 'last message', last_message: 'last message',
@ -308,10 +308,19 @@ export default {
Notification_Preferences: 'Notification Preferences', Notification_Preferences: 'Notification Preferences',
Offline: 'Offline', Offline: 'Offline',
Oops: 'Oops!', Oops: 'Oops!',
Onboarding_description: 'A workspace is your team or organizations space to collaborate. Ask the workspace admin for address to join or create one for your team.',
Onboarding_join_workspace: 'Join a workspace',
Onboarding_subtitle: 'Beyond Team Collaboration',
Onboarding_title: 'Welcome to Rocket.Chat',
Onboarding_join_open_description: 'Join our open workspace to chat with the Rocket.Chat team and community.',
Onboarding_agree_terms: 'By continuing, you agree to Rocket.Chat',
Onboarding_less_options: 'Less options',
Onboarding_more_options: 'More options',
Online: 'Online', Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Only authorized users can write new messages', Only_authorized_users_can_write_new_messages: 'Only authorized users can write new messages',
Open_emoji_selector: 'Open emoji selector', Open_emoji_selector: 'Open emoji selector',
Open_Source_Communication: 'Open Source Communication', Open_Source_Communication: 'Open Source Communication',
OR: 'OR',
Overwrites_the_server_configuration_and_use_room_config: 'Overwrites the server configuration and use room config', Overwrites_the_server_configuration_and_use_room_config: 'Overwrites the server configuration and use room config',
Password: 'Password', Password: 'Password',
Permalink_copied_to_clipboard: 'Permalink copied to clipboard!', Permalink_copied_to_clipboard: 'Permalink copied to clipboard!',
@ -481,9 +490,9 @@ export default {
Voice_call: 'Voice call', Voice_call: 'Voice call',
Websocket_disabled: 'Websocket is disabled for this server.\n{{contact}}', Websocket_disabled: 'Websocket is disabled for this server.\n{{contact}}',
Welcome: 'Welcome', Welcome: 'Welcome',
Welcome_to_RocketChat: 'Welcome to Rocket.Chat',
Whats_your_2fa: 'What\'s your 2FA code?', Whats_your_2fa: 'What\'s your 2FA code?',
Without_Servers: 'Without Servers', Without_Servers: 'Without Servers',
Workspaces: 'Workspaces',
Write_External_Permission_Message: 'Rocket Chat needs access to your gallery so you can save images.', Write_External_Permission_Message: 'Rocket Chat needs access to your gallery so you can save images.',
Write_External_Permission: 'Gallery Permission', Write_External_Permission: 'Gallery Permission',
Yes_action_it: 'Yes, {{action}} it!', Yes_action_it: 'Yes, {{action}} it!',
@ -503,6 +512,7 @@ export default {
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Your invite link will expire on {{date}} or after {{usesLeft}} uses.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Your invite link will expire on {{date}} or after {{usesLeft}} uses.',
Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.', Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.',
Your_invite_link_will_never_expire: 'Your invite link will never expire.', Your_invite_link_will_never_expire: 'Your invite link will never expire.',
Your_workspace: 'Your workspace',
Version_no: 'Version: {{version}}', Version_no: 'Version: {{version}}',
You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!', You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!',
Change_Language: 'Change Language', Change_Language: 'Change Language',

View File

@ -134,7 +134,6 @@ export default {
Collaborative: 'Colaborativo', Collaborative: 'Colaborativo',
Confirm: 'Confirmar', Confirm: 'Confirmar',
Connect: 'Conectar', Connect: 'Conectar',
Connect_to_a_server: 'Conectar a servidor',
Connected: 'Conectado', Connected: 'Conectado',
connecting_server: 'conectando a servidor', connecting_server: 'conectando a servidor',
Connecting: 'Conectando...', Connecting: 'Conectando...',
@ -188,7 +187,6 @@ export default {
Finish_recording: 'Finalizar grabación', Finish_recording: 'Finalizar grabación',
Following_thread: 'Siguiendo hilo', Following_thread: 'Siguiendo hilo',
For_your_security_you_must_enter_your_current_password_to_continue: 'Por seguridad, debes introducir tu contraseña para continuar', For_your_security_you_must_enter_your_current_password_to_continue: 'Por seguridad, debes introducir tu contraseña para continuar',
Forgot_my_password: 'He olvidado mi contraseña',
Forgot_password_If_this_email_is_registered: 'Si este email está registrado, te enviaremos las instrucciones para resetear tu contraseña.Si no recibes un email en un rato, vuelve aquí e inténtalo de nuevo.', Forgot_password_If_this_email_is_registered: 'Si este email está registrado, te enviaremos las instrucciones para resetear tu contraseña.Si no recibes un email en un rato, vuelve aquí e inténtalo de nuevo.',
Forgot_password: 'Restablecer mi contraseña', Forgot_password: 'Restablecer mi contraseña',
Forgot_Password: 'Restabler mi Contraseña', Forgot_Password: 'Restabler mi Contraseña',
@ -207,7 +205,6 @@ export default {
is_not_a_valid_RocketChat_instance: 'no es una instancia válida Rocket.Chat', is_not_a_valid_RocketChat_instance: 'no es una instancia válida Rocket.Chat',
is_typing: 'escribiendo', is_typing: 'escribiendo',
Invalid_server_version: 'El servidor que intentas conectar está usando una versión que ya no es soportada por la aplicación : {{currentVersion}}. Requerimos una versión {{minVersion}}.', Invalid_server_version: 'El servidor que intentas conectar está usando una versión que ya no es soportada por la aplicación : {{currentVersion}}. Requerimos una versión {{minVersion}}.',
Join_the_community: 'Conectar con la comunidad',
Join: 'Conectar', Join: 'Conectar',
Just_invited_people_can_access_this_channel: 'Sólo gente invitada puede acceder a este canal.', Just_invited_people_can_access_this_channel: 'Sólo gente invitada puede acceder a este canal.',
Language: 'Idioma', Language: 'Idioma',
@ -270,6 +267,7 @@ export default {
Notification_Preferences: 'Configuración de notificaciones', Notification_Preferences: 'Configuración de notificaciones',
Offline: 'Sin conexión', Offline: 'Sin conexión',
Oops: 'Oops!', Oops: 'Oops!',
Onboarding_title: 'Bienvenido a Rocket.Chat',
Online: 'Conectado', Online: 'Conectado',
Only_authorized_users_can_write_new_messages: 'Sólo pueden escribir mensajes usuarios autorizados', Only_authorized_users_can_write_new_messages: 'Sólo pueden escribir mensajes usuarios autorizados',
Open_emoji_selector: 'Abrir selector de emojis', Open_emoji_selector: 'Abrir selector de emojis',
@ -427,7 +425,6 @@ export default {
Voice_call: 'Llamada de voz', Voice_call: 'Llamada de voz',
Websocket_disabled: 'Websocket está deshabilitado para este servidor.\n{{contact}}', Websocket_disabled: 'Websocket está deshabilitado para este servidor.\n{{contact}}',
Welcome: 'Bienvenido', Welcome: 'Bienvenido',
Welcome_to_RocketChat: 'Bienvenido a Rocket.Chat',
Whats_your_2fa: '¿Cuál es tu código 2FA?', Whats_your_2fa: '¿Cuál es tu código 2FA?',
Without_Servers: 'Sin servidores', Without_Servers: 'Sin servidores',
Yes_action_it: 'Sí, {{action}}!', Yes_action_it: 'Sí, {{action}}!',

View File

@ -122,7 +122,6 @@ export default {
Collaborative: 'Collaborative', Collaborative: 'Collaborative',
Confirm: 'Confirmer', Confirm: 'Confirmer',
Connect: 'Se connecter', Connect: 'Se connecter',
Connect_to_a_server: 'Se connecter à un serveur',
Connected: 'Connecté', Connected: 'Connecté',
Connecting: 'Connexion ...', Connecting: 'Connexion ...',
Continue_with: 'Continuer avec', Continue_with: 'Continuer avec',
@ -159,7 +158,6 @@ export default {
File_name: 'Nom de fichier', File_name: 'Nom de fichier',
Finish_recording: 'Terminer l\'enregistrement', Finish_recording: 'Terminer l\'enregistrement',
For_your_security_you_must_enter_your_current_password_to_continue: 'Pour votre sécurité, vous devez entrer votre mot de passe actuel pour continuer.', For_your_security_you_must_enter_your_current_password_to_continue: 'Pour votre sécurité, vous devez entrer votre mot de passe actuel pour continuer.',
Forgot_my_password: 'J\'ai oublié mon mot de passe',
Forgot_password_If_this_email_is_registered: 'Si cet e-mail est enregistré, nous vous enverrons des instructions pour réinitialiser votre mot de passe. Si vous ne recevez pas d\'e-mail sous peu, veuillez revenir et réessayer.', Forgot_password_If_this_email_is_registered: 'Si cet e-mail est enregistré, nous vous enverrons des instructions pour réinitialiser votre mot de passe. Si vous ne recevez pas d\'e-mail sous peu, veuillez revenir et réessayer.',
Forgot_password: 'Mot de passe oublié', Forgot_password: 'Mot de passe oublié',
Forgot_Password: 'Mot de passe oublié', Forgot_Password: 'Mot de passe oublié',
@ -173,7 +171,6 @@ export default {
is_not_a_valid_RocketChat_instance: 'n\'est pas une instance valide de Rocket.Chat', is_not_a_valid_RocketChat_instance: 'n\'est pas une instance valide de Rocket.Chat',
is_typing: 'est en train d\'écrire', is_typing: 'est en train d\'écrire',
Invalid_server_version: 'Le serveur que vous essayez de connecter utilise une version qui n\'est plus prise en charge par l\'application: {{currentVersion}}.\n\nNous exigeons la version {{minVersion}}', Invalid_server_version: 'Le serveur que vous essayez de connecter utilise une version qui n\'est plus prise en charge par l\'application: {{currentVersion}}.\n\nNous exigeons la version {{minVersion}}',
Join_the_community: 'Rejoindre la communauté',
Join: 'Rejoindre', Join: 'Rejoindre',
Just_invited_people_can_access_this_channel: 'Seuls les invités peuvent accéder à ce canal', Just_invited_people_can_access_this_channel: 'Seuls les invités peuvent accéder à ce canal',
Language: 'Langue', Language: 'Langue',
@ -223,6 +220,7 @@ export default {
Notify_all_in_this_room: 'Notifier tous dans cette salle', Notify_all_in_this_room: 'Notifier tous dans cette salle',
Offline: 'Hors ligne', Offline: 'Hors ligne',
Oops: 'Oops!', Oops: 'Oops!',
Onboarding_title: 'Bienvenue sur Rocket.Chat',
Online: 'En ligne', Online: 'En ligne',
Only_authorized_users_can_write_new_messages: 'Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages.', Only_authorized_users_can_write_new_messages: 'Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages.',
Open_emoji_selector: 'Ouvrir sélecteur emoji', Open_emoji_selector: 'Ouvrir sélecteur emoji',
@ -338,7 +336,6 @@ export default {
Video_call: 'Appel vidéo', Video_call: 'Appel vidéo',
Voice_call: 'Appel vocal', Voice_call: 'Appel vocal',
Welcome: 'Bienvenue', Welcome: 'Bienvenue',
Welcome_to_RocketChat: 'Bienvenue sur Rocket.Chat',
Whats_your_2fa: 'Quel est votre code 2FA?', Whats_your_2fa: 'Quel est votre code 2FA?',
Yes_action_it: 'Oui, {{action}} le!', Yes_action_it: 'Oui, {{action}} le!',
Yesterday: 'Hier', Yesterday: 'Hier',

View File

@ -136,7 +136,6 @@ export default {
Collaborative: 'Collaborativo', Collaborative: 'Collaborativo',
Confirm: 'Conferma', Confirm: 'Conferma',
Connect: 'Connetti', Connect: 'Connetti',
Connect_to_a_server: 'Connetti ad un server',
Connected: 'Connesso', Connected: 'Connesso',
connecting_server: 'connessione al server', connecting_server: 'connessione al server',
Connecting: 'Connessione...', Connecting: 'Connessione...',
@ -192,7 +191,6 @@ export default {
Finish_recording: 'Termina registrazione', Finish_recording: 'Termina registrazione',
Following_thread: 'Thread seguito', Following_thread: 'Thread seguito',
For_your_security_you_must_enter_your_current_password_to_continue: 'Per garantire la sicurezza del tuo account, inserisci la password per continuare.', For_your_security_you_must_enter_your_current_password_to_continue: 'Per garantire la sicurezza del tuo account, inserisci la password per continuare.',
Forgot_my_password: 'Ho dimenticato la password',
Forgot_password_If_this_email_is_registered: 'Se questa e-mail è registrata, manderemo istruzioni su come resettare la tua password. Se non ricevi nulla, torna qui e riprova di nuovo.', Forgot_password_If_this_email_is_registered: 'Se questa e-mail è registrata, manderemo istruzioni su come resettare la tua password. Se non ricevi nulla, torna qui e riprova di nuovo.',
Forgot_password: 'Password dimenticata', Forgot_password: 'Password dimenticata',
Forgot_Password: 'Password dimenticata', Forgot_Password: 'Password dimenticata',
@ -215,7 +213,6 @@ export default {
Invalid_server_version: 'Il server a cui stai cercando di connetterti sta utilizzando una versione non più supportata dall\'app: {{currentVersion}}.\n\nVersione minima richiesta: {{minVersion}}', Invalid_server_version: 'Il server a cui stai cercando di connetterti sta utilizzando una versione non più supportata dall\'app: {{currentVersion}}.\n\nVersione minima richiesta: {{minVersion}}',
Invite_Link: 'Link di invito', Invite_Link: 'Link di invito',
Invite_users: 'Invita utenti', Invite_users: 'Invita utenti',
Join_the_community: 'Unisciti alla community',
Join: 'Entra', Join: 'Entra',
Just_invited_people_can_access_this_channel: 'Solo le persone invitate possono accedere a questo canale', Just_invited_people_can_access_this_channel: 'Solo le persone invitate possono accedere a questo canale',
Language: 'Lingua', Language: 'Lingua',
@ -281,6 +278,7 @@ export default {
Notification_Preferences: 'Impostazioni notifiche', Notification_Preferences: 'Impostazioni notifiche',
Offline: 'Offline', Offline: 'Offline',
Oops: 'Oops!', Oops: 'Oops!',
Onboarding_title: 'Benvenuto in Rocket.Chat',
Online: 'Online', Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Solo gli utenti autorizzati possono scrivere nuovi messaggi', Only_authorized_users_can_write_new_messages: 'Solo gli utenti autorizzati possono scrivere nuovi messaggi',
Open_emoji_selector: 'Apri selettore emoji', Open_emoji_selector: 'Apri selettore emoji',
@ -441,7 +439,6 @@ export default {
Voice_call: 'Chiamata vocale', Voice_call: 'Chiamata vocale',
Websocket_disabled: 'Websocket è disabilitato per questo server.\n{{contact}}', Websocket_disabled: 'Websocket è disabilitato per questo server.\n{{contact}}',
Welcome: 'Benvenuto', Welcome: 'Benvenuto',
Welcome_to_RocketChat: 'Benvenuto in Rocket.Chat',
Whats_your_2fa: 'Qual\'è il tuo codice 2FA?', Whats_your_2fa: 'Qual\'è il tuo codice 2FA?',
Without_Servers: 'Senza server', Without_Servers: 'Senza server',
Write_External_Permission_Message: 'Rocket.Chat ha bisogno dell\'accesso alla galleria per salvare le immagini.', Write_External_Permission_Message: 'Rocket.Chat ha bisogno dell\'accesso alla galleria per salvare le immagini.',

View File

@ -153,7 +153,6 @@ export default {
Collaborative: 'コラボ', Collaborative: 'コラボ',
Confirm: '承認', Confirm: '承認',
Connect: '接続', Connect: '接続',
Connect_to_a_server: 'サーバーに接続',
Connected: '接続しました', Connected: '接続しました',
connecting_server: 'サーバーに接続中', connecting_server: 'サーバーに接続中',
Connecting: '接続中...', Connecting: '接続中...',
@ -216,7 +215,6 @@ export default {
Following_thread: 'スレッド更新時に通知', Following_thread: 'スレッド更新時に通知',
For_your_security_you_must_enter_your_current_password_to_continue: For_your_security_you_must_enter_your_current_password_to_continue:
'セキュリティのため、続けるには現在のパスワードを入力してください。', 'セキュリティのため、続けるには現在のパスワードを入力してください。',
Forgot_my_password: 'パスワードを忘れた',
Forgot_password_If_this_email_is_registered: Forgot_password_If_this_email_is_registered:
'送信したメールアドレスが登録されていれば、パスワードのリセット方法を送信しました。メールアドレスがすぐに来ない場合はやり直してください。', '送信したメールアドレスが登録されていれば、パスワードのリセット方法を送信しました。メールアドレスがすぐに来ない場合はやり直してください。',
Forgot_password: 'パスワードを忘れた', Forgot_password: 'パスワードを忘れた',
@ -242,7 +240,6 @@ export default {
'接続しようとしているサーバーのバージョン({{currentVersion}})はサポートされていません。\n\nサポートする最低バージョンは {{minVersion}} です', '接続しようとしているサーバーのバージョン({{currentVersion}})はサポートされていません。\n\nサポートする最低バージョンは {{minVersion}} です',
Invite_Link: '招待リンク', Invite_Link: '招待リンク',
Invite_users: 'ユーザーを招待', Invite_users: 'ユーザーを招待',
Join_the_community: 'コミュニティに参加',
Join: '参加', Join: '参加',
Just_invited_people_can_access_this_channel: Just_invited_people_can_access_this_channel:
'招待されたユーザーだけがこのチャンネルに参加できます', '招待されたユーザーだけがこのチャンネルに参加できます',
@ -311,6 +308,7 @@ export default {
Notification_Preferences: '通知設定', Notification_Preferences: '通知設定',
Offline: 'オフライン', Offline: 'オフライン',
Oops: 'おっと!', Oops: 'おっと!',
Onboarding_title: 'Rocket.Chatへようこそ',
Online: 'オンライン', Online: 'オンライン',
Only_authorized_users_can_write_new_messages: Only_authorized_users_can_write_new_messages:
'承認されたユーザーだけが新しいメッセージを書き込めます', '承認されたユーザーだけが新しいメッセージを書き込めます',
@ -487,7 +485,6 @@ export default {
Voice_call: '音声通話', Voice_call: '音声通話',
Websocket_disabled: 'Websocketはこのサーバーでは無効化されています。\n{{contact}}', Websocket_disabled: 'Websocketはこのサーバーでは無効化されています。\n{{contact}}',
Welcome: 'ようこそ', Welcome: 'ようこそ',
Welcome_to_RocketChat: 'Rocket.Chatへようこそ',
Whats_your_2fa: '2段階認証のコードを入力してください', Whats_your_2fa: '2段階認証のコードを入力してください',
Without_Servers: 'サーバーを除く', Without_Servers: 'サーバーを除く',
Write_External_Permission_Message: Write_External_Permission_Message:

View File

@ -136,7 +136,6 @@ export default {
Collaborative: 'Samenwerkend', Collaborative: 'Samenwerkend',
Confirm: 'Bevestig', Confirm: 'Bevestig',
Connect: 'Verbind', Connect: 'Verbind',
Connect_to_a_server: 'Verbind met een server',
Connected: 'Verbonden', Connected: 'Verbonden',
connecting_server: 'Verbonden met een server', connecting_server: 'Verbonden met een server',
Connecting: 'Aan het verbinden...', Connecting: 'Aan het verbinden...',
@ -192,7 +191,6 @@ export default {
Finish_recording: 'Beëindig opname', Finish_recording: 'Beëindig opname',
Following_thread: 'Volg thread', Following_thread: 'Volg thread',
For_your_security_you_must_enter_your_current_password_to_continue: 'Voor je veiligheid moet je je wachtwoord invullen om door te gaan', For_your_security_you_must_enter_your_current_password_to_continue: 'Voor je veiligheid moet je je wachtwoord invullen om door te gaan',
Forgot_my_password: 'Wachtwoord vergeten',
Forgot_password_If_this_email_is_registered: 'Als dit email adres bij ons bekend is, sturen we je instructies op om je wachtwoord te resetten. Als je geen email krijgt, probeer het dan nogmaals.', Forgot_password_If_this_email_is_registered: 'Als dit email adres bij ons bekend is, sturen we je instructies op om je wachtwoord te resetten. Als je geen email krijgt, probeer het dan nogmaals.',
Forgot_password: 'Wachtwoord vergeten', Forgot_password: 'Wachtwoord vergeten',
Forgot_Password: 'Wachtwoord Vergeten', Forgot_Password: 'Wachtwoord Vergeten',
@ -215,7 +213,6 @@ export default {
Invalid_server_version: 'De server die je probeert te bereiken gebruikt een versie die niet meer door de app ondersteunt wordt: {{currentVersion}}.\n\nMinimale versienummer {{minVersion}}', Invalid_server_version: 'De server die je probeert te bereiken gebruikt een versie die niet meer door de app ondersteunt wordt: {{currentVersion}}.\n\nMinimale versienummer {{minVersion}}',
Invite_Link: 'Uitnodigingslink', Invite_Link: 'Uitnodigingslink',
Invite_users: 'Nodig gebruikers uit', Invite_users: 'Nodig gebruikers uit',
Join_the_community: 'Word lid van de community',
Join: 'Word lid', Join: 'Word lid',
Just_invited_people_can_access_this_channel: 'Alleen genodigden kunnen bij dit kanaal', Just_invited_people_can_access_this_channel: 'Alleen genodigden kunnen bij dit kanaal',
Language: 'Taal', Language: 'Taal',
@ -281,6 +278,7 @@ export default {
Notification_Preferences: 'Notificatievoorkeuren', Notification_Preferences: 'Notificatievoorkeuren',
Offline: 'Offline', Offline: 'Offline',
Oops: 'Oeps!', Oops: 'Oeps!',
Onboarding_title: 'Welkom bij Rocket.Chat',
Online: 'Online', Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Alleen gebruikers met toestemming mogen nieuwe berichten maken', Only_authorized_users_can_write_new_messages: 'Alleen gebruikers met toestemming mogen nieuwe berichten maken',
Open_emoji_selector: 'Open de emoji selector', Open_emoji_selector: 'Open de emoji selector',
@ -448,7 +446,6 @@ export default {
Voice_call: 'Audiogesprek', Voice_call: 'Audiogesprek',
Websocket_disabled: 'Websocket staat uit voor deze server.\n{{contact}}', Websocket_disabled: 'Websocket staat uit voor deze server.\n{{contact}}',
Welcome: 'Welkom', Welcome: 'Welkom',
Welcome_to_RocketChat: 'Welkom bij Rocket.Chat',
Whats_your_2fa: 'Wat is je 2FA code?', Whats_your_2fa: 'Wat is je 2FA code?',
Without_Servers: 'Zonder Servers', Without_Servers: 'Zonder Servers',
Write_External_Permission_Message: 'Rocket Chat moet bij je galerij kunnen om afbeeldingen op te slaan.', Write_External_Permission_Message: 'Rocket Chat moet bij je galerij kunnen om afbeeldingen op te slaan.',

View File

@ -142,7 +142,6 @@ export default {
Collaborative: 'Colaborativo', Collaborative: 'Colaborativo',
Confirm: 'Confirmar', Confirm: 'Confirmar',
Connect: 'Conectar', Connect: 'Conectar',
Connect_to_a_server: 'Conectar a um servidor',
Connected: 'Conectado', Connected: 'Conectado',
connecting_server: 'conectando no servidor', connecting_server: 'conectando no servidor',
Connecting: 'Conectando...', Connecting: 'Conectando...',
@ -171,6 +170,7 @@ export default {
Disable_notifications: 'Desabilitar notificações', Disable_notifications: 'Desabilitar notificações',
Discussions: 'Discussões', Discussions: 'Discussões',
Dont_Have_An_Account: 'Não tem uma conta?', Dont_Have_An_Account: 'Não tem uma conta?',
Do_you_have_an_account: 'Você tem uma conta?',
Do_you_really_want_to_key_this_room_question_mark: 'Você quer realmente {{key}} esta sala?', Do_you_really_want_to_key_this_room_question_mark: 'Você quer realmente {{key}} esta sala?',
edit: 'editar', edit: 'editar',
edited: 'editado', edited: 'editado',
@ -190,9 +190,8 @@ export default {
Finish_recording: 'Encerrar gravação', Finish_recording: 'Encerrar gravação',
Following_thread: 'Começou a seguir tópico', Following_thread: 'Começou a seguir tópico',
For_your_security_you_must_enter_your_current_password_to_continue: 'Para sua segurança, você precisa digitar sua senha', For_your_security_you_must_enter_your_current_password_to_continue: 'Para sua segurança, você precisa digitar sua senha',
Forgot_my_password: 'Esqueci minha senha',
Forgot_password_If_this_email_is_registered: 'Se este e-mail estiver cadastrado, enviaremos instruções sobre como redefinir sua senha. Se você não receber um e-mail em breve, volte e tente novamente.', Forgot_password_If_this_email_is_registered: 'Se este e-mail estiver cadastrado, enviaremos instruções sobre como redefinir sua senha. Se você não receber um e-mail em breve, volte e tente novamente.',
Forgot_password: 'Esqueci minha senha', Forgot_password: 'Esqueceu sua senha?',
Forgot_Password: 'Esqueci minha senha', Forgot_Password: 'Esqueci minha senha',
Full_table: 'Clique para ver a tabela completa', Full_table: 'Clique para ver a tabela completa',
Generate_New_Link: 'Gerar novo convite', Generate_New_Link: 'Gerar novo convite',
@ -224,8 +223,9 @@ export default {
Invalid_server_version: 'O servidor que você está conectando não é suportado mais por esta versão do aplicativo: {{currentVersion}}.\n\nEsta versão do aplicativo requer a versão {{minVersion}} do servidor para funcionar corretamente.', Invalid_server_version: 'O servidor que você está conectando não é suportado mais por esta versão do aplicativo: {{currentVersion}}.\n\nEsta versão do aplicativo requer a versão {{minVersion}} do servidor para funcionar corretamente.',
Invite_Link: 'Link de Convite', Invite_Link: 'Link de Convite',
Invite_users: 'Convidar usuários', Invite_users: 'Convidar usuários',
Join_the_community: 'Junte-se à comunidade',
Join: 'Entrar', Join: 'Entrar',
Join_our_open_workspace: 'Entra na nossa workspace pública',
Join_your_workspace: 'Entre na sua workspace',
Just_invited_people_can_access_this_channel: 'Apenas as pessoas convidadas podem acessar este canal', Just_invited_people_can_access_this_channel: 'Apenas as pessoas convidadas podem acessar este canal',
Language: 'Idioma', Language: 'Idioma',
last_message: 'última mensagem', last_message: 'última mensagem',
@ -282,10 +282,19 @@ export default {
Not_RC_Server: 'Este não é um servidor Rocket.Chat.\n{{contact}}', Not_RC_Server: 'Este não é um servidor Rocket.Chat.\n{{contact}}',
Offline: 'Offline', Offline: 'Offline',
Oops: 'Ops!', Oops: 'Ops!',
Onboarding_description: 'Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.',
Onboarding_join_workspace: 'Entre numa workspace',
Onboarding_subtitle: 'Além da colaboração em equipe',
Onboarding_title: 'Bem vindo ao Rocket.Chat',
Onboarding_join_open_description: 'Entre na nossa workspace pública para conversar com o time da Rocket.Chat e nossa comunidade.',
Onboarding_agree_terms: 'Ao continuar, você aceita nossos ',
Onboarding_less_options: 'Menos opções',
Onboarding_more_options: 'Mais opções',
Online: 'Online', Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Somente usuários autorizados podem escrever novas mensagens', Only_authorized_users_can_write_new_messages: 'Somente usuários autorizados podem escrever novas mensagens',
Open_emoji_selector: 'Abrir seletor de emoji', Open_emoji_selector: 'Abrir seletor de emoji',
Open_Source_Communication: 'Comunicação Open Source', Open_Source_Communication: 'Comunicação Open Source',
OR: 'OU',
Overwrites_the_server_configuration_and_use_room_config: 'Substituir a configuração do servidor e usar a configuração da sala', Overwrites_the_server_configuration_and_use_room_config: 'Substituir a configuração do servidor e usar a configuração da sala',
Password: 'Senha', Password: 'Senha',
Permalink_copied_to_clipboard: 'Link-permanente copiado para a área de transferência!', Permalink_copied_to_clipboard: 'Link-permanente copiado para a área de transferência!',
@ -432,9 +441,9 @@ export default {
Voice_call: 'Chamada de voz', Voice_call: 'Chamada de voz',
Websocket_disabled: 'Websocket está desativado para esse servidor.\n{{contact}}', Websocket_disabled: 'Websocket está desativado para esse servidor.\n{{contact}}',
Welcome: 'Bem vindo', Welcome: 'Bem vindo',
Welcome_to_RocketChat: 'Bem vindo ao Rocket.Chat',
Whats_your_2fa: 'Qual seu código de autenticação?', Whats_your_2fa: 'Qual seu código de autenticação?',
Without_Servers: 'Sem Servidores', Without_Servers: 'Sem Servidores',
Workspaces: 'Workspaces',
Yes_action_it: 'Sim, {{action}}!', Yes_action_it: 'Sim, {{action}}!',
Yesterday: 'Ontem', Yesterday: 'Ontem',
You_are_in_preview_mode: 'Está é uma prévia do canal', You_are_in_preview_mode: 'Está é uma prévia do canal',
@ -449,6 +458,7 @@ export default {
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Seu link de convite irá vencer em {{date}} ou depois de {{usesLeft}} usos.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Seu link de convite irá vencer em {{date}} ou depois de {{usesLeft}} usos.',
Your_invite_link_will_expire_on__date__: 'Seu link de convite irá vencer em {{date}}.', Your_invite_link_will_expire_on__date__: 'Seu link de convite irá vencer em {{date}}.',
Your_invite_link_will_never_expire: 'Seu link de convite nunca irá vencer.', Your_invite_link_will_never_expire: 'Seu link de convite nunca irá vencer.',
Your_workspace: 'Sua workspace',
You_will_not_be_able_to_recover_this_message: 'Você não será capaz de recuperar essa mensagem!', You_will_not_be_able_to_recover_this_message: 'Você não será capaz de recuperar essa mensagem!',
Write_External_Permission_Message: 'Rocket Chat precisa de acesso à sua galeria para salvar imagens', Write_External_Permission_Message: 'Rocket Chat precisa de acesso à sua galeria para salvar imagens',
Write_External_Permission: 'Acesso à Galeria', Write_External_Permission: 'Acesso à Galeria',

View File

@ -122,7 +122,6 @@ export default {
Collaborative: 'Colaborativa', Collaborative: 'Colaborativa',
Confirm: 'Confirmar', Confirm: 'Confirmar',
Connect: 'Ligar', Connect: 'Ligar',
Connect_to_a_server: 'Ligue-se a um servidor',
Connected: 'Ligado', Connected: 'Ligado',
Connecting: 'A ligar...', Connecting: 'A ligar...',
Continue_with: 'Continuar com', Continue_with: 'Continuar com',
@ -159,7 +158,6 @@ export default {
File_name: 'Nome do ficheiro', File_name: 'Nome do ficheiro',
Finish_recording: 'Terminar a gravação', Finish_recording: 'Terminar a gravação',
For_your_security_you_must_enter_your_current_password_to_continue: 'Para sua segurança, você deve escrever a sua palavra-passe actual para continuar', For_your_security_you_must_enter_your_current_password_to_continue: 'Para sua segurança, você deve escrever a sua palavra-passe actual para continuar',
Forgot_my_password: 'Esqueci minha palavra-passe',
Forgot_password_If_this_email_is_registered: 'Se este e-mail estiver registado, enviaremos instruções sobre como repor a sua palavra-passe. Se você não receber um e-mail em breve, volte e tente novamente.', Forgot_password_If_this_email_is_registered: 'Se este e-mail estiver registado, enviaremos instruções sobre como repor a sua palavra-passe. Se você não receber um e-mail em breve, volte e tente novamente.',
Forgot_password: 'Esquecer palavra-passe', Forgot_password: 'Esquecer palavra-passe',
Forgot_Password: 'Esquecer Palavra-passe', Forgot_Password: 'Esquecer Palavra-passe',
@ -174,7 +172,6 @@ export default {
is_not_a_valid_RocketChat_instance: 'is not a valid Rocket.Chat instance', is_not_a_valid_RocketChat_instance: 'is not a valid Rocket.Chat instance',
is_typing: 'está a escrever', is_typing: 'está a escrever',
Invalid_server_version: 'O servidor ao qual esta tentando ligar-se, utiliza uma versão que não é suporta pela aplicação: {{currentVersion}}.\n\nA versão mínima requerida é {{minVersion}}', Invalid_server_version: 'O servidor ao qual esta tentando ligar-se, utiliza uma versão que não é suporta pela aplicação: {{currentVersion}}.\n\nA versão mínima requerida é {{minVersion}}',
Join_the_community: 'Junte-se à comunidade',
Join: 'Entrar', Join: 'Entrar',
Just_invited_people_can_access_this_channel: 'Apenas utilizadores convidados podem aceder a este canal', Just_invited_people_can_access_this_channel: 'Apenas utilizadores convidados podem aceder a este canal',
Language: 'Idioma', Language: 'Idioma',
@ -224,6 +221,7 @@ export default {
Notify_all_in_this_room: 'Notifica todos os utilizadores nesta sala', Notify_all_in_this_room: 'Notifica todos os utilizadores nesta sala',
Offline: 'Desligado', Offline: 'Desligado',
Oops: 'Oops!', Oops: 'Oops!',
Onboarding_title: 'Bem vindo(a) ao Rocket.Chat',
Online: 'Ligado', Online: 'Ligado',
Only_authorized_users_can_write_new_messages: 'Apenas utilizadores autorizados podem escrever novas mensagens', Only_authorized_users_can_write_new_messages: 'Apenas utilizadores autorizados podem escrever novas mensagens',
Open_emoji_selector: 'Abra o selector de emoticons', Open_emoji_selector: 'Abra o selector de emoticons',
@ -341,7 +339,6 @@ export default {
Video_call: 'Video chamada', Video_call: 'Video chamada',
Voice_call: 'Chamada de voz', Voice_call: 'Chamada de voz',
Welcome: 'Bem vindo(a)', Welcome: 'Bem vindo(a)',
Welcome_to_RocketChat: 'Bem vindo(a) ao Rocket.Chat',
Whats_your_2fa: 'Qual é o seu código 2FA?', Whats_your_2fa: 'Qual é o seu código 2FA?',
Yes_action_it: 'Sim, {{action}}!', Yes_action_it: 'Sim, {{action}}!',
Yesterday: 'Ontem', Yesterday: 'Ontem',

View File

@ -131,7 +131,6 @@ export default {
Collaborative: 'Совместный', Collaborative: 'Совместный',
Confirm: 'Подтверждение', Confirm: 'Подтверждение',
Connect: 'Соединение', Connect: 'Соединение',
Connect_to_a_server: 'Подключиться к серверу',
Connected: 'Подключено', Connected: 'Подключено',
connecting_server: 'подключение к серверу', connecting_server: 'подключение к серверу',
Connecting: 'Соединение...', Connecting: 'Соединение...',
@ -183,7 +182,6 @@ export default {
Finish_recording: 'Завершить запись', Finish_recording: 'Завершить запись',
Following_thread: 'Следить за темой', Following_thread: 'Следить за темой',
For_your_security_you_must_enter_your_current_password_to_continue: 'В целях вашей безопасности вы должны ввести свой текущий пароль для продолжения', For_your_security_you_must_enter_your_current_password_to_continue: 'В целях вашей безопасности вы должны ввести свой текущий пароль для продолжения',
Forgot_my_password: 'Забыл свой пароль',
Forgot_password_If_this_email_is_registered: 'Если эта электронная почта зарегистрирована, мы отправим инструкции о том, как сбросить пароль. Если вы не получите письмо в ближайшее время, вернитесь и повторите попытку.', Forgot_password_If_this_email_is_registered: 'Если эта электронная почта зарегистрирована, мы отправим инструкции о том, как сбросить пароль. Если вы не получите письмо в ближайшее время, вернитесь и повторите попытку.',
Forgot_password: 'Забыли пароль', Forgot_password: 'Забыли пароль',
Forgot_Password: 'Забыли Пароль', Forgot_Password: 'Забыли Пароль',
@ -202,7 +200,6 @@ export default {
is_not_a_valid_RocketChat_instance: 'не является действительным сервером Rocket.Chat', is_not_a_valid_RocketChat_instance: 'не является действительным сервером Rocket.Chat',
is_typing: 'печатает', is_typing: 'печатает',
Invalid_server_version: 'Сервер, к которому вы пытаетесь подключиться, использует версию, которая больше не поддерживается приложением: {{currentVersion}}.\n\nНам нужна версия {{minVersion}}', Invalid_server_version: 'Сервер, к которому вы пытаетесь подключиться, использует версию, которая больше не поддерживается приложением: {{currentVersion}}.\n\nНам нужна версия {{minVersion}}',
Join_the_community: 'Присоединиться к сообществу',
Join: 'Присоединиться', Join: 'Присоединиться',
Just_invited_people_can_access_this_channel: 'Только приглашенные люди могут получить доступ к этому каналу', Just_invited_people_can_access_this_channel: 'Только приглашенные люди могут получить доступ к этому каналу',
Language: 'Язык', Language: 'Язык',
@ -264,6 +261,7 @@ export default {
Notification_Preferences: 'Настройки уведомлений', Notification_Preferences: 'Настройки уведомлений',
Offline: 'Офлайн', Offline: 'Офлайн',
Oops: 'Упс!', Oops: 'Упс!',
Onboarding_title: 'Добро пожаловать в Rocket.Chat',
Online: 'Онлайн', Online: 'Онлайн',
Only_authorized_users_can_write_new_messages: 'Только авторизованные пользователи могут писать новые сообщения', Only_authorized_users_can_write_new_messages: 'Только авторизованные пользователи могут писать новые сообщения',
Open_emoji_selector: 'Открыть селектор emoji', Open_emoji_selector: 'Открыть селектор emoji',
@ -420,7 +418,6 @@ export default {
Voice_call: 'Голосовой вызов', Voice_call: 'Голосовой вызов',
Websocket_disabled: 'Websocket отключен для этого сервера.\n{{contact}}', Websocket_disabled: 'Websocket отключен для этого сервера.\n{{contact}}',
Welcome: 'Добро пожаловать,', Welcome: 'Добро пожаловать,',
Welcome_to_RocketChat: 'Добро пожаловать в Rocket.Chat',
Whats_your_2fa: 'Какой у вас код 2FA?', Whats_your_2fa: 'Какой у вас код 2FA?',
Without_Servers: 'Без серверов', Without_Servers: 'Без серверов',
Yes_action_it: 'Да, {{action}} это!', Yes_action_it: 'Да, {{action}} это!',

View File

@ -122,7 +122,6 @@ export default {
Collaborative: '协作', Collaborative: '协作',
Confirm: '确认', Confirm: '确认',
Connect: '连接', Connect: '连接',
Connect_to_a_server: '连接到服务器',
Connected: '已连接', Connected: '已连接',
Connecting: '连接中', Connecting: '连接中',
Continue_with: '继续采用', Continue_with: '继续采用',
@ -156,7 +155,6 @@ export default {
Files: '文件', Files: '文件',
Finish_recording: '完成录制', Finish_recording: '完成录制',
For_your_security_you_must_enter_your_current_password_to_continue: '为了安全起见,您必须输入当前密码才能继续', For_your_security_you_must_enter_your_current_password_to_continue: '为了安全起见,您必须输入当前密码才能继续',
Forgot_my_password: '忘记密码',
Forgot_password_If_this_email_is_registered: '如果这封邮件已注册,我们将发送如何重置密码的说明。如果您没有立即收到电子邮件,请回来再试一次。', Forgot_password_If_this_email_is_registered: '如果这封邮件已注册,我们将发送如何重置密码的说明。如果您没有立即收到电子邮件,请回来再试一次。',
Forgot_password: '忘记密码', Forgot_password: '忘记密码',
Forgot_Password: '忘记密码', Forgot_Password: '忘记密码',
@ -170,7 +168,6 @@ export default {
is_a_valid_RocketChat_instance: '是一个有效的 Rocket.Chat 实例', is_a_valid_RocketChat_instance: '是一个有效的 Rocket.Chat 实例',
is_not_a_valid_RocketChat_instance: '不是有效的 Rocket.Chat 实例', is_not_a_valid_RocketChat_instance: '不是有效的 Rocket.Chat 实例',
is_typing: '正在输入', is_typing: '正在输入',
Join_the_community: '加入社区',
Join: '加入', Join: '加入',
Just_invited_people_can_access_this_channel: '刚被邀请的人可以进入这个频道', Just_invited_people_can_access_this_channel: '刚被邀请的人可以进入这个频道',
Language: '语言', Language: '语言',
@ -220,6 +217,7 @@ export default {
Notify_all_in_this_room: '通知这个房间的所有人', Notify_all_in_this_room: '通知这个房间的所有人',
Offline: '离线', Offline: '离线',
Oops: '哎呀!', Oops: '哎呀!',
Onboarding_title: '欢迎来到 Rocket.Chat',
Online: '在线', Online: '在线',
Only_authorized_users_can_write_new_messages: '只有经过授权的用户才能写新消息', Only_authorized_users_can_write_new_messages: '只有经过授权的用户才能写新消息',
Open_emoji_selector: '打开emoji选择器', Open_emoji_selector: '打开emoji选择器',
@ -333,7 +331,6 @@ export default {
Video_call: '视频电话', Video_call: '视频电话',
Voice_call: '语音电话', Voice_call: '语音电话',
Welcome: '欢迎', Welcome: '欢迎',
Welcome_to_RocketChat: '欢迎来到 Rocket.Chat',
Yes_action_it: '是的,{{action}}它!', Yes_action_it: '是的,{{action}}它!',
Yesterday: '昨天', Yesterday: '昨天',
You_are_in_preview_mode: '您处于预览模式', You_are_in_preview_mode: '您处于预览模式',

View File

@ -74,8 +74,8 @@ const OutsideStack = createStackNavigator({
NewServerView: { NewServerView: {
getScreen: () => require('./views/NewServerView').default getScreen: () => require('./views/NewServerView').default
}, },
LoginSignupView: { WorkspaceView: {
getScreen: () => require('./views/LoginSignupView').default getScreen: () => require('./views/WorkspaceView').default
}, },
LoginView: { LoginView: {
getScreen: () => require('./views/LoginView').default getScreen: () => require('./views/LoginView').default

View File

@ -91,7 +91,7 @@ export default async function() {
return; return;
} }
const data = result.settings || []; const data = result.settings || [];
const filteredSettings = this._prepareSettings(data.filter(item => item._id !== 'Assets_favicon_512')); const filteredSettings = this._prepareSettings(data);
const filteredSettingsIds = filteredSettings.map(s => s._id); const filteredSettingsIds = filteredSettings.map(s => s._id);
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings))); reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));

View File

@ -89,7 +89,7 @@ const handleOpen = function* handleOpen({ params }) {
if (!result.success) { if (!result.success) {
return; return;
} }
Navigation.navigate('OnboardingView', { previousServer: server }); Navigation.navigate('NewServerView', { previousServer: server });
yield delay(1000); yield delay(1000);
EventEmitter.emit('NewServer', { server: host }); EventEmitter.emit('NewServer', { server: host });

View File

@ -1,5 +1,5 @@
import { import {
put, take, takeLatest, fork, cancel, race, select put, take, takeLatest, fork, cancel, race
} from 'redux-saga/effects'; } from 'redux-saga/effects';
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import RNUserDefaults from 'rn-user-defaults'; import RNUserDefaults from 'rn-user-defaults';
@ -133,16 +133,9 @@ const handleServerRequest = function* handleServerRequest({ server, certificate
const serverInfo = yield getServerInfo({ server }); const serverInfo = yield getServerInfo({ server });
if (serverInfo) { if (serverInfo) {
const loginServicesLength = yield RocketChat.getLoginServices(server); yield RocketChat.getLoginServices(server);
yield RocketChat.getLoginSettings({ server }); yield RocketChat.getLoginSettings({ server });
Navigation.navigate('WorkspaceView');
const showFormLogin = yield select(state => state.settings.Accounts_ShowFormLogin);
if (!loginServicesLength && showFormLogin) {
Navigation.navigate('LoginView');
} else {
Navigation.navigate('LoginSignupView');
}
yield put(selectServerRequest(server, serverInfo.version, false)); yield put(selectServerRequest(server, serverInfo.version, false));
} }
} catch (e) { } catch (e) {

View File

@ -108,7 +108,7 @@ export const initTabletNav = (setState) => {
KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]); KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]);
setState({ inside: false, showModal: false }); setState({ inside: false, showModal: false });
} }
if (routeName === 'OnboardingView') { if (routeName === 'OnboardingView' || routeName === 'NewServerView') {
KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]); KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]);
setState({ inside: false, showModal: false }); setState({ inside: false, showModal: false });
} }

View File

@ -1,21 +1,18 @@
import React from 'react'; import React from 'react';
import { Text, ScrollView } from 'react-native'; import { Text } from 'react-native';
import { SafeAreaView } from 'react-navigation';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import Button from '../containers/Button'; import Button from '../containers/Button';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import isValidEmail from '../utils/isValidEmail'; import isValidEmail from '../utils/isValidEmail';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n'; import I18n from '../i18n';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import StatusBar from '../containers/StatusBar';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { themedHeader } from '../utils/navigation'; import { themedHeader } from '../utils/navigation';
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
class ForgotPasswordView extends React.Component { class ForgotPasswordView extends React.Component {
static navigationOptions = ({ navigation, screenProps }) => { static navigationOptions = ({ navigation, screenProps }) => {
@ -88,39 +85,31 @@ class ForgotPasswordView extends React.Component {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<KeyboardView <FormContainer theme={theme}>
style={{ backgroundColor: themes[theme].backgroundColor }} <FormContainerInner>
contentContainerStyle={sharedStyles.container} <Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Forgot_password')}</Text>
keyboardVerticalOffset={128} <TextInput
> autoFocus
<StatusBar theme={theme} /> placeholder={I18n.t('Email')}
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}> keyboardType='email-address'
<SafeAreaView style={sharedStyles.container} testID='forgot-password-view' forceInset={{ vertical: 'never' }}> returnKeyType='send'
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Forgot_password')}</Text> onChangeText={email => this.validate(email)}
<TextInput onSubmitEditing={this.resetPassword}
autoFocus testID='forgot-password-view-email'
placeholder={I18n.t('Email')} containerStyle={sharedStyles.inputLastChild}
keyboardType='email-address' theme={theme}
iconLeft='mail' />
returnKeyType='send' <Button
onChangeText={email => this.validate(email)} title={I18n.t('Reset_password')}
onSubmitEditing={this.resetPassword} type='primary'
testID='forgot-password-view-email' onPress={this.resetPassword}
containerStyle={sharedStyles.inputLastChild} testID='forgot-password-view-submit'
theme={theme} loading={isFetching}
/> disabled={invalidEmail}
<Button theme={theme}
title={I18n.t('Reset_password')} />
type='primary' </FormContainerInner>
onPress={this.resetPassword} </FormContainer>
testID='forgot-password-view-submit'
loading={isFetching}
disabled={invalidEmail}
theme={theme}
/>
</SafeAreaView>
</ScrollView>
</KeyboardView>
); );
} }
} }

View File

@ -1,50 +1,53 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Keyboard, Text, ScrollView, View, StyleSheet, Alert Text, View, StyleSheet, Keyboard, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal'; import equal from 'deep-equal';
import { analytics } from '../utils/log';
import KeyboardView from '../presentation/KeyboardView'; import { analytics } from '../utils/log';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps'; import Button from '../containers/Button';
import I18n from '../i18n'; import I18n from '../i18n';
import { loginRequest as loginRequestAction } from '../actions/login';
import { LegalButton } from '../containers/HeaderButton'; import { LegalButton } from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { animateNextTransition } from '../utils/layoutAnimation';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { themedHeader } from '../utils/navigation'; import { themedHeader } from '../utils/navigation';
import { isTablet } from '../utils/deviceInfo'; import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import TextInput from '../containers/TextInput';
import { animateNextTransition } from '../utils/layoutAnimation';
import { loginRequest as loginRequestAction } from '../actions/login';
import LoginServices from '../containers/LoginServices';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
bottomContainer: {
flexDirection: 'column',
alignItems: 'center',
marginTop: 10
},
registerDisabled: { registerDisabled: {
...sharedStyles.textRegular, ...sharedStyles.textRegular,
...sharedStyles.textAlignCenter, ...sharedStyles.textAlignCenter,
fontSize: 13 fontSize: 16
}, },
dontHaveAccount: { title: {
...sharedStyles.textBold,
fontSize: 22
},
inputContainer: {
marginVertical: 16
},
bottomContainer: {
flexDirection: 'column',
alignItems: 'center',
marginBottom: 32
},
bottomContainerText: {
...sharedStyles.textRegular, ...sharedStyles.textRegular,
fontSize: 13 fontSize: 13
}, },
createAccount: { bottomContainerTextBold: {
...sharedStyles.textSemibold, ...sharedStyles.textSemibold,
fontSize: 13 fontSize: 13
}, },
loginTitle: { loginButton: {
marginVertical: 0, marginTop: 16
marginTop: 15
} }
}); });
@ -52,29 +55,26 @@ class LoginView extends React.Component {
static navigationOptions = ({ navigation, screenProps }) => { static navigationOptions = ({ navigation, screenProps }) => {
const title = navigation.getParam('title', 'Rocket.Chat'); const title = navigation.getParam('title', 'Rocket.Chat');
return { return {
...themedHeader(screenProps.theme),
title, title,
headerRight: <LegalButton navigation={navigation} testID='login-view-more' />, headerRight: <LegalButton testID='login-view-more' navigation={navigation} />
...themedHeader(screenProps.theme)
}; };
} }
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
loginRequest: PropTypes.func.isRequired,
error: PropTypes.object,
Site_Name: PropTypes.string, Site_Name: PropTypes.string,
Accounts_RegistrationForm: PropTypes.string,
Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string, Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string, Accounts_PasswordPlaceholder: PropTypes.string,
Accounts_PasswordReset: PropTypes.bool, Accounts_PasswordReset: PropTypes.bool,
Accounts_RegistrationForm: PropTypes.string, Accounts_ShowFormLogin: PropTypes.bool,
Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
error: PropTypes.object,
failure: PropTypes.bool, failure: PropTypes.bool,
theme: PropTypes.string theme: PropTypes.string,
} loginRequest: PropTypes.func
static defaultProps = {
Accounts_PasswordReset: true
} }
constructor(props) { constructor(props) {
@ -85,15 +85,11 @@ class LoginView extends React.Component {
code: '', code: '',
showTOTP: false showTOTP: false
}; };
const { Site_Name } = this.props;
this.setTitle(Site_Name);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { Site_Name, error } = this.props; const { error } = this.props;
if (nextProps.Site_Name && nextProps.Site_Name !== Site_Name) { if (nextProps.failure && !equal(error, nextProps.error)) {
this.setTitle(nextProps.Site_Name);
} else if (nextProps.failure && !equal(error, nextProps.error)) {
if (nextProps.error && nextProps.error.error === 'totp-required') { if (nextProps.error && nextProps.error.error === 'totp-required') {
animateNextTransition(); animateNextTransition();
this.setState({ showTOTP: true }); this.setState({ showTOTP: true });
@ -103,58 +99,19 @@ class LoginView extends React.Component {
} }
} }
shouldComponentUpdate(nextProps, nextState) { login = () => {
const { const { navigation, Site_Name } = this.props;
user, password, code, showTOTP navigation.navigate('LoginView', { title: Site_Name });
} = this.state;
const {
isFetching, failure, error, Site_Name, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme
} = this.props;
if (nextState.user !== user) {
return true;
}
if (nextState.password !== password) {
return true;
}
if (nextState.code !== code) {
return true;
}
if (nextState.showTOTP !== showTOTP) {
return true;
}
if (nextProps.isFetching !== isFetching) {
return true;
}
if (nextProps.failure !== failure) {
return true;
}
if (nextProps.theme !== theme) {
return true;
}
if (nextProps.Site_Name !== Site_Name) {
return true;
}
if (nextProps.Accounts_EmailOrUsernamePlaceholder !== Accounts_EmailOrUsernamePlaceholder) {
return true;
}
if (nextProps.Accounts_PasswordPlaceholder !== Accounts_PasswordPlaceholder) {
return true;
}
if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) {
return true;
}
if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) {
return true;
}
if (!equal(nextProps.error, error)) {
return true;
}
return false;
} }
setTitle = (title) => { register = () => {
const { navigation } = this.props; const { navigation, Site_Name } = this.props;
navigation.setParams({ title }); navigation.navigate('RegisterView', { title: Site_Name });
}
forgotPassword = () => {
const { navigation, Site_Name } = this.props;
navigation.navigate('ForgotPasswordView', { title: Site_Name });
} }
valid = () => { valid = () => {
@ -179,33 +136,86 @@ class LoginView extends React.Component {
analytics().logEvent('login'); analytics().logEvent('login');
} }
register = () => { renderUserForm = () => {
const { navigation, Site_Name } = this.props; const {
navigation.navigate('RegisterView', { title: Site_Name }); Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, isFetching, theme, Accounts_ShowFormLogin
} } = this.props;
forgotPassword = () => { if (!Accounts_ShowFormLogin) {
const { navigation, Site_Name } = this.props; return null;
navigation.navigate('ForgotPasswordView', { title: Site_Name }); }
return (
<>
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Login')}</Text>
<TextInput
label='Email or username'
containerStyle={styles.inputContainer}
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
keyboardType='email-address'
returnKeyType='next'
onChangeText={value => this.setState({ user: value })}
onSubmitEditing={() => { this.passwordInput.focus(); }}
testID='login-view-email'
textContentType='username'
autoCompleteType='username'
theme={theme}
/>
<TextInput
label='Password'
containerStyle={styles.inputContainer}
inputRef={(e) => { this.passwordInput = e; }}
placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')}
returnKeyType='send'
secureTextEntry
onSubmitEditing={this.submit}
onChangeText={value => this.setState({ password: value })}
testID='login-view-password'
textContentType='password'
autoCompleteType='password'
theme={theme}
/>
<Button
title={I18n.t('Login')}
type='primary'
onPress={this.submit}
testID='login-view-submit'
loading={isFetching}
disabled={!this.valid()}
theme={theme}
style={styles.loginButton}
/>
{Accounts_PasswordReset && (
<Button
title={I18n.t('Forgot_password')}
type='secondary'
onPress={this.forgotPassword}
testID='login-view-forgot-password'
theme={theme}
color={themes[theme].auxiliaryText}
fontSize={14}
/>
)}
{Accounts_RegistrationForm === 'Public' ? (
<View style={styles.bottomContainer}>
<Text style={[styles.bottomContainerText, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text>
<Text
style={[styles.bottomContainerTextBold, { color: themes[theme].actionTintColor }]}
onPress={this.register}
testID='login-view-register'
>{I18n.t('Create_account')}
</Text>
</View>
) : (<Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>)}
</>
);
} }
renderTOTP = () => { renderTOTP = () => {
const { isFetching, theme } = this.props; const { isFetching, theme } = this.props;
return ( return (
<SafeAreaView <>
style={[ <Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Two_Factor_Authentication')}</Text>
sharedStyles.container,
isTablet && sharedStyles.tabletScreenContent,
{ backgroundColor: themes[theme].backgroundColor }
]}
testID='login-view'
forceInset={{ vertical: 'never' }}
>
<Text
style={[sharedStyles.loginTitle, sharedStyles.textBold, styles.loginTitle, { color: themes[theme].titleText }]}
>
{I18n.t('Two_Factor_Authentication')}
</Text>
<Text <Text
style={[sharedStyles.loginSubtitle, sharedStyles.textRegular, { color: themes[theme].titleText }]} style={[sharedStyles.loginSubtitle, sharedStyles.textRegular, { color: themes[theme].titleText }]}
> >
@ -232,114 +242,36 @@ class LoginView extends React.Component {
disabled={!this.valid()} disabled={!this.valid()}
theme={theme} theme={theme}
/> />
</SafeAreaView> </>
);
}
renderUserForm = () => {
const {
Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, isFetching, theme
} = this.props;
return (
<SafeAreaView
style={[
sharedStyles.container,
isTablet && sharedStyles.tabletScreenContent,
{ backgroundColor: themes[theme].backgroundColor }
]}
testID='login-view'
forceInset={{ vertical: 'never' }}
>
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Login')}</Text>
<TextInput
autoFocus
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
keyboardType='email-address'
returnKeyType='next'
iconLeft='at'
onChangeText={value => this.setState({ user: value })}
onSubmitEditing={() => { this.passwordInput.focus(); }}
testID='login-view-email'
textContentType='username'
autoCompleteType='username'
theme={theme}
/>
<TextInput
inputRef={(e) => { this.passwordInput = e; }}
placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')}
returnKeyType='send'
iconLeft='key'
secureTextEntry
onSubmitEditing={this.submit}
onChangeText={value => this.setState({ password: value })}
testID='login-view-password'
containerStyle={sharedStyles.inputLastChild}
textContentType='password'
autoCompleteType='password'
theme={theme}
/>
<Button
title={I18n.t('Login')}
type='primary'
onPress={this.submit}
testID='login-view-submit'
loading={isFetching}
disabled={!this.valid()}
theme={theme}
/>
{Accounts_PasswordReset && (
<Button
title={I18n.t('Forgot_password')}
type='secondary'
onPress={this.forgotPassword}
testID='login-view-forgot-password'
theme={theme}
/>
)}
{Accounts_RegistrationForm === 'Public' ? (
<View style={styles.bottomContainer}>
<Text style={[styles.dontHaveAccount, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text>
<Text
style={[styles.createAccount, { color: themes[theme].actionTintColor }]}
onPress={this.register}
testID='login-view-register'
>{I18n.t('Create_account')}
</Text>
</View>
) : (<Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>)}
</SafeAreaView>
); );
} }
render() { render() {
const { showTOTP } = this.state; const { showTOTP } = this.state;
const { theme } = this.props; const { Accounts_ShowFormLogin, theme } = this.props;
return ( return (
<KeyboardView <FormContainer theme={theme}>
style={{ backgroundColor: themes[theme].backgroundColor }} <FormContainerInner>
contentContainerStyle={sharedStyles.container} {!showTOTP ? <LoginServices separator={Accounts_ShowFormLogin} /> : null}
keyboardVerticalOffset={128}
key='login-view'
>
<StatusBar theme={theme} />
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}>
{!showTOTP ? this.renderUserForm() : null} {!showTOTP ? this.renderUserForm() : null}
{showTOTP ? this.renderTOTP() : null} {showTOTP ? this.renderTOTP() : null}
</ScrollView> </FormContainerInner>
</KeyboardView> </FormContainer>
); );
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
server: state.server.server,
Site_Name: state.settings.Site_Name,
Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin,
Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm,
Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText,
isFetching: state.login.isFetching, isFetching: state.login.isFetching,
failure: state.login.failure, failure: state.login.failure,
error: state.login.error && state.login.error.data, error: state.login.error && state.login.error.data,
Site_Name: state.settings.Site_Name,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm,
Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText,
Accounts_PasswordReset: state.settings.Accounts_PasswordReset Accounts_PasswordReset: state.settings.Accounts_PasswordReset
}); });

View File

@ -1,91 +1,95 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Text, ScrollView, Keyboard, Image, StyleSheet, TouchableOpacity, View, Alert Text, Keyboard, StyleSheet, TouchableOpacity, View, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import * as FileSystem from 'expo-file-system'; import * as FileSystem from 'expo-file-system';
import DocumentPicker from 'react-native-document-picker'; import DocumentPicker from 'react-native-document-picker';
import ActionSheet from 'react-native-action-sheet'; import ActionSheet from 'react-native-action-sheet';
import isEqual from 'deep-equal';
import RNUserDefaults from 'rn-user-defaults'; import RNUserDefaults from 'rn-user-defaults';
import { encode } from 'base-64'; import { encode } from 'base-64';
import parse from 'url-parse'; import parse from 'url-parse';
import { serverRequest } from '../actions/server'; import EventEmitter from '../utils/events';
import {
selectServerRequest, serverRequest, serverInitAdd, serverFinishAdd
} from '../actions/server';
import { appStart as appStartAction } from '../actions';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import Button from '../containers/Button'; import Button from '../containers/Button';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import OnboardingSeparator from '../containers/OnboardingSeparator';
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import I18n from '../i18n'; import I18n from '../i18n';
import { verticalScale, moderateScale } from '../utils/scaling'; import { isIOS } from '../utils/deviceInfo';
import KeyboardView from '../presentation/KeyboardView';
import { isIOS, isNotch, isTablet } from '../utils/deviceInfo';
import { CustomIcon } from '../lib/Icons';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import log from '../utils/log'; import log from '../utils/log';
import { animateNextTransition } from '../utils/layoutAnimation'; import { animateNextTransition } from '../utils/layoutAnimation';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch'; import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';
import { themedHeader } from '../utils/navigation';
import { CloseModalButton } from '../containers/HeaderButton';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
image: {
alignSelf: 'center',
marginVertical: verticalScale(20),
width: 210,
height: 171
},
title: { title: {
...sharedStyles.textBold, ...sharedStyles.textBold,
fontSize: moderateScale(22), fontSize: 22
letterSpacing: 0,
alignSelf: 'center'
}, },
inputContainer: { inputContainer: {
marginTop: 25, marginTop: 24,
marginBottom: 15 marginBottom: 32
},
backButton: {
position: 'absolute',
paddingHorizontal: 9,
left: 15
}, },
certificatePicker: { certificatePicker: {
flex: 1, marginBottom: 32,
marginTop: 40,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center' justifyContent: 'flex-end'
}, },
chooseCertificateTitle: { chooseCertificateTitle: {
fontSize: 15, fontSize: 13,
...sharedStyles.textRegular ...sharedStyles.textRegular
}, },
chooseCertificate: { chooseCertificate: {
fontSize: 15, fontSize: 13,
...sharedStyles.textSemibold ...sharedStyles.textSemibold
},
description: {
...sharedStyles.textRegular,
fontSize: 14,
textAlign: 'left',
marginBottom: 24
},
connectButton: {
marginBottom: 0
} }
}); });
const defaultServer = 'https://open.rocket.chat';
class NewServerView extends React.Component { class NewServerView extends React.Component {
static navigationOptions = () => ({ static navigationOptions = ({ screenProps, navigation }) => {
header: null const previousServer = navigation.getParam('previousServer', null);
}) const close = navigation.getParam('close', () => {});
return {
headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} /> : undefined,
title: I18n.t('Workspaces'),
...themedHeader(screenProps.theme)
};
}
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
server: PropTypes.string,
theme: PropTypes.string, theme: PropTypes.string,
connecting: PropTypes.bool.isRequired, connecting: PropTypes.bool.isRequired,
connectServer: PropTypes.func.isRequired connectServer: PropTypes.func.isRequired,
selectServer: PropTypes.func.isRequired,
currentServer: PropTypes.string,
initAdd: PropTypes.func,
finishAdd: PropTypes.func
} }
constructor(props) { constructor(props) {
super(props); super(props);
const server = props.navigation.getParam('server'); this.previousServer = props.navigation.getParam('previousServer');
props.navigation.setParams({ close: this.close, previousServer: this.previousServer });
// Cancel // Cancel
this.options = [I18n.t('Cancel')]; this.options = [I18n.t('Cancel')];
@ -96,47 +100,51 @@ class NewServerView extends React.Component {
this.DELETE_INDEX = 1; this.DELETE_INDEX = 1;
this.state = { this.state = {
text: server || '', text: '',
autoFocus: !server, connectingOpen: false,
certificate: null certificate: null
}; };
EventEmitter.addEventListener('NewServer', this.handleNewServerEvent);
} }
componentDidMount() { componentDidMount() {
const { text } = this.state; const { initAdd } = this.props;
const { connectServer } = this.props; if (this.previousServer) {
if (text) { initAdd();
connectServer(text);
} }
} }
shouldComponentUpdate(nextProps, nextState) { componentWillUnmount() {
const { text, certificate } = this.state; EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
const { connecting, theme } = this.props;
if (nextState.text !== text) {
return true;
}
if (!isEqual(nextState.certificate, certificate)) {
return true;
}
if (nextProps.connecting !== connecting) {
return true;
}
if (nextProps.theme !== theme) {
return true;
}
return false;
} }
onChangeText = (text) => { onChangeText = (text) => {
this.setState({ text }); this.setState({ text });
} }
close = () => {
const { selectServer, currentServer, finishAdd } = this.props;
if (this.previousServer !== currentServer) {
selectServer(this.previousServer);
}
finishAdd();
}
handleNewServerEvent = (event) => {
let { server } = event;
const { connectServer } = this.props;
this.setState({ text: server });
server = this.completeUrl(server);
connectServer(server);
}
submit = async() => { submit = async() => {
const { text, certificate } = this.state; const { text, certificate } = this.state;
const { connectServer } = this.props; const { connectServer } = this.props;
let cert = null; let cert = null;
this.setState({ connectingOpen: false });
if (certificate) { if (certificate) {
const certificatePath = `${ FileSystem.documentDirectory }/${ certificate.name }`; const certificatePath = `${ FileSystem.documentDirectory }/${ certificate.name }`;
try { try {
@ -158,6 +166,12 @@ class NewServerView extends React.Component {
} }
} }
connectOpen = () => {
this.setState({ connectingOpen: true });
const { connectServer } = this.props;
connectServer('https://open.rocket.chat');
}
basicAuth = async(server, text) => { basicAuth = async(server, text) => {
try { try {
const parsedUrl = parse(text, true); const parsedUrl = parse(text, true);
@ -238,28 +252,6 @@ class NewServerView extends React.Component {
}); });
} }
renderBack = () => {
const { navigation, theme } = this.props;
let top = 15;
if (isIOS) {
top = isNotch ? 45 : 30;
}
return (
<TouchableOpacity
style={[styles.backButton, { top }]}
onPress={() => navigation.pop()}
>
<CustomIcon
name='back'
size={30}
color={themes[theme].tintColor}
/>
</TouchableOpacity>
);
}
renderCertificatePicker = () => { renderCertificatePicker = () => {
const { certificate } = this.state; const { certificate } = this.state;
const { theme } = this.props; const { theme } = this.props;
@ -292,49 +284,49 @@ class NewServerView extends React.Component {
render() { render() {
const { connecting, theme } = this.props; const { connecting, theme } = this.props;
const { text, autoFocus } = this.state; const { text, connectingOpen } = this.state;
return ( return (
<KeyboardView <FormContainer theme={theme}>
style={{ backgroundColor: themes[theme].backgroundColor }} <FormContainerInner>
contentContainerStyle={sharedStyles.container} <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Join_your_workspace')}</Text>
keyboardVerticalOffset={128} <TextInput
key='login-view' label='Enter workspace URL'
> placeholder='Ex. your-company.rocket.chat'
<StatusBar theme={theme} /> containerStyle={styles.inputContainer}
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}> value={text}
<SafeAreaView style={sharedStyles.container} testID='new-server-view'> returnKeyType='send'
<Image style={styles.image} source={{ uri: 'new_server' }} /> onChangeText={this.onChangeText}
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Sign_in_your_server')}</Text> testID='new-server-view-input'
<View style={isTablet && sharedStyles.tabletScreenContent}> onSubmitEditing={this.submit}
<TextInput clearButtonMode='while-editing'
autoFocus={autoFocus} keyboardType='url'
containerStyle={styles.inputContainer} textContentType='URL'
placeholder={defaultServer} theme={theme}
value={text} />
returnKeyType='send' <Button
onChangeText={this.onChangeText} title={I18n.t('Connect')}
testID='new-server-view-input' type='primary'
onSubmitEditing={this.submit} onPress={this.submit}
clearButtonMode='while-editing' disabled={!text || connecting}
keyboardType='url' loading={!connectingOpen && connecting}
textContentType='URL' style={styles.connectButton}
theme={theme} testID='new-server-view-button'
/> theme={theme}
<Button />
title={I18n.t('Connect')} <OnboardingSeparator theme={theme} />
type='primary' <Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Onboarding_join_open_description')}</Text>
onPress={this.submit} <Button
disabled={!text} title={I18n.t('Join_our_open_workspace')}
loading={connecting} type='secondary'
testID='new-server-view-button' backgroundColor={themes[theme].chatComponentBackground}
theme={theme} onPress={this.connectOpen}
/> disabled={connecting}
{ isIOS ? this.renderCertificatePicker() : null } loading={connectingOpen && connecting}
</View> theme={theme}
</SafeAreaView> />
</ScrollView> </FormContainerInner>
{this.renderBack()} { isIOS ? this.renderCertificatePicker() : null }
</KeyboardView> </FormContainer>
); );
} }
} }
@ -344,7 +336,11 @@ const mapStateToProps = state => ({
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)) connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)),
initAdd: () => dispatch(serverInitAdd()),
finishAdd: () => dispatch(serverFinishAdd()),
selectServer: server => dispatch(selectServerRequest(server)),
appStart: root => dispatch(appStartAction(root))
}); });
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView)); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView));

View File

@ -1,61 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, TouchableWithoutFeedback } from 'react-native';
import styles from './styles';
import { themes } from '../../constants/colors';
import DisclosureIndicator from '../../containers/DisclosureIndicator';
export default class Button extends React.PureComponent {
static propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
type: PropTypes.string,
theme: PropTypes.string,
icon: PropTypes.node.isRequired,
testID: PropTypes.string.isRequired,
onPress: PropTypes.func
}
static defaultProps = {
title: 'Press me!',
type: 'primary',
onPress: () => alert('It works!')
}
state = {
active: false
};
render() {
const {
title, subtitle, type, onPress, icon, testID, theme
} = this.props;
const { active } = this.state;
const activeStyle = active && styles.buttonActive;
const isPrimary = (type === 'primary');
const buttonContainerStyle = {
backgroundColor: isPrimary ? themes[theme].actionTintColor : themes[theme].focusedBackground,
borderColor: isPrimary ? themes[theme].actionTintColor : themes[theme].borderColor
};
return (
<TouchableWithoutFeedback
onPress={onPress}
onPressIn={() => this.setState({ active: true })}
onPressOut={() => this.setState({ active: false })}
testID={testID}
>
<View style={[styles.buttonContainer, buttonContainerStyle]}>
<View style={styles.buttonIconContainer}>
{icon}
</View>
<View style={styles.buttonCenter}>
<Text style={[styles.buttonTitle, { color: isPrimary ? themes[theme].buttonText : themes[theme].tintColor }, activeStyle]}>{title}</Text>
{subtitle ? <Text style={[styles.buttonSubtitle, activeStyle, { color: themes[theme].auxiliaryText }]}>{subtitle}</Text> : null}
</View>
{type === 'secondary' ? <DisclosureIndicator theme={theme} /> : null}
</View>
</TouchableWithoutFeedback>
);
}
}

View File

@ -1,24 +1,19 @@
import React from 'react'; import React from 'react';
import { import {
View, Text, Image, TouchableOpacity, BackHandler, Linking View, Text, Image, BackHandler, Linking
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import { selectServerRequest, serverInitAdd, serverFinishAdd } from '../../actions/server';
import { appStart as appStartAction } from '../../actions'; import { appStart as appStartAction } from '../../actions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import Button from './Button'; import Button from '../../containers/Button';
import styles from './styles'; import styles from './styles';
import { isIOS, isNotch, isTablet } from '../../utils/deviceInfo'; import { isTablet } from '../../utils/deviceInfo';
import EventEmitter from '../../utils/events';
import { CustomIcon } from '../../lib/Icons';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import sharedStyles from '../Styles'; import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
class OnboardingView extends React.Component { class OnboardingView extends React.Component {
static navigationOptions = () => ({ static navigationOptions = () => ({
@ -27,11 +22,6 @@ class OnboardingView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
adding: PropTypes.bool,
selectServer: PropTypes.func.isRequired,
currentServer: PropTypes.string,
initAdd: PropTypes.func,
finishAdd: PropTypes.func,
appStart: PropTypes.func, appStart: PropTypes.func,
theme: PropTypes.string theme: PropTypes.string
} }
@ -39,20 +29,11 @@ class OnboardingView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
this.previousServer = props.navigation.getParam('previousServer');
if (!isTablet) { if (!isTablet) {
Orientation.lockToPortrait(); Orientation.lockToPortrait();
} }
} }
componentDidMount() {
const { initAdd } = this.props;
if (this.previousServer) {
initAdd();
}
EventEmitter.addEventListener('NewServer', this.handleNewServerEvent);
}
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
const { theme } = this.props; const { theme } = this.props;
if (theme !== nextProps.theme) { if (theme !== nextProps.theme) {
@ -62,16 +43,6 @@ class OnboardingView extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
const {
selectServer, currentServer, adding, finishAdd
} = this.props;
if (adding) {
if (this.previousServer !== currentServer) {
selectServer(this.previousServer);
}
finishAdd();
}
EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
} }
@ -81,27 +52,9 @@ class OnboardingView extends React.Component {
return false; return false;
} }
close = () => {
const { appStart } = this.props;
appStart('inside');
}
newServer = (server) => {
const { navigation } = this.props;
navigation.navigate('NewServerView', { server });
}
handleNewServerEvent = (event) => {
const { server } = event;
this.newServer(server);
}
connectServer = () => { connectServer = () => {
this.newServer(); const { navigation } = this.props;
} navigation.navigate('NewServerView');
joinCommunity = () => {
this.newServer('https://open.rocket.chat');
} }
createWorkspace = async() => { createWorkspace = async() => {
@ -112,87 +65,38 @@ class OnboardingView extends React.Component {
} }
} }
renderClose = () => {
const { theme } = this.props;
if (this.previousServer) {
let top = 15;
if (isIOS) {
top = isNotch ? 45 : 30;
}
return (
<TouchableOpacity
style={[styles.closeModal, { top }]}
onPress={this.close}
testID='onboarding-close'
>
<CustomIcon
name='cross'
size={30}
color={themes[theme].actionTintColor}
/>
</TouchableOpacity>
);
}
return null;
}
render() { render() {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<SafeAreaView <FormContainer theme={theme}>
style={[ <FormContainerInner>
styles.container, <Image style={styles.onboarding} source={{ uri: 'logo' }} fadeDuration={0} />
{ backgroundColor: themes[theme].backgroundColor } <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Onboarding_title')}</Text>
]} <Text style={[styles.subtitle, { color: themes[theme].controlText }]}>{I18n.t('Onboarding_subtitle')}</Text>
testID='onboarding-view' <Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Onboarding_description')}</Text>
> <View style={styles.buttonsContainer}>
<StatusBar theme={theme} /> <Button
<Image style={styles.onboarding} source={{ uri: 'onboarding' }} fadeDuration={0} /> title={I18n.t('Onboarding_join_workspace')}
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Welcome_to_RocketChat')}</Text> type='primary'
<Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]}>{I18n.t('Open_Source_Communication')}</Text> onPress={this.connectServer}
<View style={[styles.buttonsContainer, isTablet && sharedStyles.tabletScreenContent]}> theme={theme}
<Button />
type='secondary' <Button
title={I18n.t('Connect_to_a_server')} title={I18n.t('Create_a_new_workspace')}
icon={<CustomIcon name='permalink' size={30} color={themes[theme].actionTintColor} />} type='secondary'
onPress={this.connectServer} backgroundColor={themes[theme].chatComponentBackground}
testID='connect-server-button' onPress={this.createWorkspace}
theme={theme} theme={theme}
/> />
<Button </View>
type='secondary' </FormContainerInner>
title={I18n.t('Join_the_community')} </FormContainer>
subtitle='open.rocket.chat'
icon={<Image source={{ uri: 'logo_onboarding' }} style={{ width: 32, height: 27 }} fadeDuration={0} />}
onPress={this.joinCommunity}
testID='join-community-button'
theme={theme}
/>
<Button
type='primary'
title={I18n.t('Create_a_new_workspace')}
icon={<CustomIcon name='plus' size={30} color={themes[theme].buttonText} />}
onPress={this.createWorkspace}
testID='create-workspace-button'
theme={theme}
/>
</View>
{this.renderClose()}
</SafeAreaView>
); );
} }
} }
const mapStateToProps = state => ({
currentServer: state.server.server,
adding: state.server.adding
});
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
initAdd: () => dispatch(serverInitAdd()),
finishAdd: () => dispatch(serverFinishAdd()),
selectServer: server => dispatch(selectServerRequest(server)),
appStart: root => dispatch(appStartAction(root)) appStart: root => dispatch(appStartAction(root))
}); });
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(OnboardingView)); export default connect(null, mapDispatchToProps)(withTheme(OnboardingView));

View File

@ -5,19 +5,14 @@ import { isTablet } from '../../utils/deviceInfo';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
export default StyleSheet.create({ export default StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: isTablet ? 'center' : 'flex-start'
},
onboarding: { onboarding: {
alignSelf: 'center', alignSelf: 'center',
marginTop: verticalScale(30), marginTop: isTablet ? 0 : verticalScale(116),
marginBottom: verticalScale(35), marginBottom: verticalScale(50),
maxHeight: verticalScale(150), maxHeight: verticalScale(150),
resizeMode: 'contain', resizeMode: 'contain',
width: 309, width: 80,
height: 250 height: 70
}, },
title: { title: {
...sharedStyles.textBold, ...sharedStyles.textBold,
@ -29,50 +24,18 @@ export default StyleSheet.create({
subtitle: { subtitle: {
...sharedStyles.textRegular, ...sharedStyles.textRegular,
fontSize: moderateScale(16), fontSize: moderateScale(16),
color: '#54585E', alignSelf: 'center',
alignSelf: 'center' marginBottom: verticalScale(24)
},
description: {
...sharedStyles.textRegular,
fontSize: moderateScale(14),
alignSelf: 'center',
textAlign: 'center',
marginHorizontal: 20
}, },
buttonsContainer: { buttonsContainer: {
marginBottom: verticalScale(10), marginBottom: verticalScale(10),
marginTop: verticalScale(30) marginTop: verticalScale(30)
},
buttonContainer: {
marginHorizontal: 15,
marginVertical: 5,
flexDirection: 'row',
height: 60,
alignItems: 'center',
borderWidth: 1,
borderRadius: 2
},
buttonCenter: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
buttonTitle: {
...sharedStyles.textSemibold,
fontSize: 17
},
buttonSubtitle: {
...sharedStyles.textRegular,
fontSize: 15
},
buttonIconContainer: {
width: 65,
alignItems: 'center',
justifyContent: 'center'
},
buttonIcon: {
marginHorizontal: 10,
width: 20,
height: 20
},
buttonActive: {
opacity: 0.5
},
closeModal: {
position: 'absolute',
left: 15
} }
}); });

View File

@ -1,30 +1,55 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Keyboard, Text, ScrollView } from 'react-native'; import {
Text, View, StyleSheet, Keyboard
} from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import equal from 'deep-equal';
import TextInput from '../containers/TextInput'; import log from '../utils/log';
import Button from '../containers/Button';
import KeyboardView from '../presentation/KeyboardView';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps'; import Button from '../containers/Button';
import I18n from '../i18n'; import I18n from '../i18n';
import { LegalButton } from '../containers/HeaderButton';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
import { themedHeader } from '../utils/navigation';
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import TextInput from '../containers/TextInput';
import isValidEmail from '../utils/isValidEmail';
import { showErrorAlert } from '../utils/info';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { loginRequest as loginRequestAction } from '../actions/login'; import { loginRequest as loginRequestAction } from '../actions/login';
import isValidEmail from '../utils/isValidEmail'; import openLink from '../utils/openLink';
import { LegalButton } from '../containers/HeaderButton'; import LoginServices from '../containers/LoginServices';
import StatusBar from '../containers/StatusBar';
import log from '../utils/log';
import { withTheme } from '../theme';
import { themes } from '../constants/colors';
import { themedHeader } from '../utils/navigation';
import { isTablet } from '../utils/deviceInfo';
import { showErrorAlert } from '../utils/info';
const shouldUpdateState = ['name', 'email', 'password', 'username', 'saving']; const styles = StyleSheet.create({
title: {
...sharedStyles.textBold,
fontSize: 22
},
inputContainer: {
marginVertical: 16
},
bottomContainer: {
flexDirection: 'column',
alignItems: 'center',
marginBottom: 32,
marginHorizontal: 30
},
bottomContainerText: {
...sharedStyles.textRegular,
fontSize: 13
},
bottomContainerTextBold: {
...sharedStyles.textSemibold,
fontSize: 13
},
registerButton: {
marginTop: 16,
marginBottom: 32
}
});
class RegisterView extends React.Component { class RegisterView extends React.Component {
static navigationOptions = ({ navigation, screenProps }) => { static navigationOptions = ({ navigation, screenProps }) => {
@ -32,17 +57,18 @@ class RegisterView extends React.Component {
return { return {
...themedHeader(screenProps.theme), ...themedHeader(screenProps.theme),
title, title,
headerRight: <LegalButton testID='register-view-more' navigation={navigation} /> headerRight: <LegalButton navigation={navigation} />
}; };
} }
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
loginRequest: PropTypes.func, server: PropTypes.string,
Site_Name: PropTypes.string,
Accounts_CustomFields: PropTypes.string, Accounts_CustomFields: PropTypes.string,
Accounts_EmailVerification: PropTypes.bool, Accounts_EmailVerification: PropTypes.bool,
theme: PropTypes.string theme: PropTypes.string,
Site_Name: PropTypes.string,
loginRequest: PropTypes.func
} }
constructor(props) { constructor(props) {
@ -71,29 +97,9 @@ class RegisterView extends React.Component {
}; };
} }
shouldComponentUpdate(nextProps, nextState) { login = () => {
const { customFields } = this.state; const { navigation, Site_Name } = this.props;
const { theme } = this.props; navigation.navigate('LoginView', { title: Site_Name });
if (nextProps.theme !== theme) {
return true;
}
if (!equal(nextState.customFields, customFields)) {
return true;
}
// eslint-disable-next-line react/destructuring-assignment
return shouldUpdateState.some(key => nextState[key] !== this.state[key]);
}
componentDidUpdate(prevProps) {
const { Site_Name } = this.props;
if (Site_Name && prevProps.Site_Name !== Site_Name) {
this.setTitle(Site_Name);
}
}
setTitle = (title) => {
const { navigation } = this.props;
navigation.setParams({ title });
} }
valid = () => { valid = () => {
@ -141,6 +147,14 @@ class RegisterView extends React.Component {
this.setState({ saving: false }); this.setState({ saving: false });
} }
openContract = (route) => {
const { server, theme } = this.props;
if (!server) {
return;
}
openLink(`${ server }/${ route }`, theme);
}
renderCustomFields = () => { renderCustomFields = () => {
const { customFields } = this.state; const { customFields } = this.state;
const { Accounts_CustomFields, theme } = this.props; const { Accounts_CustomFields, theme } = this.props;
@ -166,7 +180,6 @@ class RegisterView extends React.Component {
inputRef={(e) => { this[key] = e; }} inputRef={(e) => { this[key] = e; }}
placeholder={key} placeholder={key}
value={customFields[key]} value={customFields[key]}
iconLeft='flag'
testID='register-view-custom-picker' testID='register-view-custom-picker'
theme={theme} theme={theme}
/> />
@ -178,9 +191,9 @@ class RegisterView extends React.Component {
<TextInput <TextInput
inputRef={(e) => { this[key] = e; }} inputRef={(e) => { this[key] = e; }}
key={key} key={key}
label={key}
placeholder={key} placeholder={key}
value={customFields[key]} value={customFields[key]}
iconLeft='flag'
onChangeText={(value) => { onChangeText={(value) => {
const newValue = {}; const newValue = {};
newValue[key] = value; newValue[key] = value;
@ -192,6 +205,7 @@ class RegisterView extends React.Component {
} }
this.avatarUrl.focus(); this.avatarUrl.focus();
}} }}
containerStyle={styles.inputContainer}
theme={theme} theme={theme}
/> />
); );
@ -205,77 +219,105 @@ class RegisterView extends React.Component {
const { saving } = this.state; const { saving } = this.state;
const { theme } = this.props; const { theme } = this.props;
return ( return (
<KeyboardView <FormContainer theme={theme}>
style={{ backgroundColor: themes[theme].backgroundColor }} <FormContainerInner>
contentContainerStyle={sharedStyles.container} <LoginServices />
> <Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>
<StatusBar theme={theme} /> <TextInput
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}> label='Name'
<SafeAreaView style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]} testID='register-view' forceInset={{ vertical: 'never' }}> containerStyle={styles.inputContainer}
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text> placeholder={I18n.t('Name')}
<TextInput returnKeyType='next'
autoFocus onChangeText={name => this.setState({ name })}
placeholder={I18n.t('Name')} onSubmitEditing={() => { this.usernameInput.focus(); }}
returnKeyType='next' testID='register-view-name'
iconLeft='user' theme={theme}
onChangeText={name => this.setState({ name })} />
onSubmitEditing={() => { this.usernameInput.focus(); }} <TextInput
testID='register-view-name' label='Username'
theme={theme} containerStyle={styles.inputContainer}
/> inputRef={(e) => { this.usernameInput = e; }}
<TextInput placeholder={I18n.t('Username')}
inputRef={(e) => { this.usernameInput = e; }} returnKeyType='next'
placeholder={I18n.t('Username')} onChangeText={username => this.setState({ username })}
returnKeyType='next' onSubmitEditing={() => { this.emailInput.focus(); }}
iconLeft='at' testID='register-view-username'
onChangeText={username => this.setState({ username })} theme={theme}
onSubmitEditing={() => { this.emailInput.focus(); }} />
testID='register-view-username' <TextInput
theme={theme} label='Email'
/> containerStyle={styles.inputContainer}
<TextInput inputRef={(e) => { this.emailInput = e; }}
inputRef={(e) => { this.emailInput = e; }} placeholder={I18n.t('Email')}
placeholder={I18n.t('Email')} returnKeyType='next'
returnKeyType='next' keyboardType='email-address'
keyboardType='email-address' onChangeText={email => this.setState({ email })}
iconLeft='mail' onSubmitEditing={() => { this.passwordInput.focus(); }}
onChangeText={email => this.setState({ email })} testID='register-view-email'
onSubmitEditing={() => { this.passwordInput.focus(); }} theme={theme}
testID='register-view-email' />
theme={theme} <TextInput
/> label='Password'
<TextInput containerStyle={styles.inputContainer}
inputRef={(e) => { this.passwordInput = e; }} inputRef={(e) => { this.passwordInput = e; }}
placeholder={I18n.t('Password')} placeholder={I18n.t('Password')}
returnKeyType='send' returnKeyType='send'
iconLeft='key' secureTextEntry
secureTextEntry onChangeText={value => this.setState({ password: value })}
onChangeText={value => this.setState({ password: value })} onSubmitEditing={this.submit}
onSubmitEditing={this.submit} testID='register-view-password'
testID='register-view-password' theme={theme}
containerStyle={sharedStyles.inputLastChild} />
theme={theme}
/>
{this.renderCustomFields()} {this.renderCustomFields()}
<Button <Button
title={I18n.t('Register')} title={I18n.t('Register')}
type='primary' type='primary'
onPress={this.submit} onPress={this.submit}
testID='register-view-submit' testID='register-view-submit'
disabled={!this.valid()} disabled={!this.valid()}
loading={saving} loading={saving}
theme={theme} theme={theme}
/> style={styles.registerButton}
</SafeAreaView> />
</ScrollView>
</KeyboardView> <View style={styles.bottomContainer}>
<Text style={[styles.bottomContainerText, { color: themes[theme].auxiliaryText }]}>
{`${ I18n.t('Onboarding_agree_terms') }\n`}
<Text
style={[styles.bottomContainerTextBold, { color: themes[theme].actionTintColor }]}
onPress={() => this.openContract('terms-of-service')}
>{I18n.t('Terms_of_Service')}
</Text> {I18n.t('and')}
<Text
style={[styles.bottomContainerTextBold, { color: themes[theme].actionTintColor }]}
onPress={() => this.openContract('privacy-policy')}
> {I18n.t('Privacy_Policy')}
</Text>
</Text>
</View>
<View style={styles.bottomContainer}>
<Text style={[styles.bottomContainerText, { color: themes[theme].auxiliaryText }]}>{I18n.t('Do_you_have_an_account')}</Text>
<Text
style={[styles.bottomContainerTextBold, { color: themes[theme].actionTintColor }]}
onPress={this.login}
>{I18n.t('Login')}
</Text>
</View>
</FormContainerInner>
</FormContainer>
); );
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
server: state.server.server,
Site_Name: state.settings.Site_Name,
Gitlab_URL: state.settings.API_Gitlab_URL,
CAS_enabled: state.settings.CAS_enabled,
CAS_login_url: state.settings.CAS_login_url,
Accounts_CustomFields: state.settings.Accounts_CustomFields, Accounts_CustomFields: state.settings.Accounts_CustomFields,
Accounts_EmailVerification: state.settings.Accounts_EmailVerification Accounts_EmailVerification: state.settings.Accounts_EmailVerification
}); });

View File

@ -126,7 +126,7 @@ class ServerDropdown extends Component {
this.close(); this.close();
setTimeout(() => { setTimeout(() => {
navigation.navigate('OnboardingView', { previousServer: server }); navigation.navigate('NewServerView', { previousServer: server });
}, ANIMATION_DURATION); }, ANIMATION_DURATION);
} }

View File

@ -706,7 +706,7 @@ class RoomsListView extends React.Component {
} else if (handleCommandShowNewMessage(event)) { } else if (handleCommandShowNewMessage(event)) {
navigation.navigate('NewMessageView', { onPressItem: this._onPressItem }); navigation.navigate('NewMessageView', { onPressItem: this._onPressItem });
} else if (handleCommandAddNewServer(event)) { } else if (handleCommandAddNewServer(event)) {
navigation.navigate('OnboardingView', { previousServer: server }); navigation.navigate('NewServerView', { previousServer: server });
} }
}; };

View File

@ -135,7 +135,6 @@ class SetUsernameView extends React.Component {
autoFocus autoFocus
placeholder={I18n.t('Username')} placeholder={I18n.t('Username')}
returnKeyType='send' returnKeyType='send'
iconLeft='at'
onChangeText={value => this.setState({ username: value })} onChangeText={value => this.setState({ username: value })}
value={username} value={username}
onSubmitEditing={this.submit} onSubmitEditing={this.submit}

View File

@ -4,7 +4,8 @@ import { MAX_SCREEN_CONTENT_WIDTH, MAX_CONTENT_WIDTH } from '../constants/tablet
export default StyleSheet.create({ export default StyleSheet.create({
container: { container: {
flex: 1 flex: 1,
flexDirection: 'column'
}, },
containerScrollView: { containerScrollView: {
padding: 15, padding: 15,
@ -18,6 +19,7 @@ export default StyleSheet.create({
maxWidth: MAX_CONTENT_WIDTH maxWidth: MAX_CONTENT_WIDTH
}, },
tabletScreenContent: { tabletScreenContent: {
justifyContent: 'center',
alignSelf: 'center', alignSelf: 'center',
width: MAX_SCREEN_CONTENT_WIDTH width: MAX_SCREEN_CONTENT_WIDTH
}, },

View File

@ -0,0 +1,81 @@
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import PropTypes from 'prop-types';
import { createImageProgress } from 'react-native-image-progress';
import * as Progress from 'react-native-progress';
import FastImage from 'react-native-fast-image';
import sharedStyles from '../Styles';
import { themes } from '../../constants/colors';
import { isTablet } from '../../utils/deviceInfo';
const ImageProgress = createImageProgress(FastImage);
const SIZE = 96;
const MARGIN_TOP = isTablet ? 0 : 64;
const BORDER_RADIUS = 6;
const styles = StyleSheet.create({
container: {
marginBottom: 16,
width: '100%',
height: SIZE + MARGIN_TOP,
justifyContent: 'flex-end',
alignItems: 'center'
},
image: {
width: SIZE,
height: SIZE,
borderRadius: BORDER_RADIUS
},
fallback: {
width: SIZE,
height: SIZE,
borderRadius: BORDER_RADIUS,
alignItems: 'center',
justifyContent: 'center'
},
initial: {
...sharedStyles.textBold,
fontSize: 42
}
});
const getInitial = url => url && url.replace(/http(s?):\/\//, '').slice(0, 1);
const Fallback = ({ theme, initial }) => (
<View style={[styles.container, styles.fallback, { backgroundColor: themes[theme].dangerColor }]}>
<Text style={[styles.initial, { color: themes[theme].buttonText }]}>{initial}</Text>
</View>
);
const ServerAvatar = React.memo(({ theme, url, image }) => (
<View style={styles.container}>
{image && (
<ImageProgress
style={[styles.image, { borderColor: themes[theme].borderColor }]}
source={{ uri: `${ url }/${ image }` }}
resizeMode={FastImage.resizeMode.cover}
indicator={Progress.Pie}
indicatorProps={{
color: themes[theme].actionTintColor
}}
renderError={() => <Fallback theme={theme} initial={getInitial(url)} />}
/>
)}
</View>
));
ServerAvatar.propTypes = {
theme: PropTypes.string,
url: PropTypes.string,
image: PropTypes.string
};
ServerAvatar.displayName = 'ServerAvatar';
Fallback.propTypes = {
theme: PropTypes.string,
initial: PropTypes.string
};
export default ServerAvatar;

View File

@ -0,0 +1,89 @@
import React from 'react';
import { View, Text } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import I18n from '../../i18n';
import Button from '../../containers/Button';
import styles from './styles';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
import { themedHeader } from '../../utils/navigation';
import ServerAvatar from './ServerAvatar';
class WorkspaceView extends React.Component {
static navigationOptions = ({ screenProps }) => ({
title: I18n.t('Your_workspace'),
...themedHeader(screenProps.theme)
})
static propTypes = {
navigation: PropTypes.object,
theme: PropTypes.string,
Site_Name: PropTypes.string,
Site_Url: PropTypes.string,
server: PropTypes.string,
Assets_favicon_512: PropTypes.object,
registrationEnabled: PropTypes.bool,
registrationText: PropTypes.string
}
login = () => {
const { navigation, Site_Name } = this.props;
navigation.navigate('LoginView', { title: Site_Name });
}
register = () => {
const { navigation, Site_Name } = this.props;
navigation.navigate('RegisterView', { title: Site_Name });
}
render() {
const {
theme, Site_Name, Site_Url, Assets_favicon_512, server, registrationEnabled, registrationText
} = this.props;
return (
<FormContainer theme={theme}>
<FormContainerInner>
<View style={styles.alignItemsCenter}>
<ServerAvatar theme={theme} url={server} image={Assets_favicon_512 && Assets_favicon_512.defaultUrl} />
<Text style={[styles.serverName, { color: themes[theme].titleText }]}>{Site_Name}</Text>
<Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>{Site_Url}</Text>
</View>
<Button
title={I18n.t('Login')}
type='primary'
onPress={this.login}
theme={theme}
/>
{
registrationEnabled ? (
<Button
title={I18n.t('Create_account')}
type='secondary'
backgroundColor={themes[theme].chatComponentBackground}
onPress={this.register}
theme={theme}
/>
) : (
<Text style={[styles.registrationText, { color: themes[theme].auxiliaryText }]}>{registrationText}</Text>
)
}
</FormContainerInner>
</FormContainer>
);
}
}
const mapStateToProps = state => ({
server: state.server.server,
adding: state.server.adding,
Site_Name: state.settings.Site_Name,
Site_Url: state.settings.Site_Url,
Assets_favicon_512: state.settings.Assets_favicon_512,
registrationEnabled: state.settings.Accounts_RegistrationForm === 'Public',
registrationText: state.settings.Accounts_RegistrationForm_LinkReplacementText
});
export default connect(mapStateToProps)(withTheme(WorkspaceView));

View File

@ -0,0 +1,24 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
export default StyleSheet.create({
serverName: {
...sharedStyles.textSemibold,
fontSize: 16,
marginBottom: 4
},
serverUrl: {
...sharedStyles.textRegular,
fontSize: 14,
marginBottom: 24
},
registrationText: {
fontSize: 14,
...sharedStyles.textAlignCenter,
...sharedStyles.textRegular
},
alignItemsCenter: {
alignItems: 'center'
}
});

View File

@ -2,17 +2,17 @@
"images" : [ "images" : [
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "logo@1x.png", "filename" : "icon@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "logo@2x.png", "filename" : "icon@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "logo@3x.png", "filename" : "icon@3x.png",
"scale" : "3x" "scale" : "3x"
} }
], ],

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "logo_onboarding@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "logo_onboarding@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "logo_onboarding@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "new_server.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "new_server@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "new_server@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "onboarding.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "onboarding@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "onboarding@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "options.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "options@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "options@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB