Browse Source

first commit

master
Juni Kim 9 months ago
parent
commit
208efd1f9d
  1. 7
      .gitattributes
  2. 3
      .gitignore
  3. 20
      App.tsx
  4. 6
      README.md
  5. 45
      app.json
  6. 44
      app/_layout.tsx
  7. 82
      app/auth/createpassword.tsx
  8. 140
      app/auth/login.tsx
  9. 35
      app/auth/passwordresetemail.tsx
  10. 51
      app/auth/registeremail.tsx
  11. 35
      app/index.tsx
  12. BIN
      assets/fonts/Axiforma-ExtraBold.otf
  13. BIN
      assets/fonts/Axiforma-ExtraBoldItalic.otf
  14. BIN
      assets/fonts/Pretendard-Black.ttf
  15. BIN
      assets/fonts/Pretendard-Bold.ttf
  16. BIN
      assets/fonts/Pretendard-ExtraBold.ttf
  17. BIN
      assets/fonts/Pretendard-ExtraLight.ttf
  18. BIN
      assets/fonts/Pretendard-Light.ttf
  19. BIN
      assets/fonts/Pretendard-Medium.ttf
  20. BIN
      assets/fonts/Pretendard-Regular.ttf
  21. BIN
      assets/fonts/Pretendard-SemiBold.ttf
  22. BIN
      assets/fonts/Pretendard-Thin.ttf
  23. BIN
      assets/splash2.png
  24. 42
      components/AuthProvider.tsx
  25. 60
      components/Design.ts
  26. 93
      components/EButton.tsx
  27. 100
      components/EIcons.tsx
  28. 17
      components/ELogo.tsx
  29. 40
      components/ENavButtons.tsx
  30. 76
      components/EText.tsx
  31. 113
      components/ETextInput.tsx
  32. 1156
      package-lock.json
  33. 20
      package.json
  34. 5
      tsconfig.json

7
.gitattributes

@ -0,0 +1,7 @@
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.heic filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text

3
.gitignore

@ -33,3 +33,6 @@ yarn-error.*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
/reference
/secrets
/android

20
App.tsx

@ -1,20 +0,0 @@
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

6
README.md

@ -0,0 +1,6 @@
# Essentory Login Mockup
## Firebase Setup
Create the directory `./secrets` and download `google-services.json` and
`GoogleService-Info.plist` into there.

45
app.json

@ -1,30 +1,55 @@
{ {
"expo": { "expo": {
"name": "loginmockup2",
"slug": "loginmockup2",
"name": "loginmockup",
"slug": "loginmockup",
"scheme": "loginmockup",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"userInterfaceStyle": "dark",
"splash": { "splash": {
"image": "./assets/splash.png",
"image": "./assets/splash2.png",
"resizeMode": "contain", "resizeMode": "contain",
"backgroundColor": "#ffffff"
"backgroundColor": "#333333"
}, },
"assetBundlePatterns": [ "assetBundlePatterns": [
"**/*" "**/*"
], ],
"ios": { "ios": {
"supportsTablet": true
"googleServicesFile": "./secrets/GoogleService-Info.plist",
"supportsTablet": true,
"package": "com.v1.client.app.essentory"
}, },
"android": { "android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
"googleServicesFile": "./secrets/google-services.json",
"package": "com.estrclient"
}, },
"web": { "web": {
"favicon": "./assets/favicon.png" "favicon": "./assets/favicon.png"
},
"plugins": [
"@react-native-firebase/app",
"@react-native-firebase/auth",
"@react-native-google-signin/google-signin",
[
"expo-font",
{
"fonts": [
"./assets/fonts/Pretendard-Bold.ttf",
"./assets/fonts/Pretendard-Thin.ttf",
"./assets/fonts/Pretendard-Black.ttf",
"./assets/fonts/Pretendard-Light.ttf",
"./assets/fonts/Pretendard-Medium.ttf",
"./assets/fonts/Pretendard-Regular.ttf",
"./assets/fonts/Pretendard-SemiBold.ttf",
"./assets/fonts/Pretendard-ExtraBold.ttf",
"./assets/fonts/Pretendard-ExtraLight.ttf",
"./assets/fonts/Axiforma-ExtraBold.otf",
"./assets/fonts/Axiforma-ExtraBoldItalic.otf"
]
} }
],
"expo-router"
]
} }
} }

44
app/_layout.tsx

