diff --git a/src/api/hooks.ts b/src/api/hooks.ts
index 1bcae3a..2e7b05d 100644
--- a/src/api/hooks.ts
+++ b/src/api/hooks.ts
@@ -11,6 +11,7 @@ export const useGetPosts = (type: string | null) => {
fetch(getUrl(type!))
.then((res) => res.json())
.then((data) => data.posts),
+ select: (data) => data.reverse(),
staleTime: 1000 * 60 * 5,
});
};
diff --git a/src/app/(app)/index.tsx b/src/app/(app)/index.tsx
index a5821f7..6b32b60 100644
--- a/src/app/(app)/index.tsx
+++ b/src/app/(app)/index.tsx
@@ -1,28 +1,56 @@
import { useGetPosts } from "@api/hooks";
import { isRouteAuthenticated } from "@auth/routes";
+import LoadingSpinner from "@components/LoadingSpinner";
import { PostCard } from "@components/PostCard";
+import { Text, makeStyles } from "@rneui/themed";
import { usePostStore } from "@stores/postStore";
import { useUserStore } from "@stores/userStore";
-import { usePathname } from "expo-router";
-import { StatusBar } from "expo-status-bar";
-import { ScrollView } from "react-native";
+import { Redirect, usePathname } from "expo-router";
+import { ScrollView, View } from "react-native";
-import Login from "./login";
+const useStyles = makeStyles((theme) => ({
+ container: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ marginTop: 20,
+ },
+ text: {
+ color: theme.colors.white,
+ },
+ loadingSpinner: {
+ marginTop: 20,
+ },
+}));
const App = () => {
+ const styles = useStyles();
const { user } = useUserStore();
const route = usePathname();
const postsType = usePostStore((state) => state.type);
const { data } = useGetPosts(postsType);
if (!user && isRouteAuthenticated(route)) {
- return ;
+ return ;
}
return (
-
- {data?.map((post) => )}
+ {postsType &&
+ data &&
+ data.map((post) => )}
+
+ {postsType && !data && (
+
+
+
+ )}
+
+ {!postsType && (
+
+ Please select a type!
+
+ )}
);
};
diff --git a/src/app/(app)/login.tsx b/src/app/(app)/login.tsx
index c2e3fe7..7c44df8 100644
--- a/src/app/(app)/login.tsx
+++ b/src/app/(app)/login.tsx
@@ -1,6 +1,4 @@
import { useLoginUser } from "@auth/hooks";
-import ErrorScreen from "@components/ErrorScreen";
-import LoadingSpinner from "@components/LoadingSpinner";
import LoginField from "@components/LoginField";
import { Redirect } from "expo-router";
import { useState } from "react";
@@ -14,13 +12,11 @@ const Login = () => {
return ;
}
- if (loading) {
- return ;
- }
-
- if (error) {
- return ;
- }
+ const isDisabled =
+ !email ||
+ !password ||
+ !/^[^@]+@[^@]+\.[^@]+$/g.test(email) ||
+ password.length < 5;
return (
{
onEmailChange={setEmail}
password={password}
onPasswordChange={setPassword}
- disabled={!email || !password}
+ disabled={isDisabled}
onButtonClick={() => login(email, password)}
+ isLoading={loading ?? false}
+ error={error}
/>
);
};
diff --git a/src/app/(app)/logout.tsx b/src/app/(app)/logout.tsx
new file mode 100644
index 0000000..998bcb3
--- /dev/null
+++ b/src/app/(app)/logout.tsx
@@ -0,0 +1,12 @@
+import { useLogoutUser } from "@auth/hooks";
+import { Redirect } from "expo-router";
+
+const Logout = () => {
+ const { logout } = useLogoutUser();
+
+ logout();
+
+ return ;
+};
+
+export default Logout;
diff --git a/src/app/(app)/register.tsx b/src/app/(app)/register.tsx
index 9eecf7b..e325ca8 100644
--- a/src/app/(app)/register.tsx
+++ b/src/app/(app)/register.tsx
@@ -1,6 +1,4 @@
import { useRegisterUser } from "@auth/hooks";
-import ErrorScreen from "@components/ErrorScreen";
-import LoadingSpinner from "@components/LoadingSpinner";
import LoginField from "@components/LoginField";
import { Redirect } from "expo-router";
import { useState } from "react";
@@ -14,13 +12,11 @@ const Register = () => {
return ;
}
- if (loading) {
- return ;
- }
-
- if (error) {
- return ;
- }
+ const isDisabled =
+ !email ||
+ !password ||
+ !/^[^@]+@[^@]+\.[^@]+$/g.test(email) ||
+ password.length < 5;
return (
{
onEmailChange={setEmail}
password={password}
onPasswordChange={setPassword}
- disabled={!email || !password}
+ disabled={isDisabled}
onButtonClick={() => register(email, password)}
+ isLoading={loading ?? false}
+ error={error}
/>
);
};
diff --git a/src/auth/hooks.ts b/src/auth/hooks.ts
index 18de9b4..b0b5822 100644
--- a/src/auth/hooks.ts
+++ b/src/auth/hooks.ts
@@ -1,5 +1,7 @@
+import { queryClient } from "@api/queryClient";
import { firebaseApp } from "@auth/firebase";
import AsyncStorage from "@react-native-async-storage/async-storage";
+import { usePostStore } from "@stores/postStore";
import { useUserStore } from "@stores/userStore";
import {
createUserWithEmailAndPassword,
@@ -64,6 +66,7 @@ export const useLogoutUser = () => {
const { setUser } = useUserStore();
const [loading, setLoading] = useState(null);
const [error, setError] = useState(null);
+ const setType = usePostStore((state) => state.setType);
const logout = useCallback(() => {
setLoading(true);
@@ -72,7 +75,11 @@ export const useLogoutUser = () => {
.signOut()
.then(() => {
setUser(null);
+ setType(null);
setLoading(false);
+ queryClient.invalidateQueries({
+ predicate: (query) => query.queryKey[0] === "posts",
+ });
})
.catch((error) => {
setError(error);
diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx
index 78a5ced..00fe707 100644
--- a/src/components/AppHeader.tsx
+++ b/src/components/AppHeader.tsx
@@ -1,7 +1,9 @@
-import { Header, Text, makeStyles } from "@rneui/themed";
+import { useLogoutUser } from "@auth/hooks";
+import { Button, Chip, Header, Text, makeStyles } from "@rneui/themed";
import { usePostStore } from "@stores/postStore";
+import { useUserStore } from "@stores/userStore";
import React from "react";
-import { ScrollView, TouchableOpacity } from "react-native";
+import { ScrollView } from "react-native";
const useStyles = makeStyles((theme) => ({
headerContainer: {
@@ -9,20 +11,34 @@ const useStyles = makeStyles((theme) => ({
justifyContent: "space-around",
height: 100,
},
- scrollView: {
- flexGrow: 0,
- },
option: {
paddingHorizontal: 10,
},
optionText: {
color: theme.colors.white,
},
+ loginText: {
+ color: theme.colors.white,
+ },
+ chip: {
+ marginHorizontal: 5,
+ },
+ selectedChip: {
+ borderStyle: "solid",
+ borderWidth: 1,
+ borderColor: theme.colors.white,
+ },
}));
const AppHeader = () => {
const styles = useStyles();
- const { setType, types } = usePostStore((state) => state);
+ const {
+ setType,
+ types: postTypes,
+ type: selectedType,
+ } = usePostStore((state) => state);
+ const user = useUserStore((state) => state.user);
+ const { logout, loading } = useLogoutUser();
const handlePostTypeSelect = (type: string) => {
setType(type);
@@ -30,21 +46,46 @@ const AppHeader = () => {
return (
{
+ logout();
+ }}
+ loading={Boolean(loading)}
+ type="solid"
+ />
+ ) : undefined
+ }
centerComponent={
- {types.map((type, index) => (
- handlePostTypeSelect(type)}
- style={styles.option}
- >
- {type.toUpperCase()}
-
- ))}
+ {user ? (
+ postTypes.map((postType) => (
+ handlePostTypeSelect(postType)}
+ size="md"
+ />
+ ))
+ ) : (
+ Please login to see posts!
+ )}
}
containerStyle={styles.headerContainer}
diff --git a/src/components/ErrorScreen.tsx b/src/components/ErrorScreen.tsx
index e4b8af4..132261a 100644
--- a/src/components/ErrorScreen.tsx
+++ b/src/components/ErrorScreen.tsx
@@ -13,6 +13,10 @@ const useStyles = makeStyles((theme) => ({
margin: "auto",
color: theme.colors.error,
},
+ message: {
+ color: theme.colors.white,
+ marginBottom: theme.spacing.xl,
+ },
}));
type Props = {
@@ -24,7 +28,9 @@ const ErrorScreen = ({ error }: Props) => {
return (
- The application has crashed.
+
+ The application has crashed :'(
+
{error}
);
diff --git a/src/components/LoginField.tsx b/src/components/LoginField.tsx
index d4b2495..b1ac93f 100644
--- a/src/components/LoginField.tsx
+++ b/src/components/LoginField.tsx
@@ -1,4 +1,11 @@
-import { Button, Input, Text, makeStyles } from "@rneui/themed";
+import {
+ Button,
+ Divider,
+ Input,
+ Text,
+ makeStyles,
+ useTheme,
+} from "@rneui/themed";
import { Link } from "expo-router";
import {
NativeSyntheticEvent,
@@ -13,13 +20,36 @@ const useStyles = makeStyles((theme) => ({
justifyContent: "center",
flex: 1,
backgroundColor: theme.colors.background,
+ width: "100%",
},
title: {
marginHorizontal: "auto",
marginVertical: theme.spacing.xl,
+ color: theme.colors.white,
},
button: {
- marginVertical: theme.spacing.xl,
+ marginTop: theme.spacing.lg,
+ borderRadius: 2.5,
+ },
+ center: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ inputContainer: {
+ width: "60%",
+ backgroundColor: theme.colors.background,
+ paddingTop: theme.spacing.lg,
+ },
+ input: {
+ color: theme.colors.white,
+ },
+ link: {
+ color: theme.colors.white,
+ marginVertical: theme.spacing.lg,
+ },
+ spacing: {
+ marginVertical: theme.spacing.md,
},
}));
@@ -31,6 +61,8 @@ type Props = {
onPasswordChange: (password: string) => void;
disabled: boolean;
onButtonClick: () => void;
+ isLoading: boolean;
+ error?: Error | null;
};
const LoginField = ({
@@ -41,8 +73,11 @@ const LoginField = ({
onPasswordChange,
disabled,
onButtonClick,
+ isLoading,
+ error,
}: Props) => {
- const classes = useStyles();
+ const styles = useStyles();
+ const { theme } = useTheme();
const handleKeyPress = (
event: NativeSyntheticEvent,
@@ -53,11 +88,19 @@ const LoginField = ({
};
return (
-
-
-
- {mode.toUpperCase()}
-
+
+
+ {mode.toUpperCase()}
+
+
+
+
-
+
+
- {mode === "login" ? "No account? Register" : "Have an account? Login"}
+ {mode === "login"
+ ? "No account? Register!"
+ : "Have an account? Login!"}
diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx
index 69211c1..af3987d 100644
--- a/src/components/PostCard.tsx
+++ b/src/components/PostCard.tsx
@@ -5,6 +5,10 @@ import { Linking, Text, TouchableOpacity } from "react-native";
import { Post } from "../types/Post";
const useStyles = makeStyles((theme) => ({
+ card: {
+ backgroundColor: "#100f38",
+ borderRadius: 10,
+ },
image: {
width: "100%",
height: 200,
@@ -33,10 +37,10 @@ type Props = {
export const PostCard = ({ post }: Props) => {
const styles = useStyles();
- const formatedDate = dateTimeFormat.format(new Date(post.timestamp));
+ const formattedDate = dateTimeFormat.format(new Date(post.timestamp));
return (
-
+
Linking.openURL(post.url)}>
{post.thumbnail && (
{
{post.description}
- {}
- Posted on: {formatedDate}
+ {formattedDate}
);
};
diff --git a/src/stores/postStore.ts b/src/stores/postStore.ts
index c4c9e14..ca49ed9 100644
--- a/src/stores/postStore.ts
+++ b/src/stores/postStore.ts
@@ -5,7 +5,7 @@ type PostStore = {
type: string | null;
isLoading: boolean;
setTypes: (types: string[]) => void;
- setType: (type: string) => void;
+ setType: (type: string | null) => void;
setIsLoading: (isLoading: boolean) => void;
};
@@ -14,6 +14,6 @@ export const usePostStore = create((set) => ({
type: null,
isLoading: false,
setTypes: (types: string[]) => set({ types }),
- setType: (type: string) => set({ type }),
+ setType: (type: string | null) => set({ type }),
setIsLoading: (isLoading: boolean) => set({ isLoading }),
}));
diff --git a/src/themes/theme.ts b/src/themes/theme.ts
index 0d81c8f..e85757a 100644
--- a/src/themes/theme.ts
+++ b/src/themes/theme.ts
@@ -3,20 +3,23 @@ import { createTheme } from "@rneui/themed";
export const theme = createTheme({
lightColors: {
primary: "#e7e7e8",
+ secondary: "#f5f5f5",
+ background: "#fff",
error: "#ff0000",
+ warning: "#ffae42",
+ success: "#00a86b",
+ white: "#fff",
+ black: "#000",
},
darkColors: {
- primary: "#000",
- error: "#ff0000",
+ primary: "#100f38",
+ secondary: "#6f6f6f",
background: "#070624",
- secondary: "##0d0c36",
- },
- components: {
- Card: {
- containerStyle: {
- backgroundColor: "#100f38",
- },
- },
+ error: "#ff0000",
+ warning: "#ffae42",
+ success: "#00a86b",
+ white: "#fff",
+ black: "#000",
},
mode: "dark",
});