Commit 95f401ef authored by Juan Manuel Solaro's avatar Juan Manuel Solaro

(feat) add login, new mocks, themeStore

1 merge request!472WIP: oboarding/welcome-screen
......@@ -24,6 +24,7 @@ import withdraw from './src/wallet/tokens/WithdrawStore';
import hashtag from './src/common/stores/HashtagStore';
import onboarding from './src/onboarding/OnboardingStore';
import groupsBar from './src/groups/GroupsBarStore';
import theme from './src/common/stores/ThemeStore';
import sessionService from './src/common/services/session.service';
import logService from './src/common/services/log.service';
......@@ -60,6 +61,7 @@ const stores = {
hashtag: new hashtag(),
onboarding: new onboarding(),
groupsBar: new groupsBar(),
theme: new theme(),
};
/**
......
......@@ -53,9 +53,9 @@
}
},
"auth":{
"login":"LOGIN",
"login":"Login",
"create":"CREATE A CHANNEL",
"forgot":"FORGOT PASSWORD",
"forgot":"Forgot your password?",
"email":"Email",
"username":"Username",
"password":"Password",
......@@ -69,8 +69,10 @@
"accept":"I have read and accept the",
"termsAndConditions":"terms of use",
"code":"Code",
"join":"Join the Minds Revolution",
"createChannel":"Join Minds Now"
"join":"Join the Revolution",
"createChannel":"Join Minds Now",
"haveAccount":"Don't have an account?",
"alreadyHaveAccount":"Already have an account? &{login}&"
},
"blockchain": {
"boostCreate": "Network Boost for {{tokensAmount}} Minds Tokens. {{message}}",
......@@ -501,7 +503,7 @@
"suggestedChannels":"Suggested channels",
"suggestedChannelsDescription":"Subscribe to some popular channels below based on your interests",
"suggestedGroups":"Suggested groups",
"suggestedGroupsDescription":"Join some popular groups below based on your interests",
"suggestedGroupsDescription":"Join some groups that may be of interest to you.",
"tokensCanBeUsed":"Tokens can be used to support other channels or boost your content for additional views (1 token = 1,000 views). In order to earn tokens, we will need a phone number to verify that your channel is unique.",
"yourAreReady":"You're all setup and ready to go!",
"readyDescription1":"Great, you have successfully configured your wallet to receive rewards and OnChain & OffChain payments.",
......@@ -533,20 +535,20 @@
"tokensDescription":"Tokens are the cryptocurrency used on Minds. Your total balance of tokens will be stored in two different types of addresses.",
"onchainDescription":"OnChain payments will be published to the public blockchain and require a small ETH gas fee. You can setup your own private keys or create new wallets.",
"offchainDescription":"OffChain payments will not be published to the blockchain and have spending limits. You will receive rewards into this address.",
"welcomeNew":"WELCOME",
"welcomeNew":"Welcome to the Minds Community!",
"welcomePrivacy":"Your privacy is our priority. You are free to provide as much or as little information as you wish for better recommendations and to connect with people in your local area.",
"welcomeLater":"No thanks, I’ll do it later",
"welcomeSetup":"Let’s Get Setup",
"profileSetup":"PROFILE SETUP",
"hashtagTitle":"Hashtags",
"hashtagStep":"Step 1 of 4",
"step":"Step {{step}} of {{total}}",
"hashtagInterest":"Some hashtags that may be of interest to you.",
"skipStep":"Skip",
"skipStep":"Skip this step",
"infoTitle":"Info",
"infoStep":"Step 2 of 4",
"infoMobileNumber":"Mobile Phone Number",
"infoLocation":"Location",
"infoDateBirth":"Date of Birth"
"infoDateBirth":"Date of Birth",
"groupTitle":"Groups"
},
"wallet": {
"inviteFriend":"Invite a Friend",
......
......@@ -81,6 +81,7 @@
"react-navigation-tabs": "^2.5.6",
"rn-apk": "^0.2.9",
"socket.io-client": "^2.3.0",
"styled-components": "^4.4.1",
"tipsi-stripe": "8.0.0-beta.8",
"web3": "^1.2.2"
},
......
src/assets/welcome.png

25.6 KB

import React, {
Component
} from 'react';
import * as Animatable from 'react-native-animatable';
import {
View,
Text,
KeyboardAvoidingView,
ScrollView,
// TextInput,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import authService from './AuthService';
import { CommonStyle } from '../styles/Common';
import { ComponentsStyle } from '../styles/Components';
import i18n from '../common/services/i18n.service';
import logService from '../common/services/log.service';
import ModalPicker from '../common/components/ModalPicker';
// workaround for android copy/paste issue
import TextInput from '../common/components/TextInput';
import Input from '../common/components/Input';
import styled from 'styled-components';
import { CommonStyled } from '../styles/CommonStyled';
import Button from '../common/components/Button';
/**
* Login Form
*/
export default class LoginForm extends Component {
/**
* State
*/
state = {
username: '',
password: '',
msg: '',
twoFactorToken: '',
twoFactorCode: '',
hidePassword: true,
inProgress: false,
showLanguages: false,
};
/**
* Constructor
*/
constructor(props) {
super(props);
this.state.language = i18n.getCurrentLocale();
}
/**
* Render
*/
render() {
const msg = this.state.msg ? (
<Animatable.Text animation="bounceInLeft" style={[CommonStyle.colorLight, { textAlign: 'center' }]} testID="loginMsg">{this.state.msg}</Animatable.Text>
) : null;
return (
<View
style={[CommonStyle.flexContainer]}>
<ScrollView style={[CommonStyle.flexContainer]}>
<View style={{flex:6}}>
<TitleText>{i18n.t('auth.login')}</TitleText>
{msg}
<Input
placeholder={i18n.t('auth.username')}
onChangeText={this.setUsername}
value={this.state.username}
testID="usernameInput"
/>
<View>
<Input
placeholder={i18n.t('auth.password')}
secureTextEntry={this.state.hidePassword}
onChangeText={this.setPassword}
value={this.state.password}
testID="userPasswordInput"
/>
<Icon
name={this.state.hidePassword ? 'md-eye' : 'md-eye-off'}
size={25}
onPress={this.toggleHidePassword}
style={ComponentsStyle.loginInputIconNew}
/>
</View>
</View>
<View style={[{flex:6, marginTop: 30}]}>
<Button
onPress={() => this.onLoginPress()}
title={i18n.t('auth.login')}
type="clear"
containerStyle={ComponentsStyle.loginButtonNew}
key={1}
loading={this.state.inProgress}
loadingRight={true}
disabled={this.state.inProgress}
disabledStyle={CommonStyle.backgroundTransparent}
testID="loginButton">
<Text style={ComponentsStyle.loginButtonTextNew}>{i18n.t('auth.login')}</Text>
</Button>
<View style={CommonStyle.marginTop4x}>
<Text style={[ComponentsStyle.linkNew]} onPress={this.onForgotPress}>{i18n.t('auth.forgot')}</Text>
</View>
</View>
</ScrollView>
</View>
);
}
/**
* Show languages
*/
showLanguages = () => {
this.setState({showLanguages: true});
};
/**
* Language selected
*/
languageSelected = (language) => {
this.setState({language, showLanguages: false});
i18n.setLocale(language);
};
/**
* Cancel language selection
*/
cancel = () => {
this.setState({showLanguages: false});
};
/**
* Set two factor
* @param {string} value
*/
setTwoFactor = value => {
const twoFactorCode = String(value).trim();
this.setState({twoFactorCode});
};
/**
* Set two factor
* @param {string} value
*/
setUsername = value => {
const username = String(value).trim();
this.setState({username});
};
/**
* Set two factor
* @param {string} value
*/
setPassword = value => {
const password = String(value).trim();
this.setState({password});
};
/**
* Set two factor
* @param {string} value
*/
toggleHidePassword = () => {
this.setState({hidePassword: !this.state.hidePassword});
};
/**
* Handle forgot password
*/
onForgotPress = () => {
this.props.onForgot();
};
/**
* On login press
*/
onLoginPress() {
this.setState({ msg: '', inProgress: true});
// is two factor auth
if (this.state.twoFactorToken) {
authService.twoFactorAuth(this.state.twoFactorToken, this.state.twoFactorCode)
.then(data => {
this.props.onLogin();
})
.catch(err => {
logService.exception('[LoginForm]', err);
});
} else {
authService.login(this.state.username, this.state.password)
.then(data => {
this.props.onLogin();
})
.catch(errJson => {
if (errJson.error === 'invalid_grant' || errJson.error === 'invalid_client') {
this.setState({ msg: i18n.t('auth.invalidGrant'), inProgress: false });
return;
}
//TODO implement on backend and edit
if (errJson.error === 'two_factor') {
this.setState({ twoFactorToken: errJson.message, inProgress: false });
return;
}
this.setState({ msg: errJson.message || 'Unknown error', inProgress: false });
});
}
}
}
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']}
`;
import React, {
Component
} from 'react';
import {inject, observer} from 'mobx-react/native'
import { StackActions, NavigationActions } from 'react-navigation';
import FastImage from 'react-native-fast-image';
import {
StyleSheet,
ScrollView,
View,
KeyboardAvoidingView,
Button,
Keyboard,
Animated,
Platform,
Text,
Image,
TouchableOpacity,
} from 'react-native';
import * as Animatable from 'react-native-animatable';
import LoginFormNew from './LoginFormNew';
import VideoBackground from '../common/components/VideoBackground';
import { CommonStyle } from '../styles/Common';
import { ComponentsStyle } from '../styles/Components';
import logService from '../common/services/log.service';
import featuresService from '../common/services/features.service';
import MindsLayout from '../common/components/MindsLayout';
import i18nService from '../common/services/i18n.service';
import styled from 'styled-components';
import { CommonStyled } from '../styles/CommonStyled';
const LOGO_HEIGHT = 100;
const LOGO_HEIGHT_SMALL = 50;
/**
* Login screen
*/
export default class LoginScreen extends Component {
/**
* Disable navigation bar
*/
static navigationOptions = {
header: null
}
constructor(props) {
super(props);
this.logoHeight = new Animated.Value(LOGO_HEIGHT);
}
componentWillMount () {
this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardWillShowSub.remove();
this.keyboardWillHideSub.remove();
}
keyboardWillShow = (event) => {
Animated.timing(this.logoHeight, {
duration: event.duration,
toValue: LOGO_HEIGHT_SMALL,
}).start();
};
keyboardWillHide = (event) => {
Animated.timing(this.logoHeight, {
duration: event.duration,
toValue: LOGO_HEIGHT,
}).start();
};
getLoginBody = () => {
return (
<View style={[CommonStyle.flexContainer, CommonStyle.paddingTop2x]}>
<Image
source={require('./../assets/logos/bulb.png')}
style={styles.bulb}
/>
<LoginFormNew
onLogin={() => this.login()}
onForgot={this.onPressForgot}
/>
</View>
);
};
getLoginFooter = () => {
return (
<TouchableOpacity onPress={this.onPressRegister}>
<FooterContainer>
<SubTitle>{i18nService.t('auth.haveAccount')}</SubTitle>
<TitleText>{i18nService.t('auth.createChannel')}</TitleText>
</FooterContainer>
</TouchableOpacity>
);
};
/**
* Render
*/
render() {
const resizeMode = 'center';
const body = this.getLoginBody();
const footer = this.getLoginFooter();
return (
<View style={CommonStyle.flexContainer}>
<MindsLayout
body={body}
footer={footer}
footerBackground={'secondary_background'}
/>
</View>
);
}
/**
* On press forgot
*/
onPressForgot = () => {
this.props.navigation.push('Forgot');
}
/**
* On press register
*/
onPressRegister = () => {
if (featuresService.has('register_pages-december-2019')) {
this.props.navigation.push('RegisterNew');
} else {
this.props.navigation.push('Register');
}
}
/**
* On login successful
*/
login() {
logService.info('user logged in');
}
}
const styles = StyleSheet.create({
logo: {
width: 200,
height: 84,
marginBottom: 30,
alignSelf: 'center',
},
bulb: {
width: 34.72,
height: 59.51,
alignSelf: 'center',
},
});
const FooterContainer = styled.View`${CommonStyled.flexColumnCentered}`;
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']}
`;
const SubTitle = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']}
`;
......@@ -10,25 +10,28 @@ import {
ScrollView,
Linking,
Alert,
StyleSheet
StyleSheet,
TouchableOpacity
} from 'react-native';
import authService from '../auth/AuthService';
import { CommonStyle } from '../styles/Common';
import { ComponentsStyle } from '../styles/Components';
import { CommonStyled } from '../styles/CommonStyled';
import styled from 'styled-components';
import { observer, inject } from 'mobx-react/native';
import {
CheckBox,
Button
} from 'react-native-elements'
import {CheckBox} from 'react-native-elements'
import i18n from '../common/services/i18n.service';
import sessionService from '../common/services/session.service';
import delay from '../common/helpers/delay';
import apiService from '../common/services/api.service';
import Input from '../common/components/Input';
import MindsLayout from '../common/components/MindsLayout';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Button from '../common/components/Button';
/**
* Register Form
......@@ -72,13 +75,16 @@ export default class RegisterFormNew extends Component {
setPassword = password => this.setState({password});
setConfirmPassword = confirmPassword => this.setState({confirmPassword});
render() {
getFormBody = () => {
return (
<ScrollView>
<View>
<Text style={styles.joinText}>
{i18n.t('auth.join')}
</Text>
<ScrollView style={[CommonStyle.flexContainer]}>
<View style={CommonStyle.marginBottom3x}>
<TouchableOpacity onPress={this.props.onBack}>
<Icon size={34} name="keyboard-arrow-left" color='#777777'/>
</TouchableOpacity>
</View>
<View style={CommonStyle.marginBottom3x}>
<TitleText>{i18n.t('auth.join')}</TitleText>
</View>
<View>
<Text style={{color: '#F00', textAlign: 'center', paddingTop:4, paddingLeft:4}}>
......@@ -122,22 +128,43 @@ export default class RegisterFormNew extends Component {
onPress={() => { this.setState({ termsAccepted: !this.state.termsAccepted }) }}
disabled={this.state.inProgress}
/>
<View style={[styles.containerButton, CommonStyle.marginTop2x]}>
<Button
onPress={() => this.onPressRegister()}
title={i18n.t('auth.createChannel')}
backgroundColor="#5DBAC0"
borderRadius={2}
containerViewStyle={[styles.button, ComponentsStyle.loginButton]}
textStyle={ComponentsStyle.loginButtonText}
loading={this.state.inProgress}
loadingRight={true}
disabled={this.state.inProgress}
disabledStyle={CommonStyle.backgroundTransparent}
/>
</View>
</ScrollView>
);
};
getFormFooter = () => {
return (
<View style={[styles.containerButton]}>
<Button
onPress={() => this.onPressRegister()}
borderRadius={2}
containerStyle={ComponentsStyle.loginButtonNew}
loading={this.state.inProgress}
loadingRight={true}
disabled={this.state.inProgress}
>
<Text style={ComponentsStyle.loginButtonTextNew}>{i18n.t('auth.createChannel')}</Text>
</Button>
<SubTitle>
{i18n.to('auth.alreadyHaveAccount', null, {
login: (
<Text style={[ComponentsStyle.linkNew, CommonStyle.fontL]} onPress={this.onPressBack}>
{i18n.t('auth.login')}
</Text>
),
})}
</SubTitle>
</View>
);
};
render() {
return (
<MindsLayout
body={this.getFormBody()}
footer={this.getFormFooter()}
/>
);
}
/**
......@@ -204,10 +231,21 @@ const styles = StyleSheet.create({
},
containerButton: {
flex: 1,
marginLeft: 10,
marginRight: 20,
},
button: {
alignSelf: 'stretch',
},
});
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']};
align-self: center;
`;
const SubTitle = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']};
align-self: center;
margin-top: 20px;
`;
......@@ -35,33 +35,26 @@ export default class RegisterScreenNew extends Component {
*/
static navigationOptions = {
header: null
}
};
render() {
return (
<Rectangle>
<Animatable.View animation="bounceIn">
<Animated.View>
<RegisterFormNew
onRegister={this.onRegister}
onBack={this.onPressBack}
/>
</Animated.View>
</Animatable.View>
</Rectangle>
<Animatable.View animation="bounceIn" style={[CommonStyle.flexContainer]}>
<Animated.View style={[CommonStyle.flexContainer]}>
<RegisterFormNew
onRegister={this.onRegister}
onBack={this.onPressBack}
/>
</Animated.View>
</Animatable.View>
);
}
onPressBack = () => {
this.props.navigation.navigate('Login');
}
};
onRegister = guid => {
logService.info('[Register] new user registered '+guid);
}
};
}
const styles = StyleSheet.create({
});
\ No newline at end of file
......@@ -11,7 +11,7 @@ export default class InfoPopup extends Component {
pointerColor={'#4A90E2'}
popover={<Text style={styles.textTooltip}>{this.props.info}</Text>}
containerStyle={styles.tooltip}>
<IconMC name="information-variant" size={16} onPress={this.showPopup} />
<IconMC name="information-variant" size={16} onPress={this.showPopup} color="#AEB0B8"/>
</Tooltip>
);
}
......
......@@ -46,6 +46,7 @@ export default class Input extends Component {
<PhoneInput
{...this.props}
style={[ComponentsStyle.loginInputNew, styles.shadow, this.props.style]}
textStyle={{color: '#FFFFFF'}}
onChangePhoneNumber={this.props.onChangeText}
ref="phoneInput"
placeholder=''
......@@ -95,11 +96,11 @@ export default class Input extends Component {
};
render() {
const optional = (<Text style={[styles.optional, CommonStyle.marginBottom2x]}>{"Optional"}</Text>);
const optional = (<Text style={[styles.optional]}>{"Optional"}</Text>);
return (
<View style={[CommonStyle.flexContainer, CommonStyle.marginTop]}>
<View style={styles.row}>
<View style={[CommonStyle.flexContainer, CommonStyle.marginBottom2x]}>
<View style={[styles.row, CommonStyle.marginBottom]}>
<View style={styles.row}>
<Text style={[styles.label]}>{this.props.placeholder}</Text>
{this.props.info && <InfoPopup info={this.props.info} />}
......@@ -118,17 +119,14 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
},
label: {
color: '#9B9B9B',
color: '#AEB0B8',
fontSize: 14,
fontFamily: 'Roboto',
marginLeft: 20,
marginRight: 5,
},
optional: {
color: '#9B9B9B',
color: '#AEB0B8',
fontSize: 14,
fontFamily: 'Roboto-Italic',
marginRight: 20,
},
shadow: {
shadowColor: "#000",
......
import React, {Component} from 'react';
import {CommonStyle} from '../../styles/Common';
import styled, {ThemeProvider} from 'styled-components';
import {inject, observer} from 'mobx-react/native'
@inject('theme')
@observer
export default class MindsLayout extends Component {
render() {
const bodyBackground = this.props.bodyBackground ? this.props.bodyBackground : null,
footerBackground = this.props.footerBackground ? this.props.footerBackground : null,
theme = this.props.theme.theme;
return (
<ThemeProvider theme={theme}>
<Container>
<Body backgroundColor={bodyBackground}>
{this.props.body}
</Body>
<Footer backgroundColor={footerBackground}>
{this.props.footer}
</Footer>
</Container>
</ThemeProvider>
);
}
}
const Container = styled.View`
flex: 1;
justify-content: center;
`;
const Body = styled.View`
flex: 9;
flex-direction: row;
justify-content: center;
background-color: ${(props) => props.theme[props.backgroundColor] || props.theme['primary_background']};
padding: 20px 20px 10px 20px;
`;
const Footer = styled.View`
flex: 3;
flex-direction: row;
justify-content: center;
background-color: ${(props) => props.theme[props.backgroundColor] || props.theme['primary_background']};
padding: 20px 20px 10px 20px;
`;
......@@ -131,7 +131,9 @@ export default class Wizard extends PureComponent<Props, State> {
let component;
if (featuresService.has('onboarding-december-2019')) {
component = (
<Rectangle>{this.props.steps[this.state.current].component}</Rectangle>
<View style={[CS.flexContainer]}>
{this.props.steps[this.state.current].component}
</View>
);
} else {
component = (
......
import {observable, action} from 'mobx';
import {LIGHT_THEME, DARK_THEME} from '../../styles/Colors';
export default class ThemeStore {
@observable theme = DARK_THEME;
@action
swithTheme() {
this.theme = this.theme.name === LIGHT_THEME.name ? DARK_THEME : LIGHT_THEME;
}
}
......@@ -39,6 +39,9 @@ export const CODE_PUSH_TOKEN = '';
*/
export const MINDS_FEATURES = {
crypto: Platform.OS === 'ios' ? false : true,
'homepage-december-2019': true,
'register_pages-december-2019': true,
'onboarding-december-2019': true,
};
/**
......
......@@ -4,6 +4,7 @@ import { abort } from '../common/helpers/abortableFetch';
import stores from '../../AppStores';
import featuresService from '../common/services/features.service';
import entitiesService from '../common/services/entities.service';
import FeedsService from '../common/services/feeds.service';
/**
* Groups Service
......@@ -14,17 +15,20 @@ class GroupsService {
* Load groups
*/
async loadList(filter, offset) {
let entities = '';
if (filter !=='suggested') {
let endpoint = 'api/v1/groups/' + filter;
let endpoint = (filter === 'suggested') ?
'api/v2/entities/suggested/groups' + (stores.hashtag.all ? '/all' : '' ) :
'api/v1/groups/' + filter;
// abort previous call
abort('groups:list');
// abort previous call
abort('groups:list');
const data = await api.get(endpoint, { limit: 12, offset }, 'groups:list')
const data = await api.get(endpoint, { limit: 12, offset }, 'groups:list')
let entities = (filter === 'suggested') ? data.entities : data.groups;
entities = data.groups;
} else {
entities = this.loadSuggestedGroups();
}
if (offset && entities) {
entities.shift();
......@@ -36,6 +40,30 @@ class GroupsService {
};
}
async loadSuggestedGroups(offset) {
const hashtags = '',
period = '30d',
all = '',
query = '',
nsfw = [],
feedsService = new FeedsService();
feedsService
.setEndpoint(`api/v2/feeds/global/top/groups`)
.setLimit(12)
.setAsActivities(true)
.setParams({
hashtags,
period,
all,
query,
nsfw,
});
await feedsService.fetchRemoteOrLocal();
return await feedsService.getEntities();
}
/**
* Load a single group
* @param {string} guid
......
......@@ -28,7 +28,7 @@ class GroupsStore {
/**
* Load list
*/
async loadList() {
async loadList(filter = this.filter) {
if (this.list.cantLoadMore()) {
return;
}
......@@ -37,7 +37,7 @@ class GroupsStore {
this.list.setErrorLoading(false);
try {
const data = await groupsService.loadList(this.filter, this.list.offset);
const data = await groupsService.loadList(filter, this.list.offset);
data.entities = GroupModel.createMany(data.entities);
this.list.setList(data);
this.assignRowKeys(data);
......
......@@ -51,6 +51,7 @@ import DeleteChannelScreen from '../settings/screens/DeleteChannelScreen';
import DiscoveryFeedScreen from '../discovery/DiscoveryFeedScreen';
import Gathering from '../gathering/Gathering';
import OnboardingScreenNew from '../onboarding/OnboardingScreenNew';
import LoginScreenNew from '../auth/LoginScreenNew';
/**
* Main stack navigator
......@@ -226,7 +227,7 @@ const RootStack = createStackNavigator(
*/
const AuthStack = createStackNavigator({
Login: {
screen: withErrorBoundaryScreen(LoginScreen),
screen: withErrorBoundaryScreen(LoginScreenNew),
},
Forgot: {
screen: withErrorBoundaryScreen(ForgotScreen),
......
import React, {Component} from 'react';
import {View, TouchableOpacity} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
export default class OnboardingBackButton extends Component {
render() {
return (
<View style={[{alignSelf: 'flex-start'}]}>
<TouchableOpacity onPress={this.props.onBack}>
<Icon size={34} name="keyboard-arrow-left" color="#777777" />
</TouchableOpacity>
</View>
);
}
}
import React, {Component} from 'react';
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {ComponentsStyle} from '../styles/Components';
import Button from '../common/components/Button';
import i18n from '../common/services/i18n.service';
export default class OnboardingButtons extends Component {
render() {
return (
<View style={styles.bottom}>
<Button
onPress={this.props.onNext}
borderRadius={2}
containerStyle={ComponentsStyle.loginButtonNew}>
<Text style={ComponentsStyle.loginButtonTextNew}>
{i18n.t('continue')}
</Text>
</Button>
<TouchableOpacity style={styles.skip} onPress={this.props.onNext}>
<Text style={styles.skipText}>{i18n.t('onboarding.skipStep')}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
bottom: {
flex: 1,
},
skip: {
backgroundColor: 'transparent',
borderRadius: 2,
paddingHorizontal: 30,
paddingVertical: 10,
marginTop: 20,
},
skipText: {
color: '#AEB0B8',
fontSize: 16,
lineHeight: 21,
alignSelf: 'center',
},
});
......@@ -61,6 +61,8 @@ export default class OnboardingScreenNew extends Component {
onNext = () => this.wizard.next();
onBack = () => this.wizard.previous();
render() {
const steps = [];
if (!this.props.onboarding.progress) {
......@@ -73,17 +75,17 @@ export default class OnboardingScreenNew extends Component {
}
if (!completed_items.some(r => r == 'suggested_hashtags')) {
steps.push({component: <HashtagsStepNew onNext={this.onNext}/>});
steps.push({component: <HashtagsStepNew onNext={this.onNext} onBack={this.onBack}/>});
}
steps.push({component: <ChannelSetupStepNew ref={r => this.channelSetup = r} onNext={this.onNext}/>});
steps.push({component: <ChannelSetupStepNew ref={r => this.channelSetup = r} onNext={this.onNext} onBack={this.onBack}/> });
if (!completed_items.some(r => r == 'suggested_groups')) {
steps.push({component: <SuggestedGroupsStepNew/>});
steps.push({component: <SuggestedGroupsStepNew onBack={this.onBack}/>});
}
if (!completed_items.some(r => r == 'suggested_channels')) {
steps.push({component: <SuggestedChannelsStep/>});
steps.push({component: <SuggestedChannelsStep onBack={this.onBack}/>});
}
if (!completed_items.some(r => r == 'tokens_verification')) {
......
import React, { Component } from 'react';
import {
View,
Text,
TouchableHighlight,
StyleSheet,
} from 'react-native';
import {View, Text, TouchableHighlight, StyleSheet} from 'react-native';
import { observer, inject } from 'mobx-react';
import { CommonStyle as CS } from '../../styles/Common';
import TagSelect from '../../common/components/TagSelect';
import TagInput from '../../common/components/TagInput';
import i18n from '../../common/services/i18n.service';
import { Button } from 'react-native-elements';
import { ComponentsStyle } from '../../styles/Components';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { TouchableOpacity, ScrollView } from 'react-native-gesture-handler';
import Input from '../../common/components/Input';
@inject('hashtag')
@observer
export default class ChannelSetupStepNew extends Component {
import MindsLayout from '../../common/components/MindsLayout';
import styled from 'styled-components';
import { CommonStyled } from '../../styles/CommonStyled';
import OnboardingButtons from '../OnboardingButtons';
import OnboardingBackButton from '../OnboardingBackButton';
export default class ChannelSetupStepNew extends Component {
state = {
phoneNumber: '+1',
location: '',
......@@ -31,22 +26,17 @@ export default class ChannelSetupStepNew extends Component {
setLocation = location => this.setState({location});
setBirthDate = birthDate => this.setState({birthDate});
componentDidMount() {
this.props.hashtag.setAll(true);
this.props.hashtag.loadSuggested().catch(err => {
logService.exception(err);
});
}
render() {
getBody = () => {
return (
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<OnboardingBackButton onBack={this.props.onBack} />
<View style={styles.textsContainer}>
<Text style={[CS.onboardingTitle, CS.marginTop3x, CS.marginBottom3x]}>{i18n.t('onboarding.profileSetup')}</Text>
<Text style={CS.onboardingSubtitle}>{i18n.t('onboarding.infoTitle')}</Text>
<Text style={CS.onboardingSteps}>{i18n.t('onboarding.infoStep')}</Text>
<Text style={[CS.onboardingTitle, CS.marginBottom2x]}>{i18n.t('onboarding.profileSetup')}</Text>
<TitleText>{i18n.t('onboarding.infoTitle')}</TitleText>
<Step>{i18n.t('onboarding.step',{step: 2, total: 4})}</Step>
<SubTitle>{i18n.t('onboarding.suggestedGroupsDescription')}</SubTitle>
</View>
<View style={styles.inputContainer}>
<ScrollView style={styles.inputContainer}>
<Input
placeholder={i18n.t('onboarding.infoMobileNumber')}
onChangeText={this.setPhoneNumber}
......@@ -73,65 +63,55 @@ export default class ChannelSetupStepNew extends Component {
info={"Tu hermana"}
inputType={'dateInput'}
/>
</View>
<View style={[styles.containerButton]}>
<TouchableOpacity style={styles.skip} onPress={this.props.onNext}>
<Text style={styles.skipText}>{i18n.t('onboarding.skipStep')}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.continue} onPress={this.props.onNext}>
<Text style={styles.continueText}>{i18n.t('continue')}</Text>
</TouchableOpacity>
</View>
</ScrollView>
</View>
);
};
getFooter = () => {
return <OnboardingButtons onNext={this.props.onNext} />;
};
render() {
return (
<View style={CS.flexContainer}>
<MindsLayout
body={this.getBody()}
footer={this.getFooter()}
/>
</View>
);
}
}
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']};
`;
const SubTitle = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['primary_text']}
margin-bottom: 20px;
margin-top: 25;
`;
const Step = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']}
`;
const styles = StyleSheet.create({
bottom: {
flex: 1,
marginLeft: 10,
marginRight: 20,
marginBottom: 10,
marginTop: 10,
width: '80%',
justifyContent: 'flex-end',
},
containerButton: {
flexDirection: 'row',
justifyContent: 'flex-end',
},
continue: {
backgroundColor: "#5DBAC0",
borderRadius: 2,
paddingHorizontal: 30,
paddingVertical: 10,
},
continueText: {
color: '#FFFFFF',
fontSize: 20,
lineHeight: 26,
fontWeight: "500",
},
skip: {
backgroundColor: "transparent",
borderRadius: 2,
paddingHorizontal: 30,
paddingVertical: 10,
},
skipText: {
color: '#9B9B9B',
fontSize: 16,
lineHeight: 21,
},
inputContainer: {
flex: 5,
marginLeft: 20,
marginRight: 20,
width: '90%',
flex: 6,
width: '100%',
},
textsContainer: {
flex: 2,
flex: 1.5,
alignItems: 'center',
}
});
\ No newline at end of file
import React, { Component } from 'react';
import {
View,
Text,
TouchableHighlight,
StyleSheet,
} from 'react-native';
import {View, Text, TouchableHighlight, StyleSheet} from 'react-native';
import { observer, inject } from 'mobx-react';
import { CommonStyle as CS } from '../../styles/Common';
import TagSelect from '../../common/components/TagSelect';
import TagInput from '../../common/components/TagInput';
import i18n from '../../common/services/i18n.service';
import { Button } from 'react-native-elements';
import { ComponentsStyle } from '../../styles/Components';
import { TouchableOpacity } from 'react-native-gesture-handler';
import MindsLayout from '../../common/components/MindsLayout';
import styled from 'styled-components';
import { CommonStyled } from '../../styles/CommonStyled';
import OnboardingButtons from '../OnboardingButtons';
import OnboardingBackButton from '../OnboardingBackButton';
@inject('hashtag')
@observer
export default class HashtagsStepNew extends Component {
......@@ -27,20 +26,22 @@ export default class HashtagsStepNew extends Component {
});
}
render() {
getBody = () => {
return (
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<OnboardingBackButton onBack={this.props.onBack} />
<View style={styles.textsContainer}>
<Text style={[CS.onboardingTitle, CS.marginTop3x, CS.marginBottom3x]}>{i18n.t('onboarding.profileSetup')}</Text>
<Text style={CS.onboardingSubtitle}>{i18n.t('onboarding.hashtagTitle')}</Text>
<Text style={CS.onboardingSteps}>{i18n.t('onboarding.hashtagStep')}</Text>
<Text style={[CS.linkNew, CS.marginTop2x, CS.marginBottom3x]}>{i18n.t('onboarding.hashtagInterest')}</Text>
<Text style={[CS.onboardingTitle, CS.marginBottom2x]}>{i18n.t('onboarding.profileSetup')}</Text>
<TitleText>{i18n.t('onboarding.hashtagTitle')}</TitleText>
<Step>{i18n.t('onboarding.step',{step: 1, total: 4})}</Step>
<SubTitle>{i18n.t('onboarding.hashtagInterest')}</SubTitle>
</View>
<View style={styles.hashtagContainer}>
<TagSelect
tagStyle={[CS.backgroundWhite]}
tagStyle={styles.hashtag}
tagSelectedStyle={{borderColor: '#5DBAC0'}}
textSelectedStyle={{color: '#5DBAC0'}}
textStyle={[CS.fontL, CS.colorDarkGreyed]}
textStyle={styles.hashtagText}
containerStyle={[CS.rowJustifyStart]}
onTagDeleted={this.props.hashtag.deselect}
onTagAdded={this.props.hashtag.select}
......@@ -48,65 +49,58 @@ export default class HashtagsStepNew extends Component {
disableSort={true}
/>
</View>
<View style={styles.bottom}>
<View style={[styles.containerButton]}>
<TouchableOpacity style={styles.skip} onPress={this.props.onNext}>
<Text style={styles.skipText}>{i18n.t('onboarding.skipStep')}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.continue} onPress={this.props.onNext}>
<Text style={styles.continueText}>{i18n.t('continue')}</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
};
getFooter = () => {
return <OnboardingButtons onNext={this.props.onNext} />;
};
render() {
return (
<View style={CS.flexContainer}>
<MindsLayout
body={this.getBody()}
footer={this.getFooter()}
/>
</View>
);
}
}
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']};
`;
const SubTitle = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['primary_text']}
margin-bottom: 20px;
margin-top: 25;
`;
const Step = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']}
`;
const styles = StyleSheet.create({
bottom: {
flex: 1,
marginLeft: 10,
marginRight: 20,
marginBottom: 10,
marginTop: 10,
width: '80%',
justifyContent: 'flex-end',
},
containerButton: {
flexDirection: 'row',
justifyContent: 'flex-end',
},
continue: {
backgroundColor: "#5DBAC0",
borderRadius: 2,
paddingHorizontal: 30,
paddingVertical: 10,
},
continueText: {
color: '#FFFFFF',
fontSize: 20,
lineHeight: 26,
fontWeight: "500",
},
skip: {
backgroundColor: "transparent",
borderRadius: 2,
paddingHorizontal: 30,
paddingVertical: 10,
},
skipText: {
color: '#9B9B9B',
fontSize: 16,
lineHeight: 21,
},
hashtagContainer: {
flex: 4,
marginLeft: 20,
marginRight: 20,
flex: 3,
},
textsContainer: {
flex: 3,
flex: 4,
alignItems: 'center',
},
hashtag: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#979797',
},
hashtagText: {
color: '#AEB0B8',
fontSize: 17,
}
});
\ No newline at end of file
......@@ -3,7 +3,8 @@ import React, { Component } from 'react';
import {
View,
Text,
TouchableHighlight,
StyleSheet,
ScrollView,
} from 'react-native';
import { observer, inject } from 'mobx-react';
......@@ -11,29 +12,83 @@ import { CommonStyle as CS } from '../../styles/Common';
import GroupsListItem from '../../groups/GroupsListItem';
import i18n from '../../common/services/i18n.service';
import MindsLayout from '../../common/components/MindsLayout';
import styled from 'styled-components';
import { CommonStyled } from '../../styles/CommonStyled';
import OnboardingButtons from '../OnboardingButtons';
import OnboardingBackButton from '../OnboardingBackButton';
@inject('groups', 'hashtag')
@observer
export default class SuggestedGroupsStepNew extends Component {
componentWillMount() {
componentDidMount() {
this.props.hashtag.setAll(true);
this.props.groups.reset();
this.props.groups.loadList();
this.props.groups.loadList('suggested');
}
renderGroup = (group) => {
return <GroupsListItem key={group.guid} group={group}/>
}
render() {
getBody = () => {
return (
<View>
<View style={[CS.padding4x]}>
<Text style={[CS.fontXXL, CS.colorDark, CS.fontMedium]}>{i18n.t('onboarding.suggestedGroups')}</Text>
<Text style={[CS.fontL, CS.colorDarkGreyed, CS.marginBottom3x]}>{i18n.t('onboarding.suggestedGroupsDescription')}</Text>
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<OnboardingBackButton onBack={this.props.onBack} />
<View style={styles.textsContainer}>
<Text style={[CS.onboardingTitle, CS.marginBottom2x]}>{i18n.t('onboarding.profileSetup')}</Text>
<TitleText>{i18n.t('onboarding.groupTitle')}</TitleText>
<Step>{i18n.t('onboarding.step',{step: 1, total: 4})}</Step>
<SubTitle>{i18n.t('onboarding.suggestedGroupsDescription')}</SubTitle>
</View>
{this.props.groups.list.entities.map(group => this.renderGroup(group))}
<ScrollView style={styles.groupContainer}>
{this.props.groups.list.entities.map(group => this.renderGroup(group))}
</ScrollView>
</View>
);
};
getFooter = () => {
return <OnboardingButtons onNext={this.props.onNext} />;
};
render() {
return (
<View style={CS.flexContainer}>
<MindsLayout
body={this.getBody()}
footer={this.getFooter()}
/>
</View>
);
}
}
\ No newline at end of file
}
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']};
`;
const SubTitle = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['primary_text']}
margin-bottom: 20px;
margin-top: 25;
`;
const Step = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']}
`;
const styles = StyleSheet.create({
groupContainer: {
flex: 3.5,
width: '100%',
},
textsContainer: {
flex: 1.5,
alignItems: 'center',
},
});
\ No newline at end of file
......@@ -4,50 +4,91 @@ import {
View,
Text,
StyleSheet,
Image,
} from 'react-native';
import { observer, inject } from 'mobx-react';
import { CommonStyle as CS } from '../../styles/Common';
import colors from '../../styles/Colors';
import { ListItem, Button } from 'react-native-elements';
import { ListItem } from 'react-native-elements';
import logService from '../../common/services/log.service';
import i18nService from '../../common/services/i18n.service';
import { ComponentsStyle } from '../../styles/Components';
import Icon from 'react-native-vector-icons/Ionicons';
import MindsLayout from '../../common/components/MindsLayout';
import styled from 'styled-components';
import { CommonStyled } from '../../styles/CommonStyled';
import Button from '../../common/components/Button';
@inject('user')
@observer
export default class WelcomeStep extends Component {
/**
* Render
*/
render() {
getBody = () => {
return (
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<Icon name="md-thumbs-up" size={36} color="#FED12F" style={{paddingTop:1}}/>
<Text style={CS.onboardingTitle}>{i18nService.t('onboarding.welcomeNew')}</Text>
<Image
source={require('./../../assets/welcome.png')}
style={[styles.welcome, CS.marginTop4x, CS.marginBottom3x]}
/>
<TitleText>@{this.props.user.me.name}</TitleText>
<Text style={CS.onboardingSubtitle}>@{this.props.user.me.name}</Text>
<Welcome>{i18nService.t('onboarding.welcomeNew')}</Welcome>
<Text style={styles.privacy}>{i18nService.t('onboarding.welcomePrivacy')}</Text>
<Privacy>{i18nService.t('onboarding.welcomePrivacy')}</Privacy>
</View>
);
};
<View style={[styles.containerButton]}>
<Text style={[CS.linkNew, styles.later]} onPress={ this.props.onFinish }>{i18nService.t('onboarding.welcomeLater')}</Text>
<Button
onPress={this.props.onNext}
title={i18nService.t('onboarding.welcomeSetup')}
borderRadius={2}
buttonStyle={[styles.button]}
textStyle={ComponentsStyle.loginButtonText}
/>
</View>
getFooter = () => {
return (
<View style={CS.flexContainer}>
<Button
onPress={this.props.onNext}
borderRadius={2}
containerStyle={ComponentsStyle.loginButtonNew}
>
<Text style={ComponentsStyle.loginButtonTextNew}>{i18nService.t('onboarding.welcomeSetup')}</Text>
</Button>
<Text style={[CS.linkNew, styles.later]} onPress={ this.props.onFinish }>{i18nService.t('onboarding.welcomeLater')}</Text>
</View>
)
}
/**
* Render
*/
render() {
return (
<View style={CS.flexContainer}>
<MindsLayout
body={this.getBody()}
footer={this.getFooter()}
/>
</View>
);
}
}
const TitleText = styled.Text`
${CommonStyled.textTitle}
color: ${(props) => props.theme['primary_text']};
margin-bottom: 20px;
`;
const Welcome = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['primary_text']}
margin-bottom: 20px;
`;
const Privacy = styled.Text`
${CommonStyled.textSubTitle}
color: ${(props) => props.theme['secondary_text']}
margin-top: 20px;
`;
const styles = StyleSheet.create({
containerButton: {
flex: 1,
......@@ -74,6 +115,10 @@ const styles = StyleSheet.create({
},
later: {
alignSelf: 'center',
marginBottom: 40
marginTop: 20
},
welcome: {
height: 36,
width: 36,
}
});
......@@ -12,3 +12,23 @@ export default colors = {
explicit : '#f44336',
action : 'rgb(96, 125, 139)',
}
export const LIGHT_THEME = {
name: 'LIGHT',
primary_background: '#252E31',
secondary_background: '#2D3639',
primary_text: '#FFFFFF',
secondary_text: '#AEB0B8',
button_backgound: '#5DBAC0',
button_border: '#404A4E',
};
export const DARK_THEME = {
name: 'DARK',
primary_background: '#252E31',
secondary_background: '#2D3639',
primary_text: '#FFFFFF',
secondary_text: '#AEB0B8',
button_backgound: '#5DBAC0',
button_border: '#404A4E',
};
......@@ -597,7 +597,7 @@ export const CommonStyle = StyleSheet.create({
borderRadius: 2
},
onboardingTitle: {
color: '#A2A2A2',
color: '#AEB0B8',
fontSize: 13,
lineHeight: 18,
letterSpacing: 2,
......
export const CommonStyled = {
textTitle: `
font-family: Roboto;
font-size: 28px;
font-weight: bold;
line-height: 44px;
`,
textSubTitle: `
font-family: Roboto;
font-size: 17px;
font-weight: 500;
line-height: 23px;
`,
flexColumnCentered: `
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
align-content: center;
`,
};
......@@ -14,6 +14,12 @@ export const ComponentsStyle = StyleSheet.create({
right:8,
top:22
},
loginInputIconNew: {
position: 'absolute',
right:8,
top:37,
color: '#404A4E'
},
passwordinput: {
borderColor: '#ECECEC',
borderWidth: 1,
......@@ -43,18 +49,16 @@ export const ComponentsStyle = StyleSheet.create({
borderRadius: 4,
},
loginInputNew: {
color: '#4A4A4A',
color: '#FFFFFF',
fontSize: 16,
padding: 10,
fontFamily: 'Roboto',
backgroundColor: '#FFFFFF',
backgroundColor: 'transparent',
height: 40,
borderRadius: 2,
borderColor: '#D8D8D8',
borderColor: '#404A4E',
borderWidth: 1,
lineHeight: 21,
marginLeft: 20,
marginRight: 20
},
loginButton: {
marginRight: 0,
......@@ -64,6 +68,15 @@ export const ComponentsStyle = StyleSheet.create({
borderWidth: 1,
borderRadius: 30,
},
loginButtonNew: {
marginRight: 0,
marginLeft: 0,
backgroundColor: '#5DBAC0',
borderColor: '#5DBAC0',
borderWidth: 1,
borderRadius: 2,
height: 60,
},
loginButtonText: {
// fontFamily: 'Roboto',
fontSize: 16,
......@@ -71,6 +84,12 @@ export const ComponentsStyle = StyleSheet.create({
letterSpacing: 1.25,
color: 'white',
},
loginButtonTextNew: {
// fontFamily: 'Roboto',
fontSize: 20,
fontWeight: '500',
color: 'white',
},
registerCheckboxText: {
color: 'white'
},
......@@ -154,10 +173,13 @@ export const ComponentsStyle = StyleSheet.create({
},
linkNew: {
color: '#0091FF',
fontSize: 15,
textDecorationLine: 'underline',
},
termsNew: {
color: '#4A4A4A',
paddingLeft: 8
color: '#AEB0B8',
paddingLeft: 10,
fontSize: 16,
},
preview: {
height: 200,
......
Please register or to comment