@ -0,0 +1,44 @@
import React from "react";
import { SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar } from "react-native";
import { ScrollView } from "react-native";
import AuthProvider from "@components/AuthProvider";
import * as SplashScreen from 'expo-splash-screen';
import { Slot, Stack } from "expo-router";
SplashScreen.preventAutoHideAsync();
export const unstable_settings = {
initialRouteName: 'auth/login',
}
function App() {
const backgroundStyle = {
backgroundColor: "#333333",
fontFamily: "Pretendard-Regular",
color: "white",
};
SplashScreen.hideAsync();
return (
<SafeAreaView style={{ ...backgroundStyle, height: "100%", flex:1 }}>
<AuthProvider>
<StatusBar
barStyle={"light-content"}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
style={{
paddingHorizontal: 16,
height: "auto",
backgroundColor: "#333333",
}}
>
<Slot />
</ScrollView>
</AuthProvider>
</SafeAreaView>
);
}
export default App;

82
app/auth/createpassword.tsx

@ -0,0 +1,82 @@
import React, { useMemo, useState } from "react";
import { View } from "react-native";
import ETextInput, { ETextInputBorder } from "@components/ETextInput";
import EFullButton from "@components/EButton";
import EText, { ECaptionText, ELabelText, ETitleText } from "@components/EText";
import ENavButtons from "@components/ENavButtons";
// implement logic here later.
export default function CreatePassword(props: any) {
const [password, setPassword] = useState<string>("");
const [validation, setValidation] = useState<string>("");
const [visible, setVisible] = useState<boolean>(true);
const border = useMemo(() => {
if (password.length === 0 && validation.length === 0)
return ETextInputBorder.Nothing;
if (validation !== password) return ETextInputBorder.Fail;
if (validation.length >= 8) return ETextInputBorder.Success;
return ETextInputBorder.Fail;
}, [password, validation]);
return (
<>
<View
style={{
flex: 1,
flexDirection: "column",
}}
>
<ENavButtons back={true} />
<View style={{ flexDirection: "column", marginTop: 0 }}>
<View style={{ marginBottom: 24 }}>
<ETitleText style={{ marginBottom: 8 }}>
</ETitleText>
<EText>
. , 8
</EText>
</View>
<View style={{ rowGap: 8 }}>
<ELabelText></ELabelText>
<ETextInput
border={border}
placeholder=""
value={password}
setValue={setPassword}
passwordHidden={visible}
setPasswordHidden={setVisible}
/>
<ETextInput
border={border}
placeholder="비밀번호 다시 입력하기"
value={validation}
setValue={setValidation}
passwordHidden={visible}
setPasswordHidden={setVisible}
/>
{border === ETextInputBorder.Nothing && (
<ECaptionText>
/ 8
</ECaptionText>
)}
{border === ETextInputBorder.Fail && (
<>
<ECaptionText error={true}>
/ 8
</ECaptionText>
<ECaptionText error={true}>
</ECaptionText>
</>
)}
</View>
</View>
<EFullButton
style={{ marginTop: 24 }}
text="비밀번호 설정하기"
active={border == ETextInputBorder.Success}
/>
</View>
</>
);
}

140
app/auth/login.tsx

