aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/CenteredView.tsx25
-rw-r--r--src/components/common/LoginInput.tsx105
-rw-r--r--src/components/common/OverlayView.tsx19
-rw-r--r--src/components/common/RadioCheckbox.tsx40
-rw-r--r--src/components/common/TaggInput.tsx62
-rw-r--r--src/components/common/index.ts4
-rw-r--r--src/components/index.ts2
-rw-r--r--src/components/onboarding/ArrowButton.tsx23
-rw-r--r--src/components/onboarding/Background.tsx44
-rw-r--r--src/components/onboarding/RegistrationWizard.tsx47
-rw-r--r--src/components/onboarding/TermsConditions.tsx140
-rw-r--r--src/components/onboarding/index.ts4
12 files changed, 410 insertions, 105 deletions
diff --git a/src/components/common/CenteredView.tsx b/src/components/common/CenteredView.tsx
new file mode 100644
index 00000000..1c5ed399
--- /dev/null
+++ b/src/components/common/CenteredView.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {View, StyleSheet, ViewProps} from 'react-native';
+
+interface CenteredViewProps extends ViewProps {}
+/**
+ * A centered view that grows to its parents size.
+ * @param children - children of this component.
+ */
+const CenteredView: React.FC<CenteredViewProps> = (props) => {
+ return (
+ <View style={styles.centeredView} {...props}>
+ {props.children}
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ centeredView: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
+
+export default CenteredView;
diff --git a/src/components/common/LoginInput.tsx b/src/components/common/LoginInput.tsx
deleted file mode 100644
index 2a1768a7..00000000
--- a/src/components/common/LoginInput.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import {TextInput, StyleSheet} from 'react-native';
-import * as Animatable from 'react-native-animatable';
-
-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
- }
- autoCapitalize="none"
- 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.attempt_submit && !props.isValid && (
- <Animatable.Text
- animation="shake"
- duration={500}
- style={styles.invalidCredentials}>
- {props.validationWarning}
- </Animatable.Text>
- )}
- </>
- );
-};
-
-const styles = StyleSheet.create({
- credentials: {
- top: 175,
- width: 248,
- height: 40,
- fontSize: 20,
- color: '#FFFFFF',
- borderColor: '#FFFDFD',
- borderWidth: 2,
- borderRadius: 20,
- paddingLeft: 13,
- marginVertical: 15,
- },
- invalidCredentials: {
- top: 165,
- color: '#F4DDFF',
- paddingHorizontal: 30,
- textAlign: 'center',
- },
-});
-
-interface LoginInputProps {
- type: string;
- isUsername?: boolean;
- isPassword?: boolean;
- onChangeText: (input: string) => void;
- onSubmitEditing?: () => void;
- attempt_submit?: boolean;
- input_ref?: object;
- isValid?: boolean;
- validationWarning?: string;
-}
-
-export default LoginInput;
diff --git a/src/components/common/OverlayView.tsx b/src/components/common/OverlayView.tsx
new file mode 100644
index 00000000..f0660614
--- /dev/null
+++ b/src/components/common/OverlayView.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import {View, StyleSheet} from 'react-native';
+
+/**
+ * A blurred & darkened view that grows to its parents size. Designed to be used with overlaid components.
+ * @param children - children of this component.
+ */
+const OverlayView: React.FC = ({children}) => {
+ return <View style={styles.overlayView}>{children}</View>;
+};
+
+const styles = StyleSheet.create({
+ overlayView: {
+ flex: 1,
+ backgroundColor: '#00000080',
+ },
+});
+
+export default OverlayView;
diff --git a/src/components/common/RadioCheckbox.tsx b/src/components/common/RadioCheckbox.tsx
new file mode 100644
index 00000000..33d50527
--- /dev/null
+++ b/src/components/common/RadioCheckbox.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import {
+ View,
+ StyleSheet,
+ TouchableOpacity,
+ TouchableOpacityProps,
+} from 'react-native';
+
+interface RadioCheckboxProps extends TouchableOpacityProps {
+ checked: boolean;
+}
+const RadioCheckbox: React.FC<RadioCheckboxProps> = (props) => {
+ return (
+ <TouchableOpacity {...props}>
+ <View style={styles.outerCircle}>
+ {props.checked && <View style={styles.innerCircle} />}
+ </View>
+ </TouchableOpacity>
+ );
+};
+
+const styles = StyleSheet.create({
+ outerCircle: {
+ width: 23,
+ height: 23,
+ borderRadius: 11.5,
+ borderWidth: 1,
+ borderColor: '#fff',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ innerCircle: {
+ width: 17,
+ height: 17,
+ borderRadius: 8.5,
+ backgroundColor: '#04ffff',
+ },
+});
+
+export default RadioCheckbox;
diff --git a/src/components/common/TaggInput.tsx b/src/components/common/TaggInput.tsx
new file mode 100644
index 00000000..fe11d4f0
--- /dev/null
+++ b/src/components/common/TaggInput.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import {View, TextInput, StyleSheet, TextInputProps} from 'react-native';
+import * as Animatable from 'react-native-animatable';
+
+interface TaggInputProps extends TextInputProps {
+ valid?: boolean;
+ invalidWarning?: string;
+ attemptedSubmit?: boolean;
+ width?: number | string;
+}
+/**
+ * An input component that receives all props a normal TextInput component does. TaggInput components grow to 60% of their parent's width by default, but this can be set using the `width` prop.
+ */
+const TaggInput = React.forwardRef((props: TaggInputProps, ref: any) => {
+ return (
+ <View style={styles.container}>
+ <TextInput
+ style={[{width: props.width}, styles.input]}
+ placeholderTextColor="#ddd"
+ clearButtonMode="while-editing"
+ ref={ref}
+ {...props}
+ />
+ {props.attemptedSubmit && !props.valid && (
+ <Animatable.Text
+ animation="shake"
+ duration={500}
+ style={styles.warning}>
+ {props.invalidWarning}
+ </Animatable.Text>
+ )}
+ </View>
+ );
+});
+
+const styles = StyleSheet.create({
+ container: {
+ width: '100%',
+ alignItems: 'center',
+ marginVertical: 11,
+ },
+ input: {
+ minWidth: '60%',
+ height: 40,
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#fff',
+ borderColor: '#fffdfd',
+ borderWidth: 2,
+ borderRadius: 20,
+ paddingLeft: 13,
+ },
+ warning: {
+ fontSize: 14,
+ marginTop: 5,
+ color: '#f4ddff',
+ maxWidth: 350,
+ textAlign: 'center',
+ },
+});
+
+export default TaggInput;
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
new file mode 100644
index 00000000..b7041b6d
--- /dev/null
+++ b/src/components/common/index.ts
@@ -0,0 +1,4 @@
+export {default as CenteredView} from './CenteredView';
+export {default as OverlayView} from './OverlayView';
+export {default as RadioCheckbox} from './RadioCheckbox';
+export {default as TaggInput} from './TaggInput';
diff --git a/src/components/index.ts b/src/components/index.ts
new file mode 100644
index 00000000..724b14ac
--- /dev/null
+++ b/src/components/index.ts
@@ -0,0 +1,2 @@
+export * from './common';
+export * from './onboarding';
diff --git a/src/components/onboarding/ArrowButton.tsx b/src/components/onboarding/ArrowButton.tsx
new file mode 100644
index 00000000..bf07c6ac
--- /dev/null
+++ b/src/components/onboarding/ArrowButton.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import {Image, TouchableOpacity, TouchableOpacityProps} from 'react-native';
+
+interface ArrowButtonProps extends TouchableOpacityProps {
+ direction: 'forward' | 'backward';
+ disabled?: boolean;
+}
+const ArrowButton: React.FC<ArrowButtonProps> = (props: ArrowButtonProps) => {
+ const arrow =
+ props.direction === 'forward'
+ ? props.disabled
+ ? require('../../assets/images/arrow-forward-disabled.png')
+ : require('../../assets/images/arrow-forward-enabled.png')
+ : require('../../assets/images/arrow-backward.png');
+
+ return (
+ <TouchableOpacity {...props}>
+ <Image source={arrow} />
+ </TouchableOpacity>
+ );
+};
+
+export default ArrowButton;
diff --git a/src/components/onboarding/Background.tsx b/src/components/onboarding/Background.tsx
new file mode 100644
index 00000000..98082022
--- /dev/null
+++ b/src/components/onboarding/Background.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import LinearGradient from 'react-native-linear-gradient';
+import {
+ StyleSheet,
+ TouchableWithoutFeedback,
+ Keyboard,
+ ViewProps,
+ KeyboardAvoidingView,
+ View,
+ Platform,
+} from 'react-native';
+
+interface BackgroundProps extends ViewProps {}
+const Background: React.FC<BackgroundProps> = (props) => {
+ return (
+ <LinearGradient
+ colors={['#9F00FF', '#27EAE9']}
+ useAngle={true}
+ angle={154.72}
+ angleCenter={{x: 0.5, y: 0.5}}
+ style={styles.container}>
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ style={styles.container}>
+ <TouchableWithoutFeedback accessible={false} onPress={Keyboard.dismiss}>
+ <View style={[styles.container, styles.view]} {...props}>
+ {props.children}
+ </View>
+ </TouchableWithoutFeedback>
+ </KeyboardAvoidingView>
+ </LinearGradient>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ view: {
+ alignItems: 'center',
+ },
+});
+
+export default Background;
diff --git a/src/components/onboarding/RegistrationWizard.tsx b/src/components/onboarding/RegistrationWizard.tsx
new file mode 100644
index 00000000..5d7e6ee2
--- /dev/null
+++ b/src/components/onboarding/RegistrationWizard.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import {View, StyleSheet, ViewProps} from 'react-native';
+
+interface RegistrationWizardProps extends ViewProps {
+ step: 'one' | 'two' | 'three';
+}
+
+const RegistrationWizard = (props: RegistrationWizardProps) => {
+ const stepStyle = styles.step;
+ const stepActiveStyle = [styles.step, styles.stepActive];
+ return (
+ <View {...props}>
+ <View style={styles.container}>
+ <View style={props.step === 'one' ? stepActiveStyle : stepStyle} />
+ <View style={styles.progress} />
+ <View style={props.step === 'two' ? stepActiveStyle : stepStyle} />
+ <View style={styles.progress} />
+ <View style={props.step === 'three' ? stepActiveStyle : stepStyle} />
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ step: {
+ height: 18,
+ width: 18,
+ borderRadius: 9,
+ borderWidth: 2,
+ borderColor: '#e1f0ff',
+ },
+ stepActive: {
+ backgroundColor: '#e1f0ff',
+ },
+ progress: {
+ width: '30%',
+ height: 2,
+ backgroundColor: '#e1f0ff',
+ },
+});
+
+export default RegistrationWizard;
diff --git a/src/components/onboarding/TermsConditions.tsx b/src/components/onboarding/TermsConditions.tsx
new file mode 100644
index 00000000..5af1b972
--- /dev/null
+++ b/src/components/onboarding/TermsConditions.tsx
@@ -0,0 +1,140 @@
+import React, {useState} from 'react';
+import {
+ Modal,
+ StyleSheet,
+ View,
+ Text,
+ Button,
+ TouchableOpacity,
+ ScrollView,
+ ViewProps,
+} from 'react-native';
+
+import {RadioCheckbox, CenteredView, OverlayView} from '../common';
+import {dummyTermsConditions} from '../../constants';
+
+interface TermsConditionsProps extends ViewProps {
+ accepted: boolean;
+ onChange: (accepted: boolean) => void;
+}
+const TermsConditions: React.FC<TermsConditionsProps> = (props) => {
+ // boolean representing if modal is visible
+ const [modalVisible, setModalVisible] = useState(false);
+ // destructure props
+ const {accepted, onChange} = props;
+ /**
+ * Hides the modal.
+ */
+ const hideModal = (): void => {
+ if (modalVisible) {
+ setModalVisible(false);
+ }
+ };
+ /**
+ * Sets `accepted` to `true` and hides the modal.
+ */
+ const handleAccept = (): void => {
+ onChange(true);
+ hideModal();
+ };
+ /**
+ * Toggles the value of `accepted`.
+ */
+ const toggleAccepted = (): void => {
+ onChange(!accepted);
+ };
+
+ return (
+ <View {...props}>
+ <View style={styles.body}>
+ <RadioCheckbox checked={accepted} onPress={toggleAccepted} />
+ <View style={styles.bodyPrompt}>
+ <Text style={styles.bodyPromptText}>I accept the </Text>
+ <TouchableOpacity onPress={() => setModalVisible(true)}>
+ <Text
+ style={[styles.bodyPromptText, styles.bodyPromptTextUnderline]}>
+ terms and conditions.
+ </Text>
+ </TouchableOpacity>
+ </View>
+ </View>
+ <Modal visible={modalVisible} transparent={true} animationType="fade">
+ <OverlayView>
+ <CenteredView>
+ <View style={styles.modalView}>
+ <ScrollView
+ style={styles.modalScrollView}
+ contentContainerStyle={styles.modalScrollViewContent}>
+ <Text style={styles.tcHeader}>Terms and Conditions</Text>
+ <Text>{dummyTermsConditions}</Text>
+ </ScrollView>
+ <View style={styles.modalActions}>
+ <Button title="Accept" onPress={handleAccept} />
+ <View style={styles.modalActionsDivider} />
+ <Button title="Close" onPress={hideModal} />
+ </View>
+ </View>
+ </CenteredView>
+ </OverlayView>
+ </Modal>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ body: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ bodyPrompt: {
+ flexDirection: 'row',
+ marginLeft: 10,
+ },
+ bodyPromptText: {
+ fontSize: 16,
+ color: '#fff',
+ },
+ bodyPromptTextUnderline: {
+ textDecorationLine: 'underline',
+ },
+ modalView: {
+ width: '85%',
+ height: '55%',
+ backgroundColor: '#fff',
+ shadowColor: '#000',
+ shadowOpacity: 30,
+ shadowOffset: {width: 0, height: 2},
+ shadowRadius: 5,
+ borderRadius: 8,
+ paddingTop: 30,
+ paddingBottom: 15,
+ paddingHorizontal: 20,
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ },
+ modalScrollViewContent: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalScrollView: {
+ marginBottom: 10,
+ },
+ tcHeader: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ },
+ modalActions: {
+ flexDirection: 'row',
+ justifyContent: 'space-evenly',
+ alignItems: 'center',
+ width: '100%',
+ },
+ modalActionsDivider: {
+ height: '60%',
+ backgroundColor: '#0160Ca',
+ width: 1,
+ },
+});
+
+export default TermsConditions;
diff --git a/src/components/onboarding/index.ts b/src/components/onboarding/index.ts
new file mode 100644
index 00000000..01255c01
--- /dev/null
+++ b/src/components/onboarding/index.ts
@@ -0,0 +1,4 @@
+export {default as ArrowButton} from './ArrowButton';
+export {default as Background} from './Background';
+export {default as RegistrationWizard} from './RegistrationWizard';
+export {default as TermsConditions} from './TermsConditions';