SignUp and login

master
Netcup Gituser 2023-12-01 22:00:36 +01:00
parent 7d3f457f0b
commit 1c232e86b7
14 changed files with 759 additions and 56 deletions

BIN
src/assets/apple-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/google-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 KiB

After

Width:  |  Height:  |  Size: 227 KiB

View File

@ -0,0 +1,92 @@
import {View} from 'react-native';
import {
Image,
ImageSourcePropType,
ImageStyle,
StyleProp,
Text,
TouchableOpacity,
ViewStyle,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
interface MyIconButtonProps {
image: ImageSourcePropType;
imageStyle?: StyleProp<ImageStyle>;
text: string;
}
export function MyIconButton({image, imageStyle, text}: MyIconButtonProps) {
return (
<TouchableOpacity
style={{
backgroundColor: '#26263f',
alignItems: 'center',
padding: 10,
flexDirection: 'row',
borderRadius: 10,
}}>
<Image source={image} style={imageStyle} />
<Text
style={{
flex: 1,
textAlign: 'center',
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
}}>
{text}
</Text>
</TouchableOpacity>
);
}
interface MyButtonProps {
style?: StyleProp<ViewStyle>;
type: 'primary' | 'secondary';
text: string;
onPress?: () => void;
}
export function MyButton({style, type, text, onPress}: MyButtonProps) {
const ButtonText = () => (
<Text style={{color: '#fff', fontSize: 18, fontWeight: 'bold'}}>
{text}
</Text>
);
return (
<TouchableOpacity
style={[
style,
{
borderRadius: 10,
},
]}
onPress={onPress}>
{type === 'primary' ? (
<LinearGradient
colors={['#931278', '#6030da']}
start={{x: 0, y: 1}}
end={{x: 1, y: 0}}
style={{
alignItems: 'center',
padding: 10,
borderRadius: 10,
}}>
<ButtonText />
</LinearGradient>
) : (
<View
style={{
backgroundColor: '#37375c',
alignItems: 'center',
padding: 10,
borderRadius: 10,
}}>
<ButtonText />
</View>
)}
</TouchableOpacity>
);
}

View File

@ -0,0 +1,16 @@
import {StyleProp, Text, TextStyle} from 'react-native';
import {TouchableOpacity} from 'react-native';
interface MyClickableTextProps {
textStyle?: StyleProp<TextStyle>;
text: string;
onPress?: () => void;
}
export function MyClickableText({textStyle, text, onPress}: MyClickableTextProps) {
return (
<TouchableOpacity onPress={onPress}>
<Text style={textStyle}>{text}</Text>
</TouchableOpacity>
);
}

View File

@ -0,0 +1,19 @@
import {Text, View} from 'react-native';
export function MyDivider() {
return <View style={{flex: 1, height: 1, backgroundColor: '#3b3b60'}} />;
}
interface MyDividerWithTextProps {
text?: string;
}
export function MyDividerWithText({text}: MyDividerWithTextProps) {
return (
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<MyDivider />
<Text style={{color: '#909090', marginHorizontal: 10}}>{text}</Text>
<MyDivider />
</View>
);
}

12
src/components/MyIcon.tsx Normal file
View File

@ -0,0 +1,12 @@
import {ColorValue} from 'react-native';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
interface MyIconProps {
name: string;
size?: number | undefined;
color?: number | ColorValue | undefined
}
export function MyIcon({name, size, color}: MyIconProps) {
return <MaterialIcon name={name} size={size} color={color} />;
}

View File

@ -0,0 +1,39 @@
import {KeyboardTypeOptions, Text, TextInput, View} from 'react-native';
import {MyIcon} from './MyIcon';
interface MyIconInputProps {
text: string;
iconName: string;
keyboardType?: KeyboardTypeOptions;
secureTextEntry?: boolean | undefined;
}
export function MyIconInput({text, iconName, keyboardType, secureTextEntry}: MyIconInputProps) {
return (
<View
style={{
backgroundColor: '#26263f',
flexDirection: 'row',
alignItems: 'center',
borderRadius: 10,
}}>
<View style={{marginLeft: 12}}>
<MyIcon name={iconName} size={32} />
</View>
<View style={{flex: 1, margin: 12, gap: 2}}>
<Text>{text}</Text>
<TextInput
style={{
backgroundColor: '#212137',
height: 40,
borderRadius: 10,
paddingLeft: 10,
}}
keyboardType={keyboardType}
secureTextEntry={secureTextEntry}
/>
</View>
</View>
);
}

View File

@ -0,0 +1,13 @@
import {ReactNode} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
interface MyScreenContainerProps {
children: ReactNode;
style?: StyleProp<ViewStyle>;
}
export function MyScreenContainer({children, style}: MyScreenContainerProps) {
return (
<View style={[style, {paddingLeft: 20, paddingRight: 20}]}>{children}</View>
);
}

11
src/helper/linking.ts Normal file
View File

@ -0,0 +1,11 @@
import {Linking} from 'react-native';
export function OpenURL(url: string) {
const supported = Linking.canOpenURL(url);
if (!supported) {
console.log("Can't handle url: " + url);
} else {
return Linking.openURL(url);
}
}

View File

@ -4,8 +4,6 @@ import {
BottomTabBarProps,
createBottomTabNavigator,
} from '@react-navigation/bottom-tabs';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import CalendarTab from './tabs/main/CalendarTab';
import ChatsTab from './tabs/main/ChatsTab';
import MapTab from './tabs/main/MapTab';
@ -15,20 +13,30 @@ import ProfileTab, {
import {FadeInView, SlideFromLeftView} from '@helper/animations';
import {Text, TouchableOpacity, View} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import {
RegistrationScreenAnim,
RegistrationStackNavigatorParamList,
} from './registration/registration';
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
import { MyIcon } from '@components/MyIcon';
export type RootStackNavigatorParamList = {
Home: NavigatorScreenParams<HomeStackNavigatorParamList>;
//Register: NavigatorScreenParams<LoginStackNavigatorParamList>;
Registration: NavigatorScreenParams<RegistrationStackNavigatorParamList>;
};
export type RootScreenNavigationProp =
NativeStackNavigationProp<RootStackNavigatorParamList>;
export default function Navigation() {
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
}}
initialRouteName="Registration">
<Stack.Screen name="Home" component={HomeStack} />
<Stack.Screen name="Register" component={RegisterStack} />
<Stack.Screen name="Registration" component={RegistrationScreenAnim} />
</Stack.Navigator>
);
}
@ -143,11 +151,9 @@ function CustomTabBar(props: BottomTabBarProps) {
width: '100%',
alignItems: 'center',
}}>
<MaterialIcon
name={tabBarIcons[index]}
<MyIcon name={tabBarIcons[index]}
color={isFocused ? '#fff' : '#ccc'}
size={20}
/>
size={20} />
{isFocused && (
<Text style={{color: '#fff'}}>{label.toString()}</Text>
)}
@ -159,6 +165,8 @@ function CustomTabBar(props: BottomTabBarProps) {
);
}
const Tab = createBottomTabNavigator<HomeStackNavigatorParamList>();
function HomeStack() {
return (
<Tab.Navigator
@ -171,9 +179,3 @@ function HomeStack() {
</Tab.Navigator>
);
}
function RegisterStack() {
return <></>;
}
const Tab = createBottomTabNavigator<HomeStackNavigatorParamList>();

View File

@ -0,0 +1,518 @@
import {MyScreenContainer} from '@components/MyScreenContainer';
import {SlideFromLeftView} from '@helper/animations';
import {RootScreenNavigationProp} from '@navigation/navigation';
import {useNavigation} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationOptions,
} from '@react-navigation/native-stack';
import {Image, StyleSheet, Text, TextInput, View} from 'react-native';
import GoogleLogo from '@assets/google-logo.png';
import AppleLogo from '@assets/apple-logo.png';
import Logo from '@assets/logo.png';
import {MyDividerWithText} from '@components/MyDivider';
import {MyButton, MyIconButton} from '@components/MyButton';
import {MyClickableText} from '@components/MyClickableText';
import {OpenURL} from '@helper/linking';
import {Constants} from '@utils/utils';
import {MyIconInput} from '@components/MyInput';
import {useRef, useState} from 'react';
import {KeyboardAvoidingView} from 'react-native';
export type RegistrationStackNavigatorParamList = {
SignUpPreview: undefined;
SignUpStepUsername: undefined;
SignUpStepPhoneNumber: undefined;
SignUpStepVerifyPhoneNumber: undefined;
SignUpStepPassword: undefined;
SignUpStepAccountName: undefined;
LoginPreview: undefined;
Login: undefined;
};
const RegistrationStack =
createNativeStackNavigator<RegistrationStackNavigatorParamList>();
export function RegistrationScreenAnim(props: any) {
return (
<SlideFromLeftView>
<RegistrationScreen {...props} />
</SlideFromLeftView>
);
}
const headerStyle: NativeStackNavigationOptions = {
headerShown: true,
headerStyle: {backgroundColor: '#212137'},
headerTitleAlign: 'center',
headerTitle: '',
};
export function RegistrationScreen() {
return (
<RegistrationStack.Navigator screenOptions={{headerShown: false}}>
<RegistrationStack.Screen
name="SignUpPreview"
component={SignUpPreview}
/>
<RegistrationStack.Screen name="LoginPreview" component={LoginPreview} />
<RegistrationStack.Screen
name="Login"
component={Login}
options={{
headerShown: true,
headerStyle: {backgroundColor: '#212137'},
headerTitleAlign: 'center',
}}
/>
<RegistrationStack.Screen
name="SignUpStepUsername"
component={SignUpStepUsername}
options={{...headerStyle}}
/>
<RegistrationStack.Screen
name="SignUpStepPhoneNumber"
component={SignUpStepPhoneNumber}
options={{...headerStyle}}
/>
<RegistrationStack.Screen
name="SignUpStepVerifyPhoneNumber"
component={SignUpStepVerifyPhoneNumber}
options={{...headerStyle}}
/>
<RegistrationStack.Screen
name="SignUpStepPassword"
component={SignUpStepPassword}
options={{...headerStyle}}
/>
<RegistrationStack.Screen
name="SignUpStepAccountName"
component={SignUpStepAccountName}
options={{...headerStyle}}
/>
</RegistrationStack.Navigator>
);
}
function SignUpPreview() {
return <RegistrationPreview type="signup" />;
}
function LoginPreview() {
return <RegistrationPreview type="login" />;
}
function ThirdAuthButtons() {
return (
<>
<MyIconButton
image={GoogleLogo}
imageStyle={{left: 2, height: 35, width: 35}}
text="Continue with Google"
/>
<MyIconButton
image={AppleLogo}
imageStyle={{height: 40, width: 40}}
text="Continue with Apple"
/>
</>
);
}
function RegistrationPreview({type}: {type: 'login' | 'signup'}) {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<View style={{flex: 1, marginTop: 10, alignItems: 'center'}}>
<Image source={Logo} style={{height: 60, width: 60}} />
</View>
<View style={{flex: 2, justifyContent: 'center'}}>
<Text
style={{
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: '#fff',
padding: 20,
}}>
{type === 'login'
? 'Time to get to know the next party'
: 'Find the next party near you'}
</Text>
</View>
<View
style={{
gap: 10,
flex: 3,
justifyContent: 'flex-end',
marginBottom: 20,
}}>
<ThirdAuthButtons />
<MyDividerWithText text="or" />
<MyButton
type="secondary"
text={type === 'login' ? 'Login' : 'Create account'}
style={{marginBottom: 20}}
onPress={() => {
navigation.navigate(
'Registration',
type === 'login'
? {screen: 'Login'}
: {screen: 'SignUpStepUsername'},
);
}}
/>
{type === 'login' && (
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
<Text>By signing up, you agree to our </Text>
<MyClickableText
text="Terms"
textStyle={{color: '#6030da'}}
onPress={() => OpenURL(Constants.REGISTRATION.TERMS_URL)}
/>
<Text>, </Text>
<MyClickableText
text="Privacy Policy"
textStyle={{color: '#6030da'}}
onPress={() => OpenURL(Constants.REGISTRATION.PRIVACY_POLICY_URL)}
/>
<Text> and </Text>
<MyClickableText
text="Cookie Use"
textStyle={{color: '#6030da'}}
onPress={() => OpenURL(Constants.REGISTRATION.COOKIE_USE_URL)}
/>
<Text>.</Text>
</View>
)}
<View style={{flexDirection: 'row'}}>
{type === 'login' ? (
<Text>Don't have an account?</Text>
) : (
<Text>Have already an account?</Text>
)}
<MyClickableText
text={type === 'login' ? 'Sign up' : 'Login'}
textStyle={{left: 2, color: '#931278', fontWeight: 'bold'}}
onPress={() => {
navigation.navigate(
'Registration',
type === 'login'
? {screen: 'SignUpPreview'}
: {screen: 'LoginPreview'},
);
}}
/>
</View>
</View>
</MyScreenContainer>
);
}
function ContentContainer({children}: {children: React.ReactNode}) {
return (
<>
<View style={{flex: 1}} />
<View style={{flex: 8}}>{children}</View>
</>
);
}
function Title({text}: {text: string}) {
return (
<Text
style={{
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: '#fff',
padding: 20,
}}>
{text}
</Text>
);
}
function Login() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="Welcome back!" />
<View style={{gap: 12}}>
<MyIconInput text="PHONE NUMBER OR ACCOUNT NAME" iconName="person" />
<MyIconInput text="PASSWORD" iconName="lock" secureTextEntry />
<MyButton
type="secondary"
text={'Login'}
style={{marginBottom: 20}}
onPress={() => {
navigation.navigate('Home');
}}
/>
</View>
</ContentContainer>
</MyScreenContainer>
);
}
function SignUpStepUsername() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="Let's get started, what's your name?" />
<Text>The name will be displayed on your profil overview</Text>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput text="Username" iconName="person" />
<MyButton
type="secondary"
text={'Next'}
style={{marginBottom: 20}}
onPress={() => {
navigation.navigate('Registration', {
screen: 'SignUpStepPhoneNumber',
});
}}
/>
</View>
</ContentContainer>
</MyScreenContainer>
);
}
function SignUpStepPhoneNumber() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="Create your account using your phone number" />
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput
text="Phone"
iconName="phone-iphone"
keyboardType="phone-pad"
/>
<MyButton
type="secondary"
text={'Next'}
style={{marginBottom: 20}}
onPress={() => {
navigation.navigate('Registration', {
screen: 'SignUpStepVerifyPhoneNumber',
});
}}
/>
</View>
</ContentContainer>
</MyScreenContainer>
);
}
interface ConfirmationCodeInputProps {
// Zusätzliche Props für das ConfirmationCodeInput-Komponente hier hinzufügen
}
const ConfirmationCodeInput: React.FC<ConfirmationCodeInputProps> = () => {
const [confirmationCode, setConfirmationCode] = useState([
'',
'',
'',
'',
'',
'',
]);
const inputRefs = useRef<TextInput[]>([]);
const focusInput = (index: number) => {
if (inputRefs.current[index]) {
inputRefs.current[index].focus();
}
};
const handleTextChange = (text: string, index: number) => {
const newConfirmationCode = [...confirmationCode];
newConfirmationCode[index] = text;
// Fokus zum nächsten Input verschieben, wenn der aktuelle Input nicht leer ist
if (text !== '' && index < confirmationCode.length - 1) {
focusInput(index + 1);
}
setConfirmationCode(newConfirmationCode);
};
return (
<View style={styles.container}>
{confirmationCode.map((digit, index) => (
<TextInput
key={index}
ref={ref => (inputRefs.current[index] = ref!)}
style={styles.input}
keyboardType="number-pad"
maxLength={1}
onChangeText={text => handleTextChange(text, index)}
value={digit}
/>
))}
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'center',
},
input: {
height: 40,
width: 40,
borderColor: 'gray',
borderWidth: 1,
textAlign: 'center',
margin: 5,
borderRadius: 5,
},
});
function SignUpStepVerifyPhoneNumber() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="We sent you a code" />
<Text>Enter it below to verify +49 15** ******43 </Text>
<KeyboardAvoidingView style={{gap: 12, marginTop: 20}}>
<ConfirmationCodeInput />
<MyButton
type="secondary"
text={'Next'}
style={{marginBottom: 2}}
onPress={() => {
navigation.navigate('Registration', {
screen: 'SignUpStepPassword',
});
}}
/>
<View style={{alignItems: 'center'}}>
<MyClickableText text="Resend code" />
</View>
</KeyboardAvoidingView>
</ContentContainer>
</MyScreenContainer>
);
}
function SignUpStepPassword() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="You'll need a password" />
<Text>Make sure it's 8 characters or more</Text>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput text="PASSWORD" iconName="lock" secureTextEntry />
<MyButton
type="secondary"
text={'Next'}
style={{marginBottom: 2}}
onPress={() => {
navigation.navigate('Registration', {
screen: 'SignUpStepAccountName',
});
}}
/>
</View>
</ContentContainer>
</MyScreenContainer>
);
}
function SignUpStepAccountName() {
const navigation = useNavigation<RootScreenNavigationProp>();
return (
<MyScreenContainer
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: '#212137',
}}>
<ContentContainer>
<Title text="Next, create your account name" />
<Text>
Your account name is unique and is used for friends to find you.
</Text>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput text="ACCOUNT NAME" iconName="person" />
<MyButton
type="primary"
text={'Get Started'}
style={{marginBottom: 2}}
onPress={() => {
navigation.navigate('Home');
}}
/>
</View>
</ContentContainer>
</MyScreenContainer>
);
}