@ -0,0 +1,140 @@
import React, { useState } from "react";
import { Button, Text, View } from "react-native";
import ELogo from "@components/ELogo";
import ETextInput from "@components/ETextInput";
import EFullButton, { EIconButton } from "@components/EButton";
import { ColorScheme } from "@components/Design";
import { AppleIcon, GoogleIcon } from "@components/EIcons";
import { ECaptionText, ELabelText } from "@components/EText";
import auth from "@react-native-firebase/auth";
import { useRouter } from "expo-router";
import { GoogleSignin } from "@react-native-google-signin/google-signin";
import { useAuthState } from "@components/AuthProvider";
GoogleSignin.configure({
webClientId:
"406674810515-l8a40f83kl5hnvflvcjrabmjmbvda7am.apps.googleusercontent.com",
});
export default function LoginScreen(props: any) {
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [error, setError] = useState(false);
const router = useRouter();
const [loginAttempting, setLoginAttempting] = useState(false);
const signInWithEmail = async () => {
setLoginAttempting(true);
try {
await auth().signInWithEmailAndPassword(email, password);
router.replace("/");
} catch (e) {
setError(true);
}
setLoginAttempting(false);
};
const signInWithGoogle = async () => {
setLoginAttempting(true);
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
const { idToken } = await GoogleSignin.signIn();
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
return auth().signInWithCredential(googleCredential);
};
const [user, loginstate, setLoginState] = useAuthState();
return (
<>
<View
style={{
flex: 1,
rowGap: 24,
flexDirection: "column",
paddingTop: 80,
}}
>
<ELogo />
<View style={{ rowGap: 16, flexDirection: "column" }}>
<ETextInput
placeholder={"이메일"}
value={email}
setValue={setEmail}
onChange={() => setError(false)}
/>
<ETextInput
placeholder={"비밀번호"}
value={password}
setValue={setPassword}
passwordHidden={true}
onChange={() => setError(false)}
/>
{error && (
<ECaptionText error={true}>
</ECaptionText>
)}
</View>
<View style={{ rowGap: 16, flexDirection: "column" }}>
<EFullButton
text={"로그인"}
active={
email.length !== 0 && password.length !== 0 && !loginAttempting
}
onPress={() => signInWithEmail()}
/>
<EFullButton
style={{ backgroundColor: ColorScheme.passthrough }}
onPress={() => {
setLoginState &&
setLoginState({ ...loginstate, passwordReset: true });
router.push("auth/passwordresetemail");
}}
active={true}
text={"비밀번호를 모르겠어요"}
/>
<View
style={{
flex: 1,
justifyContent: "center",
columnGap: 16,
flexDirection: "row",
}}
>
<EIconButton active={false}>
<AppleIcon fill={ColorScheme.fill[10]} />
</EIconButton>
<EIconButton
active={!loginAttempting}
onPress={() => {
signInWithGoogle()
.then((d) => {
console.log("worked!");
setLoginAttempting(false);
router.replace("");
})
.catch((e) => {
console.error(e);
setLoginAttempting(false);
});
}}
>
<GoogleIcon fill={ColorScheme.fill[10]} />
</EIconButton>
</View>
</View>
</View>
<View style={{ flexDirection: "column", rowGap: 16, marginTop: 200 }}>
<ELabelText style={{ textAlign: "center" }}>
?
</ELabelText>
<EFullButton
onPress={() => {
setLoginState &&
setLoginState({ ...loginstate, passwordReset: false });
router.push("auth/registeremail");
}}
style={{ backgroundColor: ColorScheme.frostedfill.white[10] }}
active={true}
text={"회원가입"}
/>
</View>
</>
);
}

35
app/auth/passwordresetemail.tsx

@ -0,0 +1,35 @@
import React, { useState } from 'react';
import {View} from 'react-native'
import ETextInput from '@components/ETextInput';
import EFullButton, { ESmallButton } from '@components/EButton';
import EText, { ELabelText, ETitleText } from '@components/EText';
import ENavButtons from '@components/ENavButtons';
// implement logic here later.
export default function PasswordResetEmailInput(props:any) {
const [email, setEmail] = useState<string>('');
const emailre = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
return (
<>
<View
style={{
flex: 1,
flexDirection: 'column',
}}>
<ENavButtons back={true} />
<View style={{flexDirection: 'column', marginTop: 0}}>
<View style={{marginBottom: 24}}>
<ETitleText style={{marginBottom:8}}> </ETitleText>
<EText> </EText>
</View>
<View style={{rowGap: 8}}>
<ELabelText></ELabelText>
<ETextInput placeholder='이메일 입력하기' value={email} setValue={setEmail} />
<ESmallButton text={'재설정 이메일 보내기'} active={emailre.test(email)} />
</View>
</View>
<EFullButton style={{marginTop:380}} text='다음' active={emailre.test(email)}/>
</View>
</>
);
}

51
app/auth/registeremail.tsx

@ -0,0 +1,51 @@
import React, { useEffect, useState } from "react";
import { Text, View } from "react-native";
import ETextInput from "@components/ETextInput";
import EFullButton from "@components/EButton";
import { ELabelText, ETitleText } from "@components/EText";
import ENavButtons from "@components/ENavButtons";
import { useAuthState } from "@components/AuthProvider";
import { useRouter } from "expo-router";
// implement logic here later.
export default function RegisterEmail(props: any) {
const [email, setEmail] = useState<string>('');
const emailre =
/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
const [user, loginState, setLoginState] = useAuthState();
useEffect(() => {
setEmail(loginState?.tempEmail || '')
}, [])
const router = useRouter();
return (
<>
<View
style={{
flexDirection: "column",
}}
>
<ENavButtons back={true} />
<View style={{ flexDirection: "column", marginTop: 0 }}>
<ETitleText style={{ marginBottom: 24 }}>
</ETitleText>
<View style={{ rowGap: 8 }}>
<ELabelText></ELabelText>
<ETextInput
placeholder="이메일 입력하기"
value={email}
setValue={setEmail}
onChange={(v) => setLoginState && setLoginState({ ...loginState, tempEmail: v })}
/>
</View>
</View>
<EFullButton
style={{ marginTop: 24 }}
text="다음"
active={emailre.test(email)}
onPress={() => router.push('auth/createpassword', )}
/>
</View>
</>
);
}

