Skip to content

Commit

Permalink
Merge pull request #54 from Naveen-g09/auth
Browse files Browse the repository at this point in the history
Proper Authentication with jwt context
  • Loading branch information
Naveen-g09 authored Jun 22, 2024
2 parents fd20d07 + aaf8eae commit 8f272e4
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 19 deletions.
4 changes: 4 additions & 0 deletions api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const authRoute =
"https://residentron.naveen-medium.workers.dev/api/v1/user/";
export const signin = authRoute + "signin";
export const signup = authRoute + "signup";
12 changes: 10 additions & 2 deletions app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { Link, Tabs } from "expo-router";
import { Link, Tabs, Redirect } from "expo-router";
import React from "react";
import { Pressable } from "react-native";
import { useRecoilValueLoadable } from "recoil";

import { userAtom } from "@/store";

/**
* You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
Expand All @@ -15,7 +18,12 @@ function TabBarIcon(props: {

//TODO: add a missing page to handle 404 errors

export default function RootLayout() {
export default function ProtectedLayout() {
const { contents: user, state } = useRecoilValueLoadable(userAtom);

Check warning on line 22 in app/(tabs)/_layout.tsx

View workflow job for this annotation

GitHub Actions / lint

'state' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 22 in app/(tabs)/_layout.tsx

View workflow job for this annotation

GitHub Actions / lint

'state' is assigned a value but never used

if (!user) {
return <Redirect href="/sign-in" />;
}
return (
<Tabs initialRouteName="home">
<Tabs.Screen
Expand Down
11 changes: 7 additions & 4 deletions app/(tabs)/account.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { BottomSheetModal, useBottomSheetModal } from "@gorhom/bottom-sheet";
import { Link } from "expo-router";
import React, { useState, useRef } from "react";
import React, { useRef } from "react";
import { Text, TouchableOpacity, StyleSheet, ScrollView } from "react-native";
import { useSetRecoilState } from "recoil";

import AccountSheet from "../../components/bottomSheet";

import Admin from "@/components/adminBottomSheet";
import CalenderSheet from "@/components/calenderBS";
import Transaction from "@/components/transactionBottomSheet";
import Utility from "@/components/utility";
import { userAtom } from "@/store";

//TODO: details grids of profile picture, name, email, phone number, address, and edit button
//TODO: details of flat no, family members, parking, your posts, your events, your polls, your announcements, your notices, your chats, your complaints, your suggestions, your feedbacks, your maintenance, your bills, your payments, your receipts, your orders,
//TODO: current and past utilities, current and past maintenance, current and past bills, current and past orders, current and past payments, current and past receipts, current and past complaints, current and past suggestions, current and past feedbacks, current and past polls, current and past announcements, current and past notices, current and past chats, current and past events, current and past posts, current and past family members, current and past parking, current and past flat no, current and past name, current and past email, current and past phone number, current and past address, current and past profile picture, current and past edit button
//TODO: add a logout button

const Account = () => {
const setUser = useSetRecoilState(userAtom);
const bottomSheetRef = useRef<BottomSheetModal>(null);
const calenderBottomSheetRef = useRef<BottomSheetModal>(null);
const transactionBottomSheetRef = useRef<BottomSheetModal>(null);
Expand All @@ -30,9 +33,9 @@ const Account = () => {
const handleAdminPress = () => adminBottomSheetRef.current?.present();
const handleUtilityPress = () => utilityBottomSheetRef.current?.present();

const [user, setUser] = useState(null);

const doLogout = async () => {};
const doLogout = async () => {
setUser(false);
};

return (
<ScrollView contentContainerStyle={styles.container}>
Expand Down
22 changes: 13 additions & 9 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import FontAwesome from "@expo/vector-icons/FontAwesome";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import { DefaultTheme, ThemeProvider } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import { Slot } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import React, { useEffect } from "react";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { RecoilRoot } from "recoil";

const MyTheme = {
...DefaultTheme,
Expand Down Expand Up @@ -54,16 +55,19 @@ export default function RootLayout() {

function RootLayoutNav() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<BottomSheetModalProvider>
<ThemeProvider value={MyTheme}>
<Stack>
<RecoilRoot>
<GestureHandlerRootView style={{ flex: 1 }}>
<BottomSheetModalProvider>
<ThemeProvider value={MyTheme}>
{/* <Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="ameneties" />
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
</Stack>
</ThemeProvider>
</BottomSheetModalProvider>
</GestureHandlerRootView>
</Stack> */}
<Slot />
</ThemeProvider>
</BottomSheetModalProvider>
</GestureHandlerRootView>
</RecoilRoot>
);
}
233 changes: 233 additions & 0 deletions app/sign-in.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { Entypo, FontAwesome5 } from "@expo/vector-icons";
import axios from "axios";
import { router } from "expo-router";
import React, { useState } from "react";
import { Alert, Pressable, TextInput } from "react-native";
import { useSetRecoilState } from "recoil";

import { signin, signup } from "@/api/auth";
import { Button } from "@/components/Button";
import { Container } from "@/components/Container";
import { Text } from "@/components/Text";
import { View } from "@/components/View";
import { userAtom } from "@/store";

