aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.tsx23
-rw-r--r--src/__tests__/App-test.tsx16
-rw-r--r--src/assets/images/logo.pngbin0 -> 7852 bytes
-rw-r--r--src/assets/images/logo@2x.pngbin0 -> 18292 bytes
-rw-r--r--src/assets/images/logo@3x.pngbin0 -> 30041 bytes
-rw-r--r--src/index.ts1
-rw-r--r--src/routes/Routes.tsx24
-rw-r--r--src/routes/index.ts2
-rw-r--r--src/screens/Login.tsx234
-rw-r--r--src/screens/Registration.tsx14
-rw-r--r--src/screens/index.ts2
11 files changed, 316 insertions, 0 deletions
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 00000000..6c247f7c
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,23 @@
+/**
+ * Sample React Native App
+ * https://github.com/facebook/react-native
+ *
+ * Generated with the TypeScript template
+ * https://github.com/react-native-community/react-native-template-typescript
+ *
+ * @format
+ */
+
+import React from 'react';
+import Routes from './routes';
+import {NavigationContainer} from '@react-navigation/native';
+
+const App = () => {
+ return (
+ <NavigationContainer>
+ <Routes />
+ </NavigationContainer>
+ );
+};
+
+export default App;
diff --git a/src/__tests__/App-test.tsx b/src/__tests__/App-test.tsx
new file mode 100644
index 00000000..e362fb52
--- /dev/null
+++ b/src/__tests__/App-test.tsx
@@ -0,0 +1,16 @@
+/**
+ * @format
+ */
+
+import 'react-native';
+import React from 'react';
+import App from '../App';
+
+// Note: test renderer must be required after react-native.
+import renderer from 'react-test-renderer';
+
+jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
+
+it('renders correctly', () => {
+ renderer.create(<App />);
+});
diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png
new file mode 100644
index 00000000..08b21ae9
--- /dev/null
+++ b/src/assets/images/logo.png
Binary files differ
diff --git a/src/assets/images/logo@2x.png b/src/assets/images/logo@2x.png
new file mode 100644
index 00000000..df6be90e
--- /dev/null
+++ b/src/assets/images/logo@2x.png
Binary files differ
diff --git a/src/assets/images/logo@3x.png b/src/assets/images/logo@3x.png
new file mode 100644
index 00000000..7f254e8c
--- /dev/null
+++ b/src/assets/images/logo@3x.png
Binary files differ
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 00000000..ab7fd11d
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1 @@
+export {default} from './App';
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
new file mode 100644
index 00000000..0b08cbb1
--- /dev/null
+++ b/src/routes/Routes.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import {createStackNavigator} from '@react-navigation/stack';
+
+import {Login, Registration} from '../screens';
+
+export type RootStackParams = {
+ Login: undefined;
+ Registration: undefined;
+};
+
+const RootStack = createStackNavigator<RootStackParamList>();
+
+interface RoutesProps {}
+
+const Routes: React.FC<RoutesProps> = ({}) => {
+ return (
+ <RootStack.Navigator initialRouteName="Login">
+ <RootStack.Screen name="Login" component={Login} options={ { headerShown: false} }/>
+ <RootStack.Screen name="Registration" component={Registration} />
+ </RootStack.Navigator>
+ );
+};
+
+export default Routes;
diff --git a/src/routes/index.ts b/src/routes/index.ts
new file mode 100644
index 00000000..cfa05fcb
--- /dev/null
+++ b/src/routes/index.ts
@@ -0,0 +1,2 @@
+export {default} from './Routes';
+export * from './Routes';
diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx
new file mode 100644
index 00000000..672fd035
--- /dev/null
+++ b/src/screens/Login.tsx
@@ -0,0 +1,234 @@
+import React, { useRef } from 'react';
+import {RouteProp} from '@react-navigation/native';
+import {StackNavigationProp} from '@react-navigation/stack';
+import {
+ View,
+ Text,
+ Alert,
+ StatusBar,
+ Image,
+ TextInput,
+ TouchableOpacity,
+ StyleSheet
+} from 'react-native';
+
+import {RootStackParams} from '../routes';
+import LinearGradient from 'react-native-linear-gradient';
+
+type LoginScreenRouteProp = RouteProp<RootStackParams, 'Login'>;
+type LoginScreenNavigationProp = StackNavigationProp<RootStackParams, 'Login'>;
+
+interface LoginProps {
+ route: LoginScreenRouteProp;
+ navigation: LoginScreenNavigationProp;
+}
+const Login = ({navigation}: LoginProps) => {
+ const passwordInput = useRef();
+ const [data, setData] = React.useState({
+ username: '',
+ password: '',
+ isValidUser: true,
+ isValidPassword: true,
+ })
+
+ /*
+ Updates the state of username. Also verifies the input of the Username field.
+ */
+ const handleUsernameUpdate = (val:string) => {
+ var 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) => {
+ var 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}?`)
+ navigation.navigate('Registration')
+ }
+ }
+
+ return (
+ <>
+ <StatusBar
+ barStyle='light-content'
+ />
+ <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}
+ />
+ <TextInput
+ accessibilityLabel="Username text entry box"
+ accessibilityHint="Enter your tagg username here"
+ style={styles.credentials}
+ placeholder="Username"
+ placeholderTextColor='#FFFFFF'
+ autoCompleteType='username'
+ textContentType='username'
+ returnKeyType='next'
+ keyboardType='ascii-capable'
+ onChangeText={user => handleUsernameUpdate(user)}
+ defaultValue={data.username}
+ onSubmitEditing={() => {passwordInput.current.focus()}}
+ blurOnSubmit={false}
+ />
+ { data.isValidUser ? null :
+ <Text style={styles.invalidCredentials}>Username must be at least 6 characters long.</Text>
+ }
+ <TextInput
+ accessibilityLabel="Password text entry box"
+ accessibilityHint="Enter your tagg password here"
+ style={styles.credentials}
+ placeholder="Password"
+ placeholderTextColor='#FFFFFF'
+ autoCompleteType='password'
+ textContentType='password'
+ returnKeyType='go'
+ onChangeText={pass => handlePasswordUpdate(pass)}
+ defaultValue={data.password}
+ onSubmitEditing={() => handleLogin()}
+ ref={passwordInput}
+ secureTextEntry={true}
+ />
+ { data.isValidPassword ? null :
+ <Text style={styles.invalidCredentials}>Password must be at least 8 characters long.</Text>
+ }
+ <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={() => Alert.alert("I get the tagg flip it and tumble it.")}>Get started!</Text>
+ </Text>
+ </LinearGradient>
+ </View>
+ </>
+ // <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>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'transparent',
+ },
+ linearGradient: {
+ flex: 1,
+ alignItems: 'center',
+ },
+ logo: {
+ top: 165,
+ width: 215,
+ height: 149,
+ },
+ credentials: {
+ top: 190,
+ width: 248,
+ height: 40,
+ fontSize: 20,
+ color: '#FFFFFF',
+ borderColor: '#FFFDFD',
+ borderWidth: 2,
+ borderRadius: 20,
+ paddingLeft: 13,
+ marginVertical: 15,
+ },
+ 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; \ No newline at end of file
diff --git a/src/screens/Registration.tsx b/src/screens/Registration.tsx
new file mode 100644
index 00000000..44658591
--- /dev/null
+++ b/src/screens/Registration.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import {View, Text} 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>
+ );
+};
+
+export default Registration;
diff --git a/src/screens/index.ts b/src/screens/index.ts
new file mode 100644
index 00000000..60b26b4c
--- /dev/null
+++ b/src/screens/index.ts
@@ -0,0 +1,2 @@
+export {default as Login} from './Login';
+export {default as Registration} from './Registration';