35
app/index.tsx

@ -0,0 +1,35 @@
import React, { useEffect, useState } from 'react';
import {Button, Text, View} from 'react-native';
import ELogo from '@components/ELogo';
import { useAuthState } from '@components/AuthProvider';
import { Redirect, useRouter } from 'expo-router';
import auth from '@react-native-firebase/auth';
export default function LoginScreen(props:any) {
const [user, _, setLoginState, init] = useAuthState();
const [redirect, setRedirect] = useState(false);
const router = useRouter();
useEffect(() => {
if (!init) {
if (user === null) setRedirect(true);
}
}, [init])
if (redirect) {
return <Redirect href="auth/login" />
}
return (
<>
<View
style={{
flex: 1,
rowGap: 24,
flexDirection: 'column',
paddingTop: 345,
}}>
<ELogo />
<Text style={{color: 'white'}}>{JSON.stringify(user)} , {JSON.stringify(init)}</Text>
<Button title="Signout" onPress={async ()=> {await auth().signOut(); router.replace("auth/login")}} />
</View>
</>
);
}

BIN
assets/fonts/Axiforma-ExtraBold.otf (Stored with Git LFS)

size 115273

BIN
assets/fonts/Axiforma-ExtraBoldItalic.otf (Stored with Git LFS)

size 117672

BIN
assets/fonts/Pretendard-Black.ttf (Stored with Git LFS)

size 2665004

BIN
assets/fonts/Pretendard-Bold.ttf (Stored with Git LFS)

size 2661752

BIN
assets/fonts/Pretendard-ExtraBold.ttf (Stored with Git LFS)

size 2669648

BIN
assets/fonts/Pretendard-ExtraLight.ttf (Stored with Git LFS)

size 2838808

BIN
assets/fonts/Pretendard-Light.ttf (Stored with Git LFS)

size 2813692

BIN
assets/fonts/Pretendard-Medium.ttf (Stored with Git LFS)

size 2701192

BIN
assets/fonts/Pretendard-Regular.ttf (Stored with Git LFS)

size 2725828

BIN
assets/fonts/Pretendard-SemiBold.ttf (Stored with Git LFS)

size 2671468

BIN
assets/fonts/Pretendard-Thin.ttf (Stored with Git LFS)

size 2868924

BIN
assets/splash2.png (Stored with Git LFS)

size 9995

42
components/AuthProvider.tsx

@ -0,0 +1,42 @@
import React, { createContext, useContext, useEffect, useState } from "react";
import auth, { FirebaseAuthTypes } from "@react-native-firebase/auth";
import { useRouter } from "expo-router";
interface LoginState {
tempEmail?: string;
passwordReset?: boolean
}
const AuthContext = createContext<
[
FirebaseAuthTypes.User | null,
LoginState | null,
React.Dispatch<React.SetStateAction<LoginState | null>> | null,
boolean
]
>([null, null, null, false]);
export default function AuthProvider(props: { children: React.ReactNode }) {
const [user, setUser] = useState<FirebaseAuthTypes.User | null>(null);
const [init, setInit] = useState(true);
const [loginstate, setLogInState] = useState<LoginState|null>(null);
useEffect(() => {
const subscriber = auth().onAuthStateChanged((u) => {
setUser(u);
if (init) {
setInit(false);
}
return subscriber;
});
}, []);
if (init) return null;
return (
<AuthContext.Provider value={[user, loginstate, setLogInState, init]}>
{props.children}
</AuthContext.Provider>
);
}
export function useAuthState() {
return useContext(AuthContext);
}

60
components/Design.ts