export default function Auth() {
const [visible, setVisible] = useState(false);
const [username, setusername] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [isSignUp, setIsSignUp] = useState(false);
const setUser = useSetRecoilState(userAtom);

const handleForgot = () => {
Alert.alert("Forgot Password", "Please contact the admin", [
{
text: "OK",
onPress: () => console.log("OK Pressed"),
},
]);
};

const handleBack = () => {
console.warn("Back to home");
};

const handleSignInSubmit = () => {
async function signIn() {
try {
const response = await axios.post(signin, {
username,
password,
});
console.log("return from server", response.data);
if (response.data.message === "Incorrect creds") {
// Handle incorrect credentials
console.warn("Incorrect credentials");
} else {
//TODO: save the token in the local storage, add expiration in the backend and form logic from here
setUser(true);
router.replace("/");
}
} catch (error) {
console.error(error);
}
}
signIn();
};

const handleSignUpSubmit = () => {
async function signUp() {
try {
const response = await axios.post(signup, {
name,
username,
password,
});
console.log(response.data);
if (response.data.message === "User already exists") {
// Handle user already exists
console.warn("User already exists");
} else {
setUser(true);
router.replace("/");
}
} catch (error) {
console.error(error);
}
}
signUp();
};

return (
<Container>
{/* <View className="py-5">
<Ionicons name="arrow-back" size={24} onPress={handleBack} />
</View> */}
<View className="gap-10 p-5">
<View className="gap-2">
<View className="flex-row items-center gap-1">
<Text className="text-2xl font-semibold">Welcome</Text>
<Text className="text-2xl font-semibold" variant="primary">
to Residentron!
</Text>
<Entypo name="code" size={20} />
</View>
<Text variant="secondary">
{isSignUp
? "Create your account"
: "Sign in to explore your account"}
</Text>
</View>
{isSignUp ? (
<View className="gap-4">
<View className="gap-1.5">
<Text size="sm" variant="secondary">
Enter your name
</Text>
<View variant="secondary" className="rounded px-5 py-2">
<TextInput
placeholder="Your Name"
className="text-base text-foreground placeholder:text-secondary-foreground"
value={name}
onChangeText={setName}
/>
</View>
</View>
<View className="gap-1.5">
<Text size="sm" variant="secondary">
Enter your username
</Text>
<View variant="secondary" className="rounded px-5 py-2">
<TextInput
placeholder="example name"
className="text-base text-foreground placeholder:text-secondary-foreground"
keyboardType="email-address"
value={username}
onChangeText={setusername}
/>
</View>
</View>
<View className="gap-1.5">
<Text size="sm" variant="secondary">
Enter your password
</Text>
<View
variant="secondary"
className="flex-row items-center rounded px-5 py-2"
>
<TextInput
placeholder="check caps-lock"
className="flex-1 text-base text-foreground placeholder:text-secondary-foreground"
secureTextEntry={!visible}
value={password}
onChangeText={setPassword}
/>
<FontAwesome5
name={visible ? "eye" : "eye-slash"}
size={14}
onPress={() => {
setVisible((prev) => !prev);
}}
/>
</View>
</View>
<Button
onPress={handleSignUpSubmit}
className="active:bg-primary/70"
>
<Text variant="primary-lite">Create Account</Text>
</Button>
<View className="flex-row justify-center gap-1">
<Text variant="secondary">Already have an account?</Text>
<Pressable onPress={() => setIsSignUp(false)}>
<Text variant="accent">Sign In</Text>
</Pressable>
</View>
</View>
) : (
<View className="gap-4">
<View className="gap-1.5">
<Text size="sm" variant="secondary">
Enter your username
</Text>
<View variant="secondary" className="rounded px-5 py-2">
<TextInput
placeholder="[email protected]"
className="text-base text-foreground placeholder:text-secondary-foreground"
keyboardType="email-address"
value={username}
onChangeText={setusername}
/>
</View>
</View>
<View className="gap-1.5">
<Text size="sm" variant="secondary">
Enter your password
</Text>
<View
variant="secondary"
className="flex-row items-center rounded px-5 py-2"
>
<TextInput
placeholder="check caps-lock"
className="flex-1 text-base text-foreground placeholder:text-secondary-foreground"
secureTextEntry={!visible}
value={password}
onChangeText={setPassword}
/>
<FontAwesome5
name={visible ? "eye" : "eye-slash"}
size={14}
onPress={() => {
setVisible((prev) => !prev);
}}
/>
</View>
</View>
<Button
className="items-end px-0 py-0 min-h-0"
onPress={handleForgot}
>
<Text size="sm" variant="accent">
Forgot Password
</Text>
</Button>
<Button
onPress={handleSignInSubmit}
className="active:bg-primary/70"
>
<Text variant="primary-lite">Sign In</Text>
</Button>
<View className="flex-row justify-center gap-1">
<Text variant="secondary">Don't have an account?</Text>
<Pressable onPress={() => setIsSignUp(true)}>
<Text variant="accent">Create Now</Text>
</Pressable>
</View>
</View>
)}
</View>
</Container>
);
}
52 changes: 52 additions & 0 deletions components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { VariantProps, cva } from "class-variance-authority";
import React, { forwardRef } from "react";
import { Pressable, PressableProps, View } from "react-native";

import { cn } from "@/lib/cn";

const buttonVariants = cva(
"flex items-center justify-center rounded transition-colors disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary",
secondary: "bg-secondary",
muted: "bg-muted",
accent: "bg-accent",
destructive: "bg-destructive",
outline: "bg-transparent border-2 border-primary",
"outline-secondary": "bg-transparent border-2 border-secondary",
"outline-destructive": "bg-transparent border-2 border-destructive",
ghosted: "bg-transparent",
},
size: {
default: "min-h-12 py-3.5 px-5",
sm: "min-h-9 py-2.5 px-3.5",
lg: "min-h-16 py-4 px-7",
icon: "aspect-square",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);

interface ButtonProps
extends PressableProps,
VariantProps<typeof buttonVariants> {}

export const Button = forwardRef<View, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<View>
<Pressable
ref={ref}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
</View>
);
},
);
Loading

0 comments on commit 8f272e4

Please sign in to comment.