7
src/utils/utils.ts Normal file
View File

@ -0,0 +1,7 @@
export const Constants = {
REGISTRATION: {
TERMS_URL: 'https://www.google.com',
PRIVACY_POLICY_URL: 'https://www.google.com',
COOKIE_USE_URL: 'https://www.google.com',
},
};

View File

@ -6,45 +6,19 @@
"baseUrl": ".",
"target": "ESNext",
"paths": {
"@redux/*": [
"src/redux/*"
],
"@lang/*": [
"src/lang/*"
],
"@pages/*": [
"src/pages/*"
],
"@api/*": [
"src/api/*"
],
"@assets/*": [
"src/assets/*"
],
"@components/*": [
"src/components/*"
],
"@scenes/*": [
"src/scenes/*"
],
"@theme/*": [
"src/theme/*"
],
"@utils/*": [
"src/utils/*"
],
"@navigation/*": [
"src/navigation/*"
],
"@configs/*": [
"src/configs/*"
],
"@helper/*": [
"src/helper/*"
],
"@user/*": [
"src/user/*"
],
},
"@redux/*": ["src/redux/*"],
"@lang/*": ["src/lang/*"],
"@pages/*": ["src/pages/*"],
"@api/*": ["src/api/*"],
"@assets/*": ["src/assets/*"],
"@components/*": ["src/components/*"],
"@scenes/*": ["src/scenes/*"],
"@theme/*": ["src/theme/*"],
"@utils/*": ["src/utils/*"],
"@navigation/*": ["src/navigation/*"],
"@configs/*": ["src/configs/*"],
"@helper/*": ["src/helper/*"],
"@user/*": ["src/user/*"]
}
}
}
}