@ -0,0 +1,60 @@
import React from "react";
export const ColorScheme = {
frostedfill: {
black: {
10: 'rgba(0,0,0,0.6)',
20: 'rgba(0,0,0,0.2)',
},
white: {
10: 'rgba(255,255,255,0.15)',
20: 'rgba(255,255,255,0.5)',
},
},
fill: {
10: 'rgba(255,255,255,0.85)',
20: 'rgba(255,255,255,0.5)',
30: 'rgba(255,255,255,0.3)',
40: 'rgba(255,255,255,0.15)',
50: 'rgba(255,255,255,0.08)',
60: 'rgba(255,255,255,0.05)',
black: '#000000',
accent: '#40FF4F',
background: '#090909',
},
text: {
10: 'rgba(255,255,255,0.95)',
20: 'rgba(255,255,255,0.55)',
30: 'rgba(255,255,255,0.30)',
inverse: 'rgba(0,0,0,0.9)',
},
divider: {
10: 'rgba(255,255,255,0.30)',
20: 'rgba(255,255,255,0.08)',
},
success: {
fill: '#00B23B',
text: '#16D956',
10: '#00802A',
20: '#009933',
30: '#00591E',
40: '#003311',
},
warning: {
fill: '#FF9F40',
text: '#FF9F40',
10: '#CC8033',
20: '#E58F39',
30: '#592D00',
40: '#331A00',
},
critical: {
fill: '#FF264B',
text: '#CC0023',
10: '#99001A',
20: '#B2001E',
30: '#59000F',
40: '#330009',
},
passthrough: '#00000000',
}

93
components/EButton.tsx

@ -0,0 +1,93 @@
import React from 'react';
import {Pressable, StyleProp, ViewStyle} from 'react-native';
import {ColorScheme} from './Design';
import {Text} from 'react-native';
export default function EFullButton(props: {
text: string;
active?: boolean;
onPress?: () => void;
style?: StyleProp<ViewStyle>;
}) {
return (
<Pressable
style={[
{
width: '100%',
borderRadius: 4,
backgroundColor: ColorScheme.frostedfill.black[10],
paddingHorizontal: 16,
paddingVertical: 12,
},
props.style,
]}
onPress={() => props.active && props.onPress && props.onPress()}>
<Text
style={{
color: props.active ? ColorScheme.fill[10] : ColorScheme.fill[40],
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 17,
fontWeight: '600',
}}>
{props.text}
</Text>
</Pressable>
);
}
export function ESmallButton(props: {
text: string;
active?: boolean;
onPress?: () => void;
style?: StyleProp<ViewStyle>;
}) {
return (
<Pressable
style={[{
borderRadius: 4,
backgroundColor: ColorScheme.frostedfill.white[10],
paddingHorizontal: 16,
paddingTop: 12,
paddingBottom: 13,
alignSelf: 'flex-start',
flex: 1,
},props.style]}
onPress={() => props.active && props.onPress && props.onPress()}>
<Text
style={{
color: props.active ? ColorScheme.fill[10] : ColorScheme.fill[40],
fontFamily: 'Pretendard-SemiBold',
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 15,
flex: 1,
}}>
{props.text}
</Text>
</Pressable>
);
}
export function EIconButton(props: {
children: React.ReactNode,
active?: boolean;
onPress?: () => void;
style?: StyleProp<ViewStyle>;
}) {
return (
<Pressable
style={[{
borderRadius: 4,
backgroundColor: ColorScheme.frostedfill.white[10],
paddingHorizontal: 8,
paddingVertical: 8,
justifyContent: 'center',
alignContent: 'center',
height: 40,
},props.style]}
onPress={() => props.active && props.onPress && props.onPress()}>
{props.children}
</Pressable>
);
}

100
components/EIcons.tsx

@ -0,0 +1,100 @@
import React from 'react';
import Svg, {G, Path, Defs, ClipPath, Rect} from 'react-native-svg';
export const CrossIcon = (props: any) => (
<Svg
width={24}
height={24}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<G clipPath="url(#clip0_226_47132)">
<Path d="M5.721 5.72724C5.34609 6.10215 5.34611 6.70999 5.721 7.08488L10.6425 12.0064L5.72102 16.9278C5.34613 17.3027 5.34611 17.9106 5.72102 18.2855C6.09594 18.6604 6.70377 18.6604 7.07867 18.2855L12.0001 13.364L16.9216 18.2855C17.2965 18.6604 17.9043 18.6604 18.2792 18.2855C18.6541 17.9105 18.6541 17.3027 18.2792 16.9278L13.3578 12.0064L18.2792 7.0849C18.6541 6.71001 18.6542 6.10217 18.2792 5.72726C17.9043 5.35235 17.2965 5.35236 16.9216 5.72726L12.0001 10.6487L7.07865 5.72724C6.70375 5.35234 6.09592 5.35233 5.721 5.72724Z" />
</G>
<Defs>
<ClipPath id="clip0_226_47132">
<Rect
width={24}
height={24}
transform="translate(0 0.00634766)"
/>
</ClipPath>
</Defs>
</Svg>
);
export const EyeIcon = (props: any) => (
<Svg
width={24}
height={24}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M12 5.006c3.69 0 6.974 2.19 9.83 6.443a1 1 0 0 1 0 1.115c-2.856 4.253-6.14 6.442-9.83 6.442s-6.974-2.19-9.83-6.442a1 1 0 0 1 0-1.115C5.026 7.196 8.31 5.006 12 5.006m0 2c-2.76 0-5.323 1.598-7.71 4.898l-.074.102.074.103C6.6 15.302 9.075 16.9 11.733 17l.267.005c2.76 0 5.323-1.597 7.71-4.897l.073-.103-.073-.102C17.4 8.71 14.925 7.11 12.267 7.01zm0 1a4 4 0 1 1 0 8 4 4 0 0 1 0-8m0 2a2 2 0 1 0 0 4 2 2 0 0 0 0-4"
/>
</Svg>
);
export const SuccessIcon = (props: any) => (
<Svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path
d="M15.743 9.264a.906.906 0 0 0-1.267 0l-3.745 3.635-1.2-1.179a.92.92 0 0 0-1.264-.006.86.86 0 0 0-.01 1.235l1.832 1.8a.907.907 0 0 0 1.266.003l4.383-4.25a.865.865 0 0 0 .007-1.234z"
/>
<Path
d="M21.214 8.118a10 10 0 0 0-2.144-3.18 10 10 0 0 0-3.18-2.143A9.9 9.9 0 0 0 12 2.007c-5.514.002-10 4.488-10 10 0 5.514 4.486 10 10 10s10-4.486 10-10a9.9 9.9 0 0 0-.786-3.889M12 20.353c-4.602 0-8.346-3.744-8.346-8.345S7.398 3.662 12 3.662s8.346 3.744 8.346 8.346c0 4.601-3.744 8.345-8.346 8.345"
/>
</Svg>
);
export const FailIcon = (props: any) => (
<Svg
width={24}
height={24}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path d="M12.036 2.006a9.9 9.9 0 0 1 7.055 2.91C20.909 6.733 22 9.278 22 12.042a9.9 9.9 0 0 1-2.91 7.054 9.9 9.9 0 0 1-7.054 2.91c-2.763 0-5.309-1.092-7.127-2.91A9.9 9.9 0 0 1 2 12.043c0-2.764 1.09-5.31 2.91-7.128 1.817-1.818 4.363-2.909 7.126-2.909m6.037 4c-1.527-1.6-3.71-2.545-6.037-2.545-2.4 0-4.509.945-6.036 2.545-1.6 1.528-2.545 3.637-2.545 6.037 0 2.327.945 4.509 2.545 6.036 1.527 1.527 3.636 2.473 6.036 2.473 2.328 0 4.51-.946 6.037-2.473s2.473-3.709 2.473-6.036c0-2.4-.946-4.51-2.473-6.037" />
<Path d="M14.8 8.188a.703.703 0 0 1 1.018 0 .703.703 0 0 1 0 1.018l-2.836 2.837 2.837 2.763c.29.291.29.8 0 1.091a.703.703 0 0 1-1.019 0l-2.836-2.836-2.691 2.69a.79.79 0 0 1-1.09 0 .87.87 0 0 1 0-1.018l2.763-2.69-2.764-2.691c-.218-.291-.218-.8 0-1.091a.79.79 0 0 1 1.091 0l2.69 2.69z" />
</Svg>
);
export const AppleIcon = (props:any) => (
<Svg
width={24}
height={24}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<Path
d="M20.3172 8.50055C20.1898 8.59956 17.9397 9.86909 17.9397 12.692C17.9397 15.9571 20.8028 17.1122 20.8885 17.1408C20.8753 17.2112 20.4337 18.7228 19.3789 20.2629C18.4385 21.6183 17.4563 22.9714 15.9621 22.9714C14.4679 22.9714 14.0834 22.1023 12.3585 22.1023C10.6775 22.1023 10.0798 23 8.71307 23C7.34633 23 6.39269 21.7459 5.29622 20.2057C4.02616 18.3971 3 15.5875 3 12.9208C3 8.64356 5.77743 6.37514 8.51092 6.37514C9.96336 6.37514 11.1741 7.33003 12.086 7.33003C12.9539 7.33003 14.3075 6.31793 15.9599 6.31793C16.5861 6.31793 18.8362 6.37514 20.3172 8.50055ZM15.1754 4.50715C15.8588 3.69527 16.3422 2.56876 16.3422 1.44224C16.3422 1.28603 16.329 1.12761 16.3005 1C15.1886 1.0418 13.8658 1.74147 13.0682 2.66777C12.442 3.38064 11.8575 4.50715 11.8575 5.64907C11.8575 5.82068 11.886 5.9923 11.8992 6.0473C11.9695 6.06051 12.0838 6.07591 12.198 6.07591C13.1956 6.07591 14.4503 5.40704 15.1754 4.50715Z"
/>
</Svg>
);
export const GoogleIcon = (props:any) => (
<Svg
width={24}
height={24}
viewBox="0 0 28 24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<Path
d="M14.0031 2.40625C8.69995 2.40625 4.40002 6.70465 4.40002 12.0063C4.40002 17.3078 8.69995 21.6063 14.0031 21.6063C22.0111 21.6063 23.8153 14.1719 23.0641 10.4062H22H20.186H14V13.6062H20.1906C19.479 16.3649 16.9808 18.4062 14 18.4062C10.4656 18.4062 7.60002 15.5407 7.60002 12.0063C7.60002 8.47185 10.4656 5.60625 14 5.60625C15.6072 5.60625 17.0713 6.20285 18.1953 7.18125L20.4688 4.90937C18.7616 3.35418 16.4935 2.40625 14.0031 2.40625Z"
/>
</Svg>
);
export default {CrossIcon, EyeIcon};

