+ {isSizeSmall && isLTR && toggleSidebarButton}
+
{
{isDarkMode ? : }
+
+
+
+
+
+ {isSizeSmall && !isLTR && toggleSidebarButton}
diff --git a/src/Components/Static/Settings/Item.tsx b/src/Components/Static/Settings/Item.tsx
new file mode 100644
index 00000000..6eb212cd
--- /dev/null
+++ b/src/Components/Static/Settings/Item.tsx
@@ -0,0 +1,137 @@
+// React Imports
+import React, { FC } from "react";
+
+// Material UI Imports
+import {
+ makeStyles,
+ MenuItem,
+ MenuItemProps,
+ Select,
+ Switch,
+ SwitchProps,
+ TextField,
+ TextFieldProps,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ item: {
+ display: "flex",
+ alignItems: "center",
+ width: "100%",
+ padding: theme.spacing(0.75, 6.5),
+
+ [theme.breakpoints.only("xs")]: {
+ padding: theme.spacing(0.5, 3, 0.75, 6),
+ },
+ },
+ label: {
+ flexGrow: 1,
+ },
+}));
+
+interface ItemProps {
+ label: string;
+ action: JSX.Element;
+}
+
+type ExtendItem = Omit
;
+
+const Item: FC = (props) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
+
+ return (
+
+
+ {props.label}
+
+ {props.action}
+
+ );
+};
+
+type SwitchItemProps = ExtendItem & {
+ checked: boolean;
+ onChange: () => void;
+ color?: SwitchProps["color"];
+};
+
+export const SwitchItem: FC = (props) => {
+ const theme = useTheme();
+ const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
+
+ return (
+
+ }
+ />
+ );
+};
+
+type SelectInputProps = ExtendItem & {
+ value: T;
+ values: T[] | readonly T[];
+ onChange: (value: T) => void;
+ defaultValue?: T;
+};
+
+export const SelectInput = (
+ props: SelectInputProps
+): JSX.Element => (
+ - props.onChange(e.target.value as T)}
+ >
+ {props.values.map((val: T, i: number) => (
+
+ ))}
+
+ }
+ />
+);
+
+type InputItemProps = ExtendItem & {
+ value: T;
+ onChange: (value: T) => void;
+ type?: TextFieldProps["type"];
+};
+
+export const InputItem = (
+ props: InputItemProps
+): JSX.Element => {
+ return (
+
- props.onChange(e.target.value as T)}
+ type={props.type}
+ />
+ }
+ />
+ );
+};
+
+export default Item;
diff --git a/src/Components/Static/Settings/Section.tsx b/src/Components/Static/Settings/Section.tsx
new file mode 100644
index 00000000..0f4c0ec5
--- /dev/null
+++ b/src/Components/Static/Settings/Section.tsx
@@ -0,0 +1,57 @@
+// React Imports
+import React, { FC } from "react";
+import clsx from "clsx";
+import HorizontalDivider from "../../Atomic/Divider/Horizontal";
+
+// Material UI Imports
+import { makeStyles, Typography } from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ section: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ width: "100%",
+ borderTop: `2px solid ${theme.palette.text.disabled}`,
+ margin: theme.spacing(2, 0),
+ backgroundColor:
+ theme.palette.type === "dark"
+ ? theme.palette.grey[800]
+ : theme.palette.grey[200],
+ },
+ title: {
+ width: "100%",
+ fontWeight: theme.typography.fontWeightMedium,
+ padding: theme.spacing(2, 1),
+
+ [theme.breakpoints.only("xs")]: {
+ padding: theme.spacing(1),
+ },
+ },
+}));
+
+interface SectionProps {
+ title: string;
+ className?: string;
+}
+
+const Section: FC = (props) => {
+ const classes = useStyles();
+
+ return (
+
+
+ {props.title}
+
+
+ {props.children}
+
+ );
+};
+
+export default Section;
diff --git a/src/Components/Static/Settings/Sections/Display.tsx b/src/Components/Static/Settings/Sections/Display.tsx
new file mode 100644
index 00000000..26ae2789
--- /dev/null
+++ b/src/Components/Static/Settings/Sections/Display.tsx
@@ -0,0 +1,90 @@
+// React Imports
+import React, { FC } from "react";
+import { useLocation } from "react-router-dom";
+import { useTitle } from "../../../../Context/HeadContext";
+import { generateSearch } from "../../../../Utils/funcs";
+import Section from "../Section";
+import Subsection from "../Subsection";
+import Item, { SelectInput, SwitchItem } from "../Item";
+import StyledLink from "../../../Atomic/StyledLink";
+
+// Redux Imports
+import { useSelector } from "react-redux";
+import {
+ toggleDarkMode,
+ getSpacing,
+ changeSpacing,
+ getDirection,
+ changeDirection,
+} from "../../../../Redux";
+import {
+ DEFAULT_DIRECTION,
+ DEFAULT_SPACING,
+ DIRECTIONS,
+ SPACINGS,
+} from "../../../../Redux/display.slice";
+import { useAppDispatch } from "../../../../Store";
+
+// Material UI Imports
+import { useTheme } from "@material-ui/core";
+import { Computer, SettingsBrightness } from "@material-ui/icons";
+
+const Display: FC = () => {
+ const dispatch = useAppDispatch();
+ const location = useLocation();
+ const title = useTitle();
+ const theme = useTheme();
+ const isDarkMode = theme.palette.type === "dark";
+
+ const spacing = useSelector(getSpacing);
+ const direction = useSelector(getDirection);
+
+ return (
+
+ }>
+ dispatch(toggleDarkMode())}
+ />
+ -
+ Colors Page
+
+ }
+ />
+
+ }>
+ dispatch(changeSpacing(val))}
+ />
+ dispatch(changeDirection(val))}
+ />
+
+
+ );
+};
+
+export default Display;
diff --git a/src/Components/Static/Settings/Sections/Profile/LoggedIn.tsx b/src/Components/Static/Settings/Sections/Profile/LoggedIn.tsx
new file mode 100644
index 00000000..b293703d
--- /dev/null
+++ b/src/Components/Static/Settings/Sections/Profile/LoggedIn.tsx
@@ -0,0 +1,290 @@
+// React Imports
+import React, { FC, useState } from "react";
+import { getExtension } from "mime";
+import { useClosableSnackbar } from "../../../../../Hooks";
+import { User } from "../../../../../Context/UserContext";
+
+// Firebase Imports
+import "firebase/auth";
+import "firebase/storage";
+import { useAuth, useStorage } from "../../../../../Utils/Config/firebase";
+
+// Material UI Imports
+import {
+ Avatar,
+ Button,
+ CircularProgress,
+ InputAdornment,
+ makeStyles,
+ TextField,
+ Tooltip,
+ useMediaQuery,
+ useTheme,
+} from "@material-ui/core";
+import { Check, CloudUpload } from "@material-ui/icons";
+import clsx from "clsx";
+
+const useStyles = makeStyles((theme) => ({
+ container: {
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ width: "100%",
+ padding: theme.spacing(2),
+ },
+ info: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ margin: theme.spacing(0, 3, 2),
+
+ [theme.breakpoints.down("sm")]: {
+ flexDirection: "column",
+ },
+ },
+ input: {
+ margin: theme.spacing(1, 1.5),
+
+ [theme.breakpoints.down("sm")]: {
+ width: "100%",
+ margin: theme.spacing(1, 0),
+ },
+ },
+ saveIcon: {
+ cursor: "pointer",
+ },
+ savingSpinner: {
+ color: theme.palette.text.primary,
+ },
+ emailInput: {
+ cursor: "not-allowed",
+ },
+ logout: {
+ borderColor: theme.palette.error.main,
+ color: theme.palette.error.main,
+ marginLeft: "auto",
+
+ [theme.breakpoints.down("sm")]: {
+ marginLeft: 0,
+ },
+ },
+}));
+
+interface LoggedInProps {
+ user: User;
+}
+
+const LoggedIn: FC = (props) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
+
+ return (
+
+ );
+};
+
+const useProfilePictureStyles = makeStyles((theme) => ({
+ container: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ height: 56,
+ width: 56,
+ margin: theme.spacing(1, 1.5),
+ cursor: "pointer",
+ position: "relative",
+ },
+ fileInput: {
+ position: "absolute",
+ left: "0px",
+ top: "0px",
+ opacity: 0,
+ width: "100%",
+ height: "100%",
+ },
+ avatar: {
+ width: "100%",
+ height: "100%",
+ },
+ avatarDimmed: {
+ opacity: 0.5,
+ },
+ spinner: {
+ position: "absolute",
+ color: theme.palette.text.primary,
+ zIndex: 1000,
+ },
+}));
+
+const ProfilePicture: FC = (props) => {
+ const classes = useProfilePictureStyles();
+ const theme = useTheme();
+ const storage = useStorage();
+ const { enqueueSnackbar } = useClosableSnackbar();
+ const [uploading, setUploading] = useState(false);
+
+ const onUpload = async (files: FileList | null) => {
+ if (files === null) return;
+
+ const file = files[0];
+ if (!file) return;
+
+ try {
+ setUploading(true);
+ const size = file.size / 1000 / 1000; // Size in MB
+ if (size > 5) {
+ const formattedSize = size.toFixed(1);
+ throw new Error(
+ `File size must be less than 5 MB (Uploaded file size was ${formattedSize} MB)`
+ );
+ }
+ const ext = getExtension(file.type);
+ const ref = storage
+ .ref()
+ .child(`users/${props.user.id}/profile_picture${ext ? `.${ext}` : ""}`);
+ const upload = await ref.put(file);
+ const url = await upload.ref.getDownloadURL();
+ await props.user.updatePicture(url);
+ enqueueSnackbar("Uploaded New Profile Picture", {
+ variant: "success",
+ });
+ } catch (e: any) {
+ const message = typeof e === "string" ? e : e.message;
+ enqueueSnackbar(message || "An error occurred. Please try again.", {
+ variant: "error",
+ });
+ } finally {
+ setUploading(false);
+ }
+ };
+
+ return (
+
+ );
+};
+
+const NameField: FC = (props) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
+ const { enqueueSnackbar } = useClosableSnackbar();
+ const [name, setName] = useState(props.user.name);
+ const [isNameSaving, setNameSaving] = useState(false);
+
+ const onNameSave = async () => {
+ setNameSaving(true);
+ try {
+ await props.user.updateName(name);
+ enqueueSnackbar("Saved Name", {
+ variant: "success",
+ });
+ } catch (e: any) {
+ const message = typeof e === "string" ? e : e.message;
+ enqueueSnackbar(message || "An error occurred. Please try again.", {
+ variant: "error",
+ });
+ } finally {
+ setNameSaving(false);
+ }
+ };
+
+ return (
+ setName(e.target.value)}
+ name="name"
+ type="text"
+ label="Name"
+ variant="outlined"
+ size={isSizeXS ? "small" : "medium"}
+ InputProps={{
+ endAdornment: (
+
+ {name === props.user.name ? (
+
+
+
+ ) : isNameSaving ? (
+
+ ) : (
+
+
+
+ )}
+
+ ),
+ }}
+ className={classes.input}
+ />
+ );
+};
+
+const SignOutButton: FC = () => {
+ const classes = useStyles();
+ const auth = useAuth();
+ const { enqueueSnackbar } = useClosableSnackbar();
+
+ return (
+
+ );
+};
+
+export default LoggedIn;
diff --git a/src/Components/Static/Settings/Sections/Profile/LoggedOut.tsx b/src/Components/Static/Settings/Sections/Profile/LoggedOut.tsx
new file mode 100644
index 00000000..e646d687
--- /dev/null
+++ b/src/Components/Static/Settings/Sections/Profile/LoggedOut.tsx
@@ -0,0 +1,60 @@
+// React Imports
+import React, { FC } from "react";
+import { useClosableSnackbar } from "../../../../../Hooks";
+
+// Firebase Imports
+import "firebase/auth";
+import firebase, { useAuth } from "../../../../../Utils/Config/firebase";
+import { StyledFirebaseAuth } from "react-firebaseui";
+
+// Material UI Imports
+import { makeStyles, Typography } from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ container: {
+ margin: theme.spacing(2),
+ },
+}));
+
+const NotLoggedIn: FC = () => {
+ const classes = useStyles();
+ const auth = useAuth();
+ const { enqueueSnackbar } = useClosableSnackbar();
+
+ return (
+
+ You are not signed in.
+
+
+ );
+};
+
+export default NotLoggedIn;
diff --git a/src/Components/Static/Settings/Sections/Profile/index.tsx b/src/Components/Static/Settings/Sections/Profile/index.tsx
new file mode 100644
index 00000000..8c6ff62a
--- /dev/null
+++ b/src/Components/Static/Settings/Sections/Profile/index.tsx
@@ -0,0 +1,20 @@
+// React Imports
+import React, { FC } from "react";
+import LoggedIn from "./LoggedIn";
+import NotLoggedIn from "./LoggedOut";
+import Section from "../../Section";
+import { useUser } from "../../../../../Context/UserContext";
+import HorizontalDivider from "../../../../Atomic/Divider/Horizontal";
+
+const Profile: FC = () => {
+ const user = useUser();
+
+ return (
+
+ );
+};
+
+export default Profile;
diff --git a/src/Components/Static/Settings/Subsection.tsx b/src/Components/Static/Settings/Subsection.tsx
new file mode 100644
index 00000000..2f82b3cf
--- /dev/null
+++ b/src/Components/Static/Settings/Subsection.tsx
@@ -0,0 +1,72 @@
+// React Imports
+import React, { cloneElement, FC } from "react";
+import clsx from "clsx";
+import HorizontalDivider from "../../Atomic/Divider/Horizontal";
+
+// Material UI Imports
+import {
+ makeStyles,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ subsection: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ width: "100%",
+ },
+ titleContainer: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ width: "100%",
+ padding: theme.spacing(2, 1, 0.5),
+ },
+ icon: {
+ marginLeft: theme.spacing(1),
+ },
+ title: {
+ flexGrow: 1,
+ marginLeft: theme.spacing(1.5),
+ },
+}));
+
+interface SubsectionProps {
+ title: string;
+ icon: JSX.Element;
+ className?: string;
+}
+
+const Subsection: FC = (props) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
+
+ const icon = cloneElement(props.icon, {
+ className: classes.icon,
+ fontSize: isSizeXS ? "small" : "default",
+ });
+
+ return (
+
+
+ {icon}
+
+ {props.title}
+
+
+ {props.children}
+
+
+ );
+};
+
+export default Subsection;
diff --git a/src/Context/UserContext.tsx b/src/Context/UserContext.tsx
new file mode 100644
index 00000000..0191f490
--- /dev/null
+++ b/src/Context/UserContext.tsx
@@ -0,0 +1,72 @@
+// React Imports
+import React, {
+ createContext,
+ FC,
+ useContext,
+ useEffect,
+ useState,
+} from "react";
+
+// Firebase Imports
+import "firebase/auth";
+import firebase, { useAuth } from "../Utils/Config/firebase";
+
+interface UserInfo {
+ id: string;
+ name: string;
+ email: string;
+ picture: string;
+}
+
+export interface User extends UserInfo {
+ updateName: (newName: string) => Promise;
+ updatePicture: (newPicture: string) => Promise;
+}
+
+const mapFirebaseUser = (user: firebase.User | null): UserInfo | null => {
+ if (user === null) return user;
+ return {
+ id: user.uid,
+ name: user.displayName ?? "",
+ email: user.email ?? "",
+ picture: user.photoURL ?? "",
+ };
+};
+
+const UserContext = createContext(null);
+
+export const UserProvider: FC = ({ children }) => {
+ const auth = useAuth();
+ const [user, setUser] = useState(
+ mapFirebaseUser(auth.currentUser)
+ );
+
+ const updateName = async (newName: string) => {
+ await auth.currentUser!.updateProfile({
+ displayName: newName,
+ });
+ setUser({ ...user!, name: newName });
+ };
+
+ const updatePicture = async (newPicture: string) => {
+ await auth.currentUser!.updateProfile({
+ photoURL: newPicture,
+ });
+ setUser({ ...user!, picture: newPicture });
+ };
+
+ useEffect(
+ () => auth.onAuthStateChanged((user) => setUser(mapFirebaseUser(user))),
+ [auth]
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useUser = (): User | null => useContext(UserContext);
diff --git a/src/Pages/Contact/index.tsx b/src/Pages/Contact/index.tsx
index ae7566ea..ebbfa312 100644
--- a/src/Pages/Contact/index.tsx
+++ b/src/Pages/Contact/index.tsx
@@ -11,6 +11,7 @@ import {
import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha";
import emailjs from "emailjs-com";
import { useAnalytics, useClosableSnackbar } from "../../Hooks";
+import { useUser } from "../../Context/UserContext";
import HorizontalDivider from "../../Components/Atomic/Divider/Horizontal";
import { generatePageTitle } from "../../Utils/funcs";
@@ -33,12 +34,12 @@ import {
import { Rating } from "@material-ui/lab";
const useStyles = makeStyles((theme) => ({
- title: {
- marginTop: theme.spacing(0.5),
+ divider: {
+ margin: theme.spacing(1.5, 0, 1),
},
container: {
width: "100%",
- marginTop: theme.spacing(1.5),
+ marginTop: theme.spacing(2),
padding: theme.spacing(2),
},
form: {
@@ -79,11 +80,17 @@ const Contact: FC = () => {
const classes = useStyles();
const { enqueueSnackbar } = useClosableSnackbar();
const firestore = useFirestore();
+ const user = useUser();
const theme = useTheme();
const isSizeXS = useMediaQuery(theme.breakpoints.only("xs"));
- const { formState, control, handleSubmit, reset } = useForm();
+ const { formState, control, handleSubmit, reset } = useForm({
+ defaultValues: {
+ name: user?.name,
+ email: user?.email,
+ },
+ });
const [loading, setLoading] = useState(false);
const recaptchaRef = useRef(null);
@@ -119,6 +126,7 @@ const Contact: FC = () => {
Object.entries(inputs).filter(([_, v]) => v !== undefined)
);
data.timestamp = new Date();
+ data.user = user === null ? null : user.id;
data["g-recaptcha-response"] = recaptcha;
try {
@@ -144,7 +152,7 @@ const Contact: FC = () => {
bugs: "",
});
recaptchaRef.current?.reset();
- } catch (e) {
+ } catch (e: any) {
const message =
(typeof e === "string" ? e : e.message) ||
"An error occurred. Please try again.";
@@ -171,8 +179,8 @@ const Contact: FC = () => {
{generatePageTitle("Contact")}
-
-
+
+
Let's get in touch!
diff --git a/src/Pages/Settings/index.tsx b/src/Pages/Settings/index.tsx
new file mode 100644
index 00000000..acf6b21a
--- /dev/null
+++ b/src/Pages/Settings/index.tsx
@@ -0,0 +1,45 @@
+// React Imports
+import React, { FC } from "react";
+import { Helmet } from "react-helmet";
+import { generatePageTitle } from "../../Utils/funcs";
+import HorizontalDivider from "../../Components/Atomic/Divider/Horizontal";
+import Profile from "../../Components/Static/Settings/Sections/Profile";
+import Display from "../../Components/Static/Settings/Sections/Display";
+
+// Material UI Imports
+import { makeStyles, Typography } from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ divider: {
+ margin: theme.spacing(1.5, 0, 1),
+ },
+ settings: {
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ justifyContent: "stretch",
+ width: "100%",
+ },
+}));
+
+const Settings: FC = () => {
+ const classes = useStyles();
+
+ return (
+ <>
+
+ {generatePageTitle("Settings")}
+
+
+ >
+ );
+};
+
+export default Settings;
diff --git a/src/Redux/display.slice.ts b/src/Redux/display.slice.ts
index e3e5f1ad..febec638 100644
--- a/src/Redux/display.slice.ts
+++ b/src/Redux/display.slice.ts
@@ -1,6 +1,14 @@
+import { Direction } from "@material-ui/core";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "../Store";
+export const SPACINGS = [6, 8, 10] as const;
+export const DEFAULT_SPACING = 8 as const;
+export type Spacing = typeof SPACINGS[number];
+
+export const DIRECTIONS = ["ltr", "rtl"] as const;
+export const DEFAULT_DIRECTION = "ltr" as const;
+
export const SCHEMES = ["primary", "secondary"] as const;
export type Scheme = typeof SCHEMES[number];
@@ -45,6 +53,8 @@ export type Shade = typeof SHADES[number];
export interface DisplayState {
isDarkMode: boolean | null;
isSidebarOpen: boolean;
+ spacing: Spacing;
+ direction: Direction;
colors: Record;
shades: Record;
}
@@ -52,6 +62,8 @@ export interface DisplayState {
export const initialDisplayState: DisplayState = {
isDarkMode: null,
isSidebarOpen: false,
+ spacing: DEFAULT_SPACING,
+ direction: DEFAULT_DIRECTION,
colors: {
primary: "lightBlue",
secondary: "amber",
@@ -74,6 +86,17 @@ const displaySlice = createSlice({
...state,
isSidebarOpen: action.payload ?? !state.isSidebarOpen,
}),
+ changeSpacing: (state, action: PayloadAction) => ({
+ ...state,
+ spacing: action.payload,
+ }),
+ changeDirection: (
+ state,
+ action: PayloadAction
+ ) => ({
+ ...state,
+ direction: action.payload,
+ }),
changeColor: (
state,
action: PayloadAction>
@@ -123,6 +146,8 @@ const displaySlice = createSlice({
export const {
toggleDarkMode,
toggleSidebar,
+ changeSpacing,
+ changeDirection,
changeColor,
changeShade,
changeShadeAndColors,
@@ -137,6 +162,12 @@ export const getIsSidebarOpen = (
state: RootState
): DisplayState["isSidebarOpen"] => state.display.isSidebarOpen;
+export const getSpacing = (state: RootState): DisplayState["spacing"] =>
+ state.display.spacing;
+
+export const getDirection = (state: RootState): DisplayState["direction"] =>
+ state.display.direction;
+
export const getColors = (state: RootState): DisplayState["colors"] =>
state.display.colors;
diff --git a/src/Redux/index.ts b/src/Redux/index.ts
index 55a37d1b..4273d20e 100644
--- a/src/Redux/index.ts
+++ b/src/Redux/index.ts
@@ -8,12 +8,16 @@ export {
// -> Selectors
getIsDarkMode,
getIsSidebarOpen,
+ getSpacing,
+ getDirection,
getColors,
getShades,
getIsDefaultColors,
// -> Actions
toggleDarkMode,
toggleSidebar,
+ changeSpacing,
+ changeDirection,
changeColor,
changeShade,
changeShadeAndColors,
diff --git a/src/Theme.tsx b/src/Theme.tsx
index d3c99ac3..62de6cbf 100644
--- a/src/Theme.tsx
+++ b/src/Theme.tsx
@@ -3,7 +3,15 @@ import React, { FC } from "react";
//Redux Imports
import { useSelector } from "react-redux";
-import { getColors, getIsDarkMode, getShades, toggleDarkMode } from "./Redux";
+import {
+ getColors,
+ getDirection,
+ getIsDarkMode,
+ getShades,
+ getSpacing,
+ toggleDarkMode,
+} from "./Redux";
+import { DEFAULT_DIRECTION, DEFAULT_SPACING } from "./Redux/display.slice";
import { useAppDispatch } from "./Store";
//Material UI Imports
@@ -14,6 +22,7 @@ import {
CssBaseline,
} from "@material-ui/core";
import * as muiColors from "@material-ui/core/colors";
+import createSpacing from "@material-ui/core/styles/createSpacing";
export const alternativeFont = "Arial, sans-serif";
@@ -21,6 +30,8 @@ const Theme: FC = ({ children }) => {
const dispatch = useAppDispatch();
const colors = useSelector(getColors);
const shades = useSelector(getShades);
+ const spacing = useSelector(getSpacing);
+ const direction = useSelector(getDirection);
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
const isDarkMode = useSelector(getIsDarkMode);
@@ -47,6 +58,8 @@ const Theme: FC = ({ children }) => {
},
},
},
+ direction: direction ?? DEFAULT_DIRECTION,
+ spacing: createSpacing(spacing ?? DEFAULT_SPACING),
palette: {
type: isDarkMode ? "dark" : "light",
primary: {
diff --git a/src/Utils/Config/firebase.ts b/src/Utils/Config/firebase.ts
index 6c0d70f6..2043b52c 100644
--- a/src/Utils/Config/firebase.ts
+++ b/src/Utils/Config/firebase.ts
@@ -4,7 +4,7 @@ import "firebase/performance";
export const config = {
apiKey: "AIzaSyBkV0LzaVCDpgr6-f-60MArbZWlyJ7utYU",
- authDomain: "yashtotale.firebaseapp.com",
+ authDomain: "yash-totale.firebaseapp.com",
projectId: "yash-totale",
storageBucket: "yash-totale.appspot.com",
messagingSenderId: "37331567202",
@@ -16,6 +16,14 @@ firebase.initializeApp(config);
export const performance = firebase.performance();
+let auth: firebase.auth.Auth;
+export const useAuth = (): firebase.auth.Auth => {
+ if (!auth) {
+ auth = firebase.auth();
+ }
+ return auth;
+};
+
let firestore: firebase.firestore.Firestore;
export const useFirestore = (): firebase.firestore.Firestore => {
if (!firestore) {
@@ -24,6 +32,14 @@ export const useFirestore = (): firebase.firestore.Firestore => {
return firestore;
};
+let storage: firebase.storage.Storage;
+export const useStorage = (): firebase.storage.Storage => {
+ if (!storage) {
+ storage = firebase.storage();
+ }
+ return storage;
+};
+
let analytics: firebase.analytics.Analytics;
export const useAnalytics = (
triggerCall = true