aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets/sign_in_logo.pngbin0 -> 28865 bytes
-rw-r--r--src/components/common/LoginInput.tsx96
-rw-r--r--src/routes/Routes.tsx6
-rw-r--r--src/screens/Login.tsx215
-rw-r--r--src/screens/Registration.tsx16
5 files changed, 321 insertions, 12 deletions
diff --git a/src/assets/sign_in_logo.png b/src/assets/sign_in_logo.png
new file mode 100644
index 00000000..27e43268
--- /dev/null
+++ b/src/assets/sign_in_logo.png
Binary files differ
diff --git a/src/components/common/LoginInput.tsx b/src/components/common/LoginInput.tsx
new file mode 100644
index 00000000..e4d6b957
--- /dev/null
+++ b/src/components/common/LoginInput.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import {Text, TextInput, StyleSheet} from 'react-native';
+
+const LoginInput = (props: LoginInputProps) => {
+ return (
+ <>
+ <TextInput
+ accessibilityLabel={
+ props.isUsername
+ ? 'Username text entry box'
+ : props.isPassword
+ ? 'Password text entry box'
+ : undefined
+ }
+ accessibilityHint={
+ props.isUsername
+ ? 'Enter your tagg username here'
+ : props.isPassword
+ ? 'Enter your tagg password here'
+ : undefined
+ }
+ style={styles.credentials}
+ placeholder={
+ props.isUsername
+ ? 'Username'
+ : props.isPassword
+ ? 'Password'
+ : undefined
+ }
+ placeholderTextColor="#FFFFFF"
+ autoCompleteType={
+ props.isUsername ? 'username' : props.isPassword ? 'password' : 'off'
+ }
+ textContentType={
+ props.isUsername ? 'username' : props.isPassword ? 'password' : 'none'
+ }
+ returnKeyType={
+ props.isUsername ? 'next' : props.isPassword ? 'go' : 'default'
+ }
+ keyboardType={
+ props.isUsername
+ ? 'ascii-capable'
+ : props.isPassword
+ ? 'default'
+ : undefined
+ }
+ onChangeText={(input) => props.onChangeText(input)}
+ defaultValue={props.type}
+ onSubmitEditing={props.onSubmitEditing}
+ blurOnSubmit={
+ props.isUsername ? false : props.isPassword ? undefined : undefined
+ }
+ secureTextEntry={
+ props.isUsername ? false : props.isPassword ? true : false
+ }
+ ref={props.input_ref}
+ />
+ {!props.isValid && (
+ <Text style={styles.invalidCredentials}>{props.validationWarning}</Text>
+ )}
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ credentials: {
+ top: 190,
+ width: 248,
+ height: 40,
+ fontSize: 20,
+ color: '#FFFFFF',
+ borderColor: '#FFFDFD',
+ borderWidth: 2,
+ borderRadius: 20,
+ paddingLeft: 13,
+ marginVertical: 15,
+ },
+ invalidCredentials: {
+ top: 180,
+ color: '#F4DDFF',
+ },
+});
+
+interface LoginInputProps {
+ type: string;
+ isUsername?: boolean;
+ isPassword?: boolean;
+ onChangeText: (input: string) => void;
+ onSubmitEditing?: () => void;
+ input_ref?: object;
+ focusPasswordInput?: boolean;
+ isValid?: boolean;
+ validationWarning?: string;
+}
+
+export default LoginInput;
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index 9c2efada..d96c1d80 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -15,7 +15,11 @@ interface RoutesProps {}
const Routes: React.FC<RoutesProps> = ({}) => {
return (
<RootStack.Navigator initialRouteName="Login">
- <RootStack.Screen name="Login" component={Login} />
+ <RootStack.Screen
+ name="Login"
+ component={Login}
+ options={{headerShown: false}}
+ />
<RootStack.Screen name="Registration" component={Registration} />
</RootStack.Navigator>
);
diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx
index 0305b907..417d9bd7 100644
--- a/src/screens/Login.tsx
+++ b/src/screens/Login.tsx
@@ -1,9 +1,22 @@
import React from 'react';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import {View, Text, Button} from 'react-native';
+import {
+ View,
+ Text,
+ Alert,
+ StatusBar,
+ Image,
+ TouchableOpacity,
+ StyleSheet,
+ Keyboard,
+ TouchableWithoutFeedback,
+} from 'react-native';
import {RootStackParams} from '../routes';
+import LinearGradient from 'react-native-linear-gradient';
+
+import LoginInput from '../components/common/LoginInput';
type LoginScreenRouteProp = RouteProp<RootStackParams, 'Login'>;
type LoginScreenNavigationProp = StackNavigationProp<RootStackParams, 'Login'>;
@@ -12,16 +25,202 @@ interface LoginProps {
route: LoginScreenRouteProp;
navigation: LoginScreenNavigationProp;
}
+
const Login = ({navigation}: LoginProps) => {
+ const input_ref = React.createRef();
+ const [data, setData] = React.useState({
+ username: '',
+ password: '',
+ isValidUser: true,
+ isValidPassword: true,
+ focusPasswordInput: false,
+ });
+
+ /*
+ Updates the state of username. Also verifies the input of the Username field.
+ */
+ const handleUsernameUpdate = (val: string) => {
+ let validLength: boolean = val.trim().length >= 6;
+
+ if (validLength) {
+ setData({
+ ...data,
+ username: val,
+ isValidUser: true,
+ });
+ } else {
+ setData({
+ ...data,
+ username: val,
+ isValidUser: false,
+ });
+ }
+ };
+
+ /*
+ Updates the state of password. Also verifies the input of the Password field.
+ */
+ const handlePasswordUpdate = (val: string) => {
+ let validLength: boolean = val.trim().length >= 8;
+
+ if (validLength) {
+ setData({
+ ...data,
+ password: val,
+ isValidPassword: true,
+ });
+ } else {
+ setData({
+ ...data,
+ password: val,
+ isValidPassword: false,
+ });
+ }
+ };
+
+ /*
+ Handler for the Let's Start button or the Go button on the keyboard.
+ */
+ const handleLogin = () => {
+ if (data.isValidUser && data.isValidPassword) {
+ Alert.alert(
+ `My favorite Girl Scout Cookies are taggalongs! What are yours ${data.username}?`,
+ );
+ }
+ };
+
+ /*
+ Handler for the submit button on the Username keyboard
+ */
+ const handleUsernameSubmit = () => {
+ input_ref.current.focus();
+ };
+
+ const handleRegistration = () => {
+ navigation.navigate('Registration');
+ };
+
return (
- <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
- <Text style={{fontSize: 18}}>Welcome to Tagg! Login page goes here.</Text>
- <Button
- title="Register"
- onPress={() => navigation.navigate('Registration')}
- />
- </View>
+ <>
+ <StatusBar barStyle="light-content" />
+ <TouchableWithoutFeedback
+ onPress={() => {
+ Keyboard.dismiss();
+ }}>
+ <View style={styles.container}>
+ <LinearGradient
+ colors={['#8F00FF', '#6EE7E7']}
+ style={styles.linearGradient}
+ useAngle={true}
+ angle={154.72}
+ angleCenter={{x: 0.5, y: 0.5}}>
+ <Image
+ source={require('../assets/images/logo.png')}
+ style={styles.logo}
+ />
+ <LoginInput
+ type={data.username}
+ isUsername={true}
+ onChangeText={(user) => handleUsernameUpdate(user)}
+ onSubmitEditing={() => handleUsernameSubmit()}
+ isValid={data.isValidUser}
+ validationWarning={'Username must be at least 6 characters long.'}
+ />
+ <LoginInput
+ type={data.password}
+ isPassword={true}
+ onChangeText={(user) => handlePasswordUpdate(user)}
+ focusPasswordInput={data.focusPasswordInput}
+ onSubmitEditing={() => handleLogin()}
+ isValid={data.isValidPassword}
+ validationWarning={'Password must be at least 8 characters long.'}
+ input_ref={input_ref}
+ />
+ <TouchableOpacity
+ accessibilityLabel="Forgot password button"
+ accessibilityHint="Select this if you forgot your tagg password"
+ style={styles.forgotPassword}
+ onPress={() => Alert.alert("tagg! You're it!")}>
+ <Text style={styles.forgotPasswordText}>Forgot password</Text>
+ </TouchableOpacity>
+ <TouchableOpacity
+ accessibilityLabel="Let's start button"
+ accessibilityHint="Select this after entering your tagg username and password"
+ style={styles.start}
+ onPress={() => handleLogin()}>
+ <Text style={styles.startText}>Let's Start!</Text>
+ </TouchableOpacity>
+ <Text
+ accessible={true}
+ accessibilityLabel="New to tagg?"
+ style={styles.newUser}>
+ New to tagg?{' '}
+ <Text
+ accessible={true}
+ accessibilityLabel="Get started"
+ accessibilityHint="Select this if you do not have a tagg account"
+ style={styles.getStarted}
+ onPress={() => handleRegistration()}>
+ Get started!
+ </Text>
+ </Text>
+ </LinearGradient>
+ </View>
+ </TouchableWithoutFeedback>
+ </>
);
};
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'transparent',
+ },
+ linearGradient: {
+ flex: 1,
+ alignItems: 'center',
+ },
+ logo: {
+ top: 165,
+ width: 215,
+ height: 149,
+ },
+ forgotPassword: {
+ top: 190,
+ left: -60,
+ },
+ forgotPasswordText: {
+ fontSize: 15,
+ color: '#FFFFFF',
+ textDecorationLine: 'underline',
+ },
+ start: {
+ top: 195,
+ width: 144,
+ height: 36,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: '#FFFFFF',
+ borderRadius: 20,
+ marginTop: 15,
+ },
+ startText: {
+ fontSize: 15,
+ color: '#78A0EF',
+ fontWeight: 'bold',
+ },
+ getStarted: {
+ color: '#FFFFFF',
+ textDecorationLine: 'underline',
+ },
+ newUser: {
+ top: 240,
+ color: '#F4DDFF',
+ },
+ invalidCredentials: {
+ top: 180,
+ color: '#F4DDFF',
+ },
+});
+
export default Login;
diff --git a/src/screens/Registration.tsx b/src/screens/Registration.tsx
index 44658591..57b0eb18 100644
--- a/src/screens/Registration.tsx
+++ b/src/screens/Registration.tsx
@@ -1,14 +1,24 @@
import React from 'react';
-import {View, Text} from 'react-native';
+import {View, Text, StyleSheet} from 'react-native';
interface RegistrationProps {}
const Registration: React.FC<RegistrationProps> = ({}) => {
return (
- <View style={{flex: 1, alignSelf: 'center', justifyContent: 'center'}}>
- <Text style={{fontSize: 18}}>Registration sequence begins here!</Text>
+ <View style={styles.view}>
+ <Text style={styles.text}>Registration sequence begins here!</Text>
</View>
);
};
+const styles = StyleSheet.create({
+ view: {
+ flex: 1,
+ alignSelf: 'center',
+ justifyContent: 'center',
+ },
+ text: {
+ fontSize: 18,
+ },
+});
export default Registration;