17
components/ELogo.tsx

@ -0,0 +1,17 @@
import React from 'react';
import {Text} from 'react-native';
import {ColorScheme} from './Design';
export default function ELogo() {
return (
<Text
style={{
fontFamily: 'Axiforma-ExtraBold',
fontSize: 36,
color: ColorScheme.fill[10],
textAlign: 'center',
}}>
essentory
</Text>
);
}

40
components/ENavButtons.tsx

@ -0,0 +1,40 @@
import React from 'react';
import {Pressable, StyleProp, Text, View, ViewStyle} from 'react-native';
import {Svg, Path} from 'react-native-svg';
import {ColorScheme} from './Design';
import { useRouter } from 'expo-router';
const BackButton = (props: any) => (
<Svg
width={64}
height={64}
viewBox="0 0 64 64"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M36.0002 38.3939L25.5862 27.9799L36.0002 17.5659L37.4142 18.9799L28.4142 27.9799L37.4142 36.9799L36.0002 38.3939Z"
fill="white"
fillOpacity={0.95}
/>
</Svg>
);
// fill in logic for moving back later.
export default function ENavButtons(props: {
back?: boolean;
style?: StyleProp<ViewStyle>,
}) {
const router = useRouter();
return (
<View
style={[{
paddingHorizontal: 0,
margin:0,
left:-24
}]}>
{props.back && <Pressable onPress={() => router.back()}><BackButton fill={ColorScheme.text[10]} /></Pressable>}
</View>
);
}

76
components/EText.tsx

@ -0,0 +1,76 @@
import React from 'react';
import {StyleProp, Text, TextStyle, ViewStyle} from 'react-native';
import {ColorScheme} from './Design';
export default function EText(props: {
children: React.ReactNode;
style?: StyleProp<TextStyle>;
}) {
return (
<Text
style={[
{
fontFamily: 'Pretendard-Regular',
fontSize: 15,
color: ColorScheme.fill[10],
},
props.style,
]}>
{props.children}
</Text>
);
}
export function ETitleText(props: {
children: React.ReactNode;
style?: StyleProp<TextStyle>;
}) {
return (
<Text
style={[
{
fontFamily: 'Pretendard-SemiBold',
fontSize: 24,
color: ColorScheme.fill[10],
},
props.style,
]}>
{props.children}
</Text>
);
}
export function ELabelText(props: {
children: React.ReactNode;
style?: StyleProp<TextStyle>;
}) {
return (
<Text
style={[
{fontFamily: 'Pretendard-SemiBold', fontSize: 15, color: 'white'},
props.style,
]}>
{props.children}
</Text>
);
}
export function ECaptionText(props: {
children: React.ReactNode;
error?: boolean;
style?: StyleProp<TextStyle>;
}) {
return (
<Text
style={[
{
fontFamily: 'Pretendard-Regular',
fontSize: 12,
color: props.error ? ColorScheme.critical.text : ColorScheme.fill[20],
},
props.style,
]}>
{props.children}
</Text>
);
}

113
components/ETextInput.tsx

@ -0,0 +1,113 @@
import React, {useState} from 'react';
import {StyleProp, Text, TextInput, View, ViewStyle} from 'react-native';
import {ColorScheme} from './Design';
import {CrossIcon, EyeIcon, FailIcon, SuccessIcon} from './EIcons';
export enum ETextInputBorder {
Nothing = 0,
Border = 1,
Success = 2,
Fail = 3,
}
const borderColorScheme = [
'rgba(0,0,0,0)',
ColorScheme.fill[30],
ColorScheme.success.fill,
ColorScheme.critical.fill,
];
export default function ETextInput(props: {
placeholder: string;
value: string;
setValue: any;
passwordHidden?: boolean;
setPasswordHidden?: (newState: boolean) => void;
checkFailStatus?: boolean;
border?: ETextInputBorder;
style?: StyleProp<ViewStyle>;
onChange?: (value: string) => void;
}) {
const border =
props.border === undefined ? ETextInputBorder.Nothing : props.border;
const [focused, setFocused] = useState(false);
const clear = () => {
props.setValue('');
props.onChange !== undefined && props.onChange('');
};
const sideIcon = () => {
if (props.setPasswordHidden !== undefined) {
return (
<EyeIcon
fill={
props.passwordHidden ? ColorScheme.fill[20] : ColorScheme.fill[10]
}
onPress={() =>
props.setPasswordHidden &&
props.setPasswordHidden(!props.passwordHidden)
}
/>
);
} else if (props.checkFailStatus !== undefined) {
return props.checkFailStatus ? (
<SuccessIcon fill={ColorScheme.success.text} />
) : (
<FailIcon fill={ColorScheme.critical.text} />
);
} else {
return (
props.value && <CrossIcon fill={ColorScheme.fill[20]} onPress={clear} />
);
}
};
return (
<View
style={[
props.style,
{
borderColor:
borderColorScheme[
focused && border == ETextInputBorder.Nothing
? ETextInputBorder.Border
: border
],
borderWidth: 1,
borderRadius: 8,
backgroundColor: ColorScheme.frostedfill.black[20],
paddingHorizontal: 16,
paddingVertical: 12,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
// these two are sketch.
shadowRadius: 30,
shadowColor: '#000000',
},
]}>
<TextInput
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
onChangeText={text => {
props.setValue(text);
props.onChange !== undefined && props.onChange(text);
}}
value={props.value}
placeholder={props.placeholder}
placeholderTextColor={ColorScheme.fill[30]}
secureTextEntry={props.passwordHidden}
style={{
fontFamily: 'Pretendard-SemiBold',
fontSize: 17,
fontWeight: '300',
color: ColorScheme.fill[10],
margin: 0,
padding: 0,
flex: 1,
}}></TextInput>
{sideIcon()}
</View>
);
}

1156
package-lock.json
File diff suppressed because it is too large
View File

20
package.json

@ -1,18 +1,30 @@
{ {
"name": "loginmockup2", "name": "loginmockup2",
"version": "1.0.0", "version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"main": "expo-router/entry",
"scripts": { "scripts": {
"start": "expo start", "start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web" "web": "expo start --web"
}, },
"dependencies": { "dependencies": {
"@react-native-firebase/app": "^19.1.2",
"@react-native-firebase/auth": "^19.1.2",
"@react-native-google-signin/google-signin": "^11.0.1",
"expo": "~50.0.14", "expo": "~50.0.14",
"expo-constants": "~15.4.5",
"expo-dev-client": "~3.3.11",
"expo-font": "~11.10.3",
"expo-linking": "~6.2.2",
"expo-router": "~3.4.8",
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1", "expo-status-bar": "~1.11.1",
"react": "18.2.0", "react": "18.2.0",
"react-native": "0.73.6"
"react-native": "0.73.6",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-svg": "14.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",

5
tsconfig.json

@ -1,6 +1,9 @@
{ {
"extends": "expo/tsconfig.base", "extends": "expo/tsconfig.base",
"compilerOptions": { "compilerOptions": {
"strict": true
"strict": true,
"paths": {
"@components/*": ["components/*"]
}
} }
} }
Loading…
Cancel
Save