├── functions ├── tsconfig.dev.json ├── .gitignore ├── tsconfig.json ├── .eslintrc.js ├── package.json └── src │ └── index.ts ├── public ├── favicon.ico ├── images │ ├── googlelogo.png │ ├── recCommsArt.png │ ├── redditlogo.png │ ├── redditPersonalHome.png │ ├── redditText.svg │ └── redditFace.svg └── vercel.svg ├── next.config.js ├── Dockerfile ├── src ├── atoms │ ├── userAtom.ts │ ├── authModalAtom.ts │ ├── directoryMenuAtom.ts │ ├── postsAtom.ts │ └── communitiesAtom.ts ├── styles │ └── globals.css ├── components │ ├── Main │ │ └── index.tsx │ ├── Post │ │ ├── PostsHome.tsx │ │ ├── Loader.tsx │ │ ├── PostForm │ │ │ ├── TabItem.tsx │ │ │ ├── TextInputs.tsx │ │ │ ├── ImageUpload.tsx │ │ │ └── NewPostForm.tsx │ │ ├── Comments │ │ │ ├── Input.tsx │ │ │ ├── CommentItem.tsx │ │ │ └── index.tsx │ │ ├── PostItem │ │ │ └── index.tsx │ │ └── Posts.tsx │ ├── Layout │ │ ├── index.tsx │ │ ├── InputField.tsx │ │ ├── PageContent.tsx │ │ └── InputItem.tsx │ ├── Navbar │ │ ├── RightContent │ │ │ ├── ActionIcon.tsx │ │ │ ├── index.tsx │ │ │ ├── ProfileMenu │ │ │ │ ├── NoUserList.tsx │ │ │ │ ├── UserList.tsx │ │ │ │ └── MenuWrapper.tsx │ │ │ ├── AuthButtons.tsx │ │ │ └── Icons.tsx │ │ ├── Directory │ │ │ ├── Moderating.tsx │ │ │ ├── MenuListItem.tsx │ │ │ ├── MyCommunities.tsx │ │ │ ├── index.tsx │ │ │ └── Communities.tsx │ │ ├── SearchInput.tsx │ │ └── index.tsx │ ├── Community │ │ ├── CommunityNotFound.tsx │ │ ├── Premium.tsx │ │ ├── PersonalHome.tsx │ │ ├── CreatePostLink.tsx │ │ ├── Temp.tsx │ │ ├── Header.tsx │ │ ├── Recommendations.tsx │ │ └── About.tsx │ └── Modal │ │ ├── ModalWrapper.tsx │ │ ├── Auth │ │ ├── Inputs.tsx │ │ ├── OAuthButtons.tsx │ │ ├── SignUp.tsx │ │ ├── Login.tsx │ │ ├── index.tsx │ │ └── ResetPassword.tsx │ │ └── CreateCommunity │ │ └── index.tsx ├── firebase │ ├── errors.ts │ ├── authFunctions.ts │ └── clientApp.ts ├── pages │ ├── api │ │ └── hello.ts │ ├── _document.tsx │ ├── _app.tsx │ ├── r │ │ └── [community] │ │ │ ├── submit.tsx │ │ │ ├── index.tsx │ │ │ └── comments │ │ │ └── [pid].tsx │ └── index.tsx ├── helpers │ └── firestore.ts ├── chakra │ ├── theme.ts │ ├── input.ts │ └── button.ts └── hooks │ ├── useSelectFile.ts │ ├── useAuth.ts │ ├── useDirectory.ts │ ├── useCommunityData.ts │ └── usePosts.ts ├── firebase.json ├── Eks-terraform ├── backend.tf ├── provider.tf └── main.tf ├── next-env.d.ts ├── service.yml ├── deployment.yml ├── tsconfig.json ├── ingress.yml ├── package.json ├── README.md └── Installation-script.sh /functions/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | ".eslintrc.js" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aj7Ay/reddit-clone-k8s/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/images/googlelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aj7Ay/reddit-clone-k8s/HEAD/public/images/googlelogo.png -------------------------------------------------------------------------------- /public/images/recCommsArt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aj7Ay/reddit-clone-k8s/HEAD/public/images/recCommsArt.png -------------------------------------------------------------------------------- /public/images/redditlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aj7Ay/reddit-clone-k8s/HEAD/public/images/redditlogo.png -------------------------------------------------------------------------------- /public/images/redditPersonalHome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aj7Ay/reddit-clone-k8s/HEAD/public/images/redditPersonalHome.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:19-alpine3.15 2 | 3 | WORKDIR /reddit-clone 4 | 5 | COPY . /reddit-clone 6 | RUN npm install 7 | 8 | EXPOSE 3000 9 | CMD ["npm","run","dev"] 10 | -------------------------------------------------------------------------------- /src/atoms/userAtom.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | 3 | const defaultUserState = {}; 4 | 5 | export const userState = atom({ 6 | key: "userState", 7 | default: null, 8 | }); 9 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "predeploy": [ 4 | "npm --prefix \"$RESOURCE_DIR\" run lint", 5 | "npm --prefix \"$RESOURCE_DIR\" run build" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /functions/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled JavaScript files 2 | lib/**/*.js 3 | lib/**/*.js.map 4 | 5 | # TypeScript v1 declaration files 6 | typings/ 7 | 8 | # Node.js dependency directory 9 | node_modules/ 10 | -------------------------------------------------------------------------------- /Eks-terraform/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "ajay-mrcloudbook777" # Replace with your actual S3 bucket name 4 | key = "EKS/terraform.tfstate" 5 | region = "ap-south-1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: "Open Sans", sans-serif; 6 | } 7 | 8 | a { 9 | color: inherit; 10 | text-decoration: none; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | } 16 | -------------------------------------------------------------------------------- /service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: reddit-clone-service 5 | spec: 6 | selector: 7 | app: reddit-clone 8 | ports: 9 | - port: 80 10 | targetPort: 3000 11 | protocol: TCP 12 | type: LoadBalancer 13 | 14 | -------------------------------------------------------------------------------- /Eks-terraform/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.0" 6 | } 7 | } 8 | } 9 | 10 | # Configure the AWS Provider 11 | provider "aws" { 12 | region = "ap-south-1" 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | 4 | const Main: React.FC<{}> = () => { 5 | return ( 6 |
7 | Here is main 8 | hehe 9 |
10 | ); 11 | }; 12 | export default Main; 13 | -------------------------------------------------------------------------------- /src/firebase/errors.ts: -------------------------------------------------------------------------------- 1 | export const FIREBASE_ERRORS = { 2 | "Firebase: Error (auth/email-already-in-use).": 3 | "A user with that email already exists", 4 | 5 | "Firebase: Error (auth/user-not-found).": "Invalid email or password", 6 | "Firebase: Error (auth/wrong-password).": "Invalid email or password", 7 | }; 8 | -------------------------------------------------------------------------------- /functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitReturns": true, 5 | "noUnusedLocals": true, 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "strict": true, 9 | "target": "es2017" 10 | }, 11 | "compileOnSave": true, 12 | "include": [ 13 | "src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Post/PostsHome.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | type PostsHomeProps = {}; 4 | 5 | const PostsHome: React.FC = () => { 6 | const [loading, setLoading] = useState(false); 7 | 8 | // stuff related to home page only 9 | return
Home Posts Wrapper
; 10 | }; 11 | export default PostsHome; 12 | -------------------------------------------------------------------------------- /src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /src/atoms/authModalAtom.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | 3 | export interface AuthModalState { 4 | open: boolean; 5 | view: ModalView; 6 | } 7 | 8 | export type ModalView = "login" | "signup" | "resetPassword"; 9 | 10 | const defaultModalState: AuthModalState = { 11 | open: false, 12 | view: "login", 13 | }; 14 | 15 | export const authModalState = atom({ 16 | key: "authModalState", 17 | default: defaultModalState, 18 | }); 19 | -------------------------------------------------------------------------------- /deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: reddit-clone-deployment 5 | labels: 6 | app: reddit-clone 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: reddit-clone 12 | template: 13 | metadata: 14 | labels: 15 | app: reddit-clone 16 | spec: 17 | containers: 18 | - name: reddit-clone 19 | image: sevenajay/reddit:latest 20 | ports: 21 | - containerPort: 3000 22 | -------------------------------------------------------------------------------- /src/helpers/firestore.ts: -------------------------------------------------------------------------------- 1 | import { user } from "firebase-functions/v1/auth"; 2 | import { query, collection, getDocs } from "firebase/firestore"; 3 | import { firestore } from "../firebase/clientApp"; 4 | 5 | export const getMySnippets = async (userId: string) => { 6 | const snippetQuery = query( 7 | collection(firestore, `users/${userId}/communitySnippets`) 8 | ); 9 | 10 | const snippetDocs = await getDocs(snippetQuery); 11 | return snippetDocs.docs.map((doc) => ({ ...doc.data() })); 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useAuthState } from "react-firebase-hooks/auth"; 3 | import { auth } from "../../firebase/clientApp"; 4 | import useAuth from "../../hooks/useAuth"; 5 | import Navbar from "../Navbar"; 6 | import AuthModal from "../Modal/Auth"; 7 | 8 | const Layout: React.FC = ({ children }) => { 9 | // useAuth(); // will implement later at end of tutorial 10 | 11 | return ( 12 | <> 13 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export default Layout; 20 | -------------------------------------------------------------------------------- /src/firebase/authFunctions.ts: -------------------------------------------------------------------------------- 1 | import { GoogleAuthProvider, signInWithPopup, signOut } from "firebase/auth"; 2 | 3 | import { auth } from "./clientApp"; 4 | 5 | export const signInWithGoogle: any = async () => 6 | signInWithPopup(auth, new GoogleAuthProvider()); 7 | 8 | export const signUpWithEmailAndPassword = async ( 9 | email: string, 10 | password: string 11 | ) => {}; 12 | 13 | export const loginWithEmaiAndPassword = async ( 14 | email: string, 15 | password: string 16 | ) => {}; 17 | 18 | export const logout = () => signOut(auth); 19 | -------------------------------------------------------------------------------- /src/components/Navbar/RightContent/ActionIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Icon } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | type ActionIcon = { 5 | icon: any; 6 | size: number; 7 | onClick?: any; 8 | }; 9 | 10 | const ActionIcon: React.FC = ({ icon, size }) => { 11 | return ( 12 | 19 | 20 | 21 | ); 22 | }; 23 | export default ActionIcon; 24 | -------------------------------------------------------------------------------- /src/chakra/theme.ts: -------------------------------------------------------------------------------- 1 | import { extendTheme } from "@chakra-ui/react"; 2 | import { Button } from "./button"; 3 | import { Input } from "./input"; 4 | 5 | export const theme = extendTheme({ 6 | colors: { 7 | brand: { 8 | 100: "#FF3C00", 9 | }, 10 | }, 11 | fonts: { 12 | body: "Open Sans, sans-serif", 13 | }, 14 | styles: { 15 | global: () => ({ 16 | body: { 17 | bg: "gray.200", 18 | }, 19 | }), 20 | }, 21 | components: { 22 | Button, 23 | // Input, // not working for some reason - come back to this 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/Community/CommunityNotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, Button } from "@chakra-ui/react"; 3 | import Link from "next/link"; 4 | 5 | const CommunityNotFound: React.FC = () => { 6 | return ( 7 | 13 | Sorry, that community does not exist or has been banned 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | export default CommunityNotFound; 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProvider } from "@chakra-ui/react"; 2 | import type { AppProps } from "next/app"; 3 | import { RecoilRoot } from "recoil"; 4 | import { theme } from "../chakra/theme"; 5 | import Layout from "../components/Layout"; 6 | import "../firebase/clientApp"; 7 | import "../styles/globals.css"; 8 | 9 | function MyApp({ Component, pageProps }: AppProps) { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | export default MyApp; 22 | -------------------------------------------------------------------------------- /ingress.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-reddit-app 5 | spec: 6 | rules: 7 | - host: "domain.com" 8 | http: 9 | paths: 10 | - pathType: Prefix 11 | path: "/test" 12 | backend: 13 | service: 14 | name: reddit-clone-service 15 | port: 16 | number: 3000 17 | - host: "*.domain.com" 18 | http: 19 | paths: 20 | - pathType: Prefix 21 | path: "/test" 22 | backend: 23 | service: 24 | name: reddit-clone-service 25 | port: 26 | number: 3000 27 | -------------------------------------------------------------------------------- /src/components/Navbar/RightContent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex } from "@chakra-ui/react"; 3 | import { User } from "firebase/auth"; 4 | import AuthModal from "../../Modal/Auth"; 5 | import AuthButtons from "./AuthButtons"; 6 | import Icons from "./Icons"; 7 | import MenuWrapper from "./ProfileMenu/MenuWrapper"; 8 | 9 | type RightContentProps = { 10 | user: User; 11 | }; 12 | 13 | const RightContent: React.FC = ({ user }) => { 14 | return ( 15 | <> 16 | 17 | 18 | {user ? : } 19 | 20 | 21 | 22 | ); 23 | }; 24 | export default RightContent; 25 | -------------------------------------------------------------------------------- /functions/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:import/errors", 10 | "plugin:import/warnings", 11 | "plugin:import/typescript", 12 | "google", 13 | "plugin:@typescript-eslint/recommended", 14 | ], 15 | parser: "@typescript-eslint/parser", 16 | parserOptions: { 17 | project: ["tsconfig.json", "tsconfig.dev.json"], 18 | sourceType: "module", 19 | }, 20 | ignorePatterns: [ 21 | "/lib/**/*", // Ignore built files. 22 | ], 23 | plugins: [ 24 | "@typescript-eslint", 25 | "import", 26 | ], 27 | rules: { 28 | "quotes": ["error", "double"], 29 | "import/no-unresolved": 0, 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /src/components/Modal/ModalWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Modal, 4 | ModalOverlay, 5 | ModalContent, 6 | ModalHeader, 7 | ModalCloseButton, 8 | ModalBody, 9 | ModalFooter, 10 | useDisclosure, 11 | } from "@chakra-ui/react"; 12 | import React from "react"; 13 | 14 | type ModalWrapperProps = { 15 | isOpen: boolean; 16 | onClose: () => void; 17 | }; 18 | 19 | const ModalWrapper: React.FC = ({ 20 | children, 21 | isOpen, 22 | onClose, 23 | }) => { 24 | return ( 25 | <> 26 | 27 | 28 | {children} 29 | 30 | 31 | ); 32 | }; 33 | export default ModalWrapper; 34 | -------------------------------------------------------------------------------- /src/hooks/useSelectFile.ts: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const useSelectFile = () => { 4 | const [selectedFile, setSelectedFile] = useState(); 5 | 6 | const onSelectFile = (event: React.ChangeEvent) => { 7 | console.log("THIS IS HAPPENING", event); 8 | 9 | const reader = new FileReader(); 10 | 11 | if (event.target.files?.[0]) { 12 | reader.readAsDataURL(event.target.files[0]); 13 | } 14 | 15 | reader.onload = (readerEvent) => { 16 | if (readerEvent.target?.result) { 17 | setSelectedFile(readerEvent.target.result as string); 18 | } 19 | }; 20 | }; 21 | 22 | return { 23 | selectedFile, 24 | setSelectedFile, 25 | onSelectFile, 26 | }; 27 | }; 28 | export default useSelectFile; 29 | -------------------------------------------------------------------------------- /src/atoms/directoryMenuAtom.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | import { IconType } from "react-icons"; 3 | import { TiHome } from "react-icons/ti"; 4 | 5 | export type DirectoryMenuItem = { 6 | displayText: string; 7 | link: string; 8 | icon: IconType; 9 | iconColor: string; 10 | imageURL?: string; 11 | }; 12 | 13 | interface DirectoryMenuState { 14 | isOpen: boolean; 15 | selectedMenuItem: DirectoryMenuItem; 16 | } 17 | 18 | export const defaultMenuItem = { 19 | displayText: "Home", 20 | link: "/", 21 | icon: TiHome, 22 | iconColor: "black", 23 | }; 24 | 25 | export const defaultMenuState: DirectoryMenuState = { 26 | isOpen: false, 27 | selectedMenuItem: defaultMenuItem, 28 | }; 29 | 30 | export const directoryMenuState = atom({ 31 | key: "directoryMenuState", 32 | default: defaultMenuState, 33 | }); 34 | -------------------------------------------------------------------------------- /src/components/Post/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Stack, Box, SkeletonText, Skeleton } from "@chakra-ui/react"; 3 | 4 | const PostLoader: React.FC = () => { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | export default PostLoader; 21 | -------------------------------------------------------------------------------- /src/components/Modal/Auth/Inputs.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { useRecoilState, useRecoilValue } from "recoil"; 4 | import { authModalState, ModalView } from "../../../atoms/authModalAtom"; 5 | import Login from "./Login"; 6 | import SignUp from "./SignUp"; 7 | 8 | type AuthInputsProps = { 9 | toggleView: (view: ModalView) => void; 10 | }; 11 | 12 | const AuthInputs: React.FC = ({ toggleView }) => { 13 | const modalState = useRecoilValue(authModalState); 14 | 15 | return ( 16 | 17 | {modalState.view === "login" ? ( 18 | 19 | ) : ( 20 | 21 | )} 22 | 23 | ); 24 | }; 25 | export default AuthInputs; 26 | -------------------------------------------------------------------------------- /src/chakra/input.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentStyleConfig } from "@chakra-ui/theme"; 2 | 3 | export const Input: ComponentStyleConfig = { 4 | baseStyle: { 5 | field: { 6 | fontSize: "10pt", 7 | bg: "gray.50", 8 | _placeholder: { 9 | color: "gray.500", 10 | }, 11 | _hover: { 12 | bg: "white", 13 | border: "1px solid", 14 | borderColor: "blue.500", 15 | }, 16 | _focus: { 17 | outline: "none", 18 | border: "1px solid", 19 | borderColor: "blue.500", 20 | }, 21 | }, 22 | addons: { 23 | height: "30px", 24 | }, 25 | }, 26 | sizes: { 27 | md: { 28 | field: { 29 | // height: "30px", 30 | fontSize: "10pt", 31 | }, 32 | }, 33 | }, 34 | variants: {}, 35 | defaultProps: { 36 | variant: null, 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/Navbar/RightContent/ProfileMenu/NoUserList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MenuItem, Flex, Icon } from "@chakra-ui/react"; 3 | import { MdOutlineLogin } from "react-icons/md"; 4 | import { AuthModalState } from "../../../../atoms/authModalAtom"; 5 | 6 | type NoUserListProps = { 7 | setModalState: (value: AuthModalState) => void; 8 | }; 9 | 10 | const NoUserList: React.FC = ({ setModalState }) => { 11 | return ( 12 | <> 13 | setModalState({ open: true, view: "login" })} 18 | > 19 | 20 | 21 | Log In / Sign Up 22 | 23 | 24 | 25 | ); 26 | }; 27 | export default NoUserList; 28 | -------------------------------------------------------------------------------- /src/components/Community/Premium.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, Icon, Text, Stack, Button } from "@chakra-ui/react"; 3 | import { GiCheckedShield } from "react-icons/gi"; 4 | 5 | const Premium: React.FC = () => { 6 | return ( 7 | 16 | 17 | 18 | 19 | Reddit Premium 20 | The best Reddit experience, with monthly Coins 21 | 22 | 23 | 26 | 27 | ); 28 | }; 29 | export default Premium; 30 | -------------------------------------------------------------------------------- /src/firebase/clientApp.ts: -------------------------------------------------------------------------------- 1 | import { initializeApp, getApp, getApps } from "firebase/app"; 2 | import { getAuth } from "firebase/auth"; 3 | import { getFirestore } from "firebase/firestore"; 4 | import { getStorage } from "firebase/storage"; 5 | 6 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 7 | const firebaseConfig = { 8 | apiKey: "AIzaSyBqe95nCUomWkU9PnZT_OlV_NCAZnTmyz0", 9 | authDomain: "reddit-clone-r.firebaseapp.com", 10 | projectId: "reddit-clone-r", 11 | storageBucket: "reddit-clone-r.appspot.com", 12 | messagingSenderId: "233455437474", 13 | appId: "1:233455437474:web:5f21ac4b94a42fddb379fb", 14 | measurementId: "G-3L9HMZR4XQ" 15 | }; 16 | // Initialize Firebase for SSR 17 | const app = !getApps().length ? initializeApp(firebaseConfig) : getApp(); 18 | const firestore = getFirestore(app); 19 | const auth = getAuth(app); 20 | const storage = getStorage(app); 21 | 22 | export { app, auth, firestore, storage }; 23 | -------------------------------------------------------------------------------- /src/chakra/button.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentStyleConfig } from "@chakra-ui/theme"; 2 | 3 | export const Button: ComponentStyleConfig = { 4 | baseStyle: { 5 | borderRadius: "60px", 6 | fontSize: "10pt", 7 | fontWeight: 700, 8 | _focus: { 9 | boxShadow: "none", 10 | }, 11 | }, 12 | sizes: { 13 | sm: { 14 | fontSize: "8pt", 15 | }, 16 | md: { 17 | fontSize: "10pt", 18 | // height: "28px", 19 | }, 20 | }, 21 | variants: { 22 | solid: { 23 | color: "white", 24 | bg: "blue.500", 25 | _hover: { 26 | bg: "blue.400", 27 | }, 28 | }, 29 | outline: { 30 | color: "blue.500", 31 | border: "1px solid", 32 | borderColor: "blue.500", 33 | }, 34 | oauth: { 35 | height: "34px", 36 | border: "1px solid", 37 | borderColor: "gray.300", 38 | _hover: { 39 | bg: "gray.50", 40 | }, 41 | }, 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "scripts": { 4 | "lint": "eslint --ext .js,.ts", 5 | "build": "tsc", 6 | "serve": "npm run build && firebase emulators:start --only functions", 7 | "shell": "npm run build && firebase functions:shell", 8 | "start": "npm run shell", 9 | "deploy": "firebase deploy --only functions", 10 | "logs": "firebase functions:log" 11 | }, 12 | "engines": { 13 | "node": "16" 14 | }, 15 | "main": "lib/index.js", 16 | "dependencies": { 17 | "firebase-admin": "^10.0.2", 18 | "firebase-functions": "^3.18.0" 19 | }, 20 | "devDependencies": { 21 | "@typescript-eslint/eslint-plugin": "^5.12.0", 22 | "@typescript-eslint/parser": "^5.12.0", 23 | "eslint": "^8.9.0", 24 | "eslint-config-google": "^0.14.0", 25 | "eslint-plugin-import": "^2.25.4", 26 | "firebase-functions-test": "^0.2.0", 27 | "typescript": "^4.5.4" 28 | }, 29 | "private": true 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Navbar/Directory/Moderating.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Text } from "@chakra-ui/react"; 3 | import { FaReddit } from "react-icons/fa"; 4 | import { CommunitySnippet } from "../../../atoms/communitiesAtom"; 5 | import MenuListItem from "./MenuListItem"; 6 | 7 | type ModeratingProps = { 8 | snippets: CommunitySnippet[]; 9 | }; 10 | 11 | const Moderating: React.FC = ({ snippets }) => { 12 | return ( 13 | 14 | 15 | MODERATING 16 | 17 | {snippets.map((snippet) => ( 18 | 25 | ))} 26 | 27 | ); 28 | }; 29 | export default Moderating; 30 | -------------------------------------------------------------------------------- /src/components/Layout/InputField.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, Input } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | type InputFieldProps = { 5 | name: string; 6 | placeholder: string; 7 | type: string; 8 | isRequired?: boolean; 9 | mb?: number; 10 | }; 11 | 12 | const InputField: React.FC = ({ 13 | name, 14 | placeholder, 15 | type, 16 | isRequired, // not sure if will need this 17 | mb, 18 | }) => { 19 | return ( 20 | <> 21 | 41 | 42 | ); 43 | }; 44 | export default InputField; 45 | -------------------------------------------------------------------------------- /src/components/Layout/PageContent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Flex } from "@chakra-ui/react"; 3 | 4 | interface PageContentLayoutProps { 5 | maxWidth?: string; 6 | } 7 | 8 | // Assumes array of two children are passed 9 | const PageContentLayout: React.FC = ({ 10 | children, 11 | maxWidth, 12 | }) => { 13 | return ( 14 | 15 | 16 | 21 | {children && children[0 as keyof typeof children]} 22 | 23 | {/* Right Content */} 24 | 29 | {children && children[1 as keyof typeof children]} 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default PageContentLayout; 37 | -------------------------------------------------------------------------------- /src/components/Modal/Auth/OAuthButtons.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Flex, Image, Text } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { useAuthState, useSignInWithGoogle } from "react-firebase-hooks/auth"; 4 | import { auth } from "../../../firebase/clientApp"; 5 | 6 | type OAuthButtonsProps = {}; 7 | 8 | const OAuthButtons: React.FC = () => { 9 | const [signInWithGoogle, _, loading, error] = useSignInWithGoogle(auth); 10 | 11 | return ( 12 | 13 | 22 | 23 | {error && ( 24 | 25 | {error} 26 | 27 | )} 28 | 29 | ); 30 | }; 31 | export default OAuthButtons; 32 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /functions/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as functions from "firebase-functions"; 2 | import * as admin from "firebase-admin"; 3 | 4 | admin.initializeApp(); 5 | const db = admin.firestore(); 6 | 7 | export const createUserDocument = functions.auth 8 | .user() 9 | .onCreate(async (user) => { 10 | db.collection("users") 11 | .doc(user.uid) 12 | .set(JSON.parse(JSON.stringify(user))); 13 | }); 14 | 15 | export const deletePostComments = functions.firestore 16 | .document(`posts/{postId}`) 17 | .onDelete(async (snap) => { 18 | const postId = snap.id; 19 | console.log("HERE IS POST ID", postId); 20 | 21 | admin 22 | .firestore() 23 | .collection("comments") 24 | .get() 25 | .then((snapshot) => { 26 | snapshot.forEach((doc) => { 27 | if (doc.data().postId === postId) { 28 | console.log("DELETING COMMENT: ", doc.id, doc.data().text); 29 | doc.ref.delete(); 30 | } 31 | }); 32 | }) 33 | .catch((error) => { 34 | console.log("Error deleting post comments"); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reddit-clone-yt", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/icons": "^1.1.7", 13 | "@chakra-ui/react": "^1.8.6", 14 | "@emotion/react": "^11.8.1", 15 | "@emotion/styled": "^11.8.1", 16 | "firebase": "^9.6.8", 17 | "firebase-admin": "^10.0.2", 18 | "firebase-functions": "^3.18.1", 19 | "framer-motion": "^6.2.8", 20 | "moment": "^2.29.1", 21 | "next": "12.1.0", 22 | "nookies": "^2.5.2", 23 | "react": "17.0.2", 24 | "react-dom": "17.0.2", 25 | "react-firebase-hooks": "^5.0.3", 26 | "react-icons": "^4.3.1", 27 | "recoil": "^0.6.1", 28 | "safe-json-stringify": "^1.2.0" 29 | }, 30 | "devDependencies": { 31 | "@types/node": "17.0.21", 32 | "@types/react": "17.0.39", 33 | "@types/safe-json-stringify": "^1.1.2", 34 | "eslint": "8.10.0", 35 | "eslint-config-next": "12.1.0", 36 | "typescript": "4.6.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Post/PostForm/TabItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, Icon, Text } from "@chakra-ui/react"; 3 | import { TabItem } from "./NewPostForm"; 4 | 5 | type TabItemProps = { 6 | item: TabItem; 7 | selected: boolean; 8 | setSelectedTab: (value: string) => void; 9 | }; 10 | 11 | const TabItem: React.FC = ({ 12 | item, 13 | selected, 14 | setSelectedTab, 15 | }) => { 16 | return ( 17 | setSelectedTab(item.title)} 30 | > 31 | 32 | 33 | 34 | {item.title} 35 | 36 | ); 37 | }; 38 | export default TabItem; 39 | -------------------------------------------------------------------------------- /src/components/Navbar/RightContent/AuthButtons.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@chakra-ui/react"; 2 | import React, { useState } from "react"; 3 | import { useSetRecoilState } from "recoil"; 4 | import { authModalState } from "../../../atoms/authModalAtom"; 5 | import AuthModal from "../../Modal/Auth"; 6 | 7 | type AuthButtonsProps = {}; 8 | 9 | const AuthButtons: React.FC = () => { 10 | const setAuthModalState = useSetRecoilState(authModalState); 11 | 12 | return ( 13 | <> 14 | 24 | 34 | 35 | ); 36 | }; 37 | export default AuthButtons; 38 | -------------------------------------------------------------------------------- /src/components/Navbar/Directory/MenuListItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, Icon, MenuItem, Image } from "@chakra-ui/react"; 3 | import { IconType } from "react-icons"; 4 | import useDirectory from "../../../hooks/useDirectory"; 5 | 6 | type DirectoryItemProps = { 7 | displayText: string; 8 | link: string; 9 | icon: IconType; 10 | iconColor: string; 11 | imageURL?: string; 12 | }; 13 | 14 | const MenuListItem: React.FC = ({ 15 | displayText, 16 | link, 17 | icon, 18 | iconColor, 19 | imageURL, 20 | }) => { 21 | const { onSelectMenuItem } = useDirectory(); 22 | return ( 23 | 28 | onSelectMenuItem({ displayText, link, icon, iconColor, imageURL }) 29 | } 30 | > 31 | 32 | {imageURL ? ( 33 | 34 | ) : ( 35 | 36 | )} 37 | {displayText} 38 | 39 | 40 | ); 41 | }; 42 | export default MenuListItem; 43 | -------------------------------------------------------------------------------- /src/atoms/postsAtom.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | import { Timestamp } from "firebase/firestore"; 3 | 4 | export type Post = { 5 | id: string; 6 | communityId: string; 7 | communityImageURL?: string; 8 | userDisplayText: string; // change to authorDisplayText 9 | creatorId: string; 10 | title: string; 11 | body: string; 12 | numberOfComments: number; 13 | voteStatus: number; 14 | currentUserVoteStatus?: { 15 | id: string; 16 | voteValue: number; 17 | }; 18 | imageURL?: string; 19 | postIdx?: number; 20 | createdAt?: Timestamp; 21 | editedAt?: Timestamp; 22 | }; 23 | 24 | export type PostVote = { 25 | id?: string; 26 | postId: string; 27 | communityId: string; 28 | voteValue: number; 29 | }; 30 | 31 | interface PostState { 32 | selectedPost: Post | null; 33 | posts: Post[]; 34 | postVotes: PostVote[]; 35 | postsCache: { 36 | [key: string]: Post[]; 37 | }; 38 | postUpdateRequired: boolean; 39 | } 40 | 41 | export const defaultPostState: PostState = { 42 | selectedPost: null, 43 | posts: [], 44 | postVotes: [], 45 | postsCache: {}, 46 | postUpdateRequired: true, 47 | }; 48 | 49 | export const postState = atom({ 50 | key: "postState", 51 | default: defaultPostState, 52 | }); 53 | -------------------------------------------------------------------------------- /src/components/Layout/InputItem.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormErrorMessage, Input } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | type InputItemProps = { 5 | name: string; 6 | value?: string; 7 | placeholder?: string; 8 | type: string; 9 | onChange?: (event: React.ChangeEvent) => void; 10 | mb?: number; 11 | bg?: string; 12 | size?: string; 13 | }; 14 | 15 | const InputItem: React.FC = ({ 16 | name, 17 | placeholder, 18 | value, 19 | type, 20 | onChange, 21 | mb, 22 | bg, 23 | size, 24 | }) => { 25 | return ( 26 | 51 | ); 52 | }; 53 | export default InputItem; 54 | -------------------------------------------------------------------------------- /src/atoms/communitiesAtom.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | import { FieldValue, Timestamp } from "firebase/firestore"; 3 | 4 | export interface Community { 5 | id: string; 6 | creatorId: string; 7 | numberOfMembers: number; 8 | privacyType: "public" | "restrictied" | "private"; 9 | createdAt?: Timestamp; 10 | imageURL?: string; 11 | } 12 | 13 | export interface CommunitySnippet { 14 | communityId: string; 15 | isModerator?: boolean; 16 | imageURL?: string; 17 | } 18 | 19 | interface CommunityState { 20 | [key: string]: 21 | | CommunitySnippet[] 22 | | { [key: string]: Community } 23 | | Community 24 | | boolean 25 | | undefined; 26 | mySnippets: CommunitySnippet[]; 27 | initSnippetsFetched: boolean; 28 | visitedCommunities: { 29 | [key: string]: Community; 30 | }; 31 | currentCommunity: Community; 32 | } 33 | 34 | export const defaultCommunity: Community = { 35 | id: "", 36 | creatorId: "", 37 | numberOfMembers: 0, 38 | privacyType: "public", 39 | }; 40 | 41 | export const defaultCommunityState: CommunityState = { 42 | mySnippets: [], 43 | initSnippetsFetched: false, 44 | visitedCommunities: {}, 45 | currentCommunity: defaultCommunity, 46 | }; 47 | 48 | export const communityState = atom({ 49 | key: "communitiesState", 50 | default: defaultCommunityState, 51 | }); 52 | -------------------------------------------------------------------------------- /src/components/Navbar/Directory/MyCommunities.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MenuItem, Flex, Icon, Text, Box } from "@chakra-ui/react"; 3 | import { FaReddit } from "react-icons/fa"; 4 | import { GrAdd } from "react-icons/gr"; 5 | import MenuListItem from "./MenuListItem"; 6 | import { CommunitySnippet } from "../../../atoms/communitiesAtom"; 7 | 8 | type MyCommunitiesProps = { 9 | snippets: CommunitySnippet[]; 10 | setOpen: (value: boolean) => void; 11 | }; 12 | 13 | const MyCommunities: React.FC = ({ snippets, setOpen }) => { 14 | return ( 15 | 16 | 17 | MY COMMUNITIES 18 | 19 | setOpen(true)} 24 | > 25 | 26 | 27 | Create Community 28 | 29 | 30 | {snippets.map((snippet) => ( 31 | 38 | ))} 39 | 40 | ); 41 | }; 42 | export default MyCommunities; 43 | -------------------------------------------------------------------------------- /src/components/Community/PersonalHome.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button, Flex, Icon, Stack, Text } from "@chakra-ui/react"; 3 | import { FaReddit } from "react-icons/fa"; 4 | 5 | const PersonalHome: React.FC = () => { 6 | return ( 7 | 16 | 27 | 28 | 29 | 30 | Home 31 | 32 | 33 | 34 | Your personal Reddit frontpage, built for you. 35 | 36 | 37 | 40 | 41 | 42 | 43 | ); 44 | }; 45 | export default PersonalHome; 46 | -------------------------------------------------------------------------------- /src/components/Navbar/SearchInput.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, InputGroup, InputLeftElement, Input } from "@chakra-ui/react"; 3 | import { SearchIcon } from "@chakra-ui/icons"; 4 | import { auth } from "firebase-admin"; 5 | import { user } from "firebase-functions/v1/auth"; 6 | import { User } from "firebase/auth"; 7 | 8 | type SearchInputProps = { 9 | user: User; 10 | }; 11 | 12 | const SearchInput: React.FC = ({ user }) => { 13 | return ( 14 | 20 | 21 | } 25 | > 26 | 27 | 28 | 45 | 46 | 47 | ); 48 | }; 49 | export default SearchInput; 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reddit Clone App on Kubernetes with Ingress 2 | This project demonstrates how to deploy a Reddit clone app on Kubernetes with Ingress and expose it to the world using Minikube as the cluster. 3 | 4 | ## Prerequisites 5 | Before you begin, you should have the following tools installed on your local machine: 6 | 7 | - Docker 8 | - Kubeatm master and worker node 9 | - kubectl 10 | - Git 11 | 12 | You can install Prerequisites by doing this steps. [click here & complete all steps one by one](). 13 | 14 | 15 | ## Installation 16 | Follow these steps to install and run the Reddit clone app on your local machine: 17 | 18 | 1) Clone this repository to your local machine: `git clone https://github.com/LondheShubham153/reddit-clone-k8s-ingress.git` 19 | 2) Navigate to the project directory: `cd reddit-clone-k8s-ingress` 20 | 3) Build the Docker image for the Reddit clone app: `docker build -t reddit-clone-app .` 21 | 4) Deploy the app to Kubernetes: `kubectl apply -f deployment.yaml` 22 | 1) Deploy the Service for deployment to Kubernetes: `kubectl apply -f service.yaml` 23 | 5) Enable Ingress by using Command: `minikube addons enable ingress` 24 | 6) Expose the app as a Kubernetes service: `kubectl expose deployment reddit-deployment --type=NodePort --port=5000` 25 | 7) Create an Ingress resource: `kubectl apply -f ingress.yaml` 26 | 27 | 28 | ## Test Ingress DNS for the app: 29 | 30 | 31 | ## Contributing 32 | If you'd like to contribute to this project, please open an issue or submit a pull request. 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/Navbar/RightContent/ProfileMenu/UserList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Flex, Icon, MenuDivider, MenuItem } from "@chakra-ui/react"; 3 | import { signOut } from "firebase/auth"; 4 | import { CgProfile } from "react-icons/cg"; 5 | import { MdOutlineLogin } from "react-icons/md"; 6 | import { useResetRecoilState } from "recoil"; 7 | import { communityState } from "../../../../atoms/communitiesAtom"; 8 | import { auth } from "../../../../firebase/clientApp"; 9 | 10 | type UserListProps = {}; 11 | 12 | const UserList: React.FC = () => { 13 | const resetCommunityState = useResetRecoilState(communityState); 14 | 15 | const logout = async () => { 16 | await signOut(auth); 17 | resetCommunityState(); 18 | }; 19 | 20 | return ( 21 | <> 22 | 27 | 28 | 29 | Profile 30 | 31 | 32 | 33 | 39 | 40 | 41 | Log Out 42 | 43 | 44 | 45 | ); 46 | }; 47 | export default UserList; 48 | -------------------------------------------------------------------------------- /src/hooks/useAuth.ts: -------------------------------------------------------------------------------- 1 | import { doc, onSnapshot } from "firebase/firestore"; 2 | import { useEffect } from "react"; 3 | import { useAuthState } from "react-firebase-hooks/auth"; 4 | import { useRecoilState } from "recoil"; 5 | import { userState } from "../atoms/userAtom"; 6 | import { auth, firestore } from "../firebase/clientApp"; 7 | import nookies from "nookies"; 8 | import { User } from "firebase/auth"; 9 | 10 | const useAuth = () => { 11 | const [user] = useAuthState(auth); 12 | // const [currentUser, setCurrentUser] = useRecoilState(userState); maybe later 13 | 14 | useEffect(() => { 15 | console.log("HERE IS USER", user); 16 | 17 | user ? setUserCookie(user) : nookies.set(undefined, "token", ""); 18 | }, [user]); 19 | 20 | const setUserCookie = async (user: User) => { 21 | const token = await user.getIdToken(); 22 | console.log("HERE IS TOKEN", token); 23 | nookies.set(undefined, "token", token); 24 | }; 25 | 26 | // useEffect(() => { 27 | // // User has logged out; firebase auth state has been cleared 28 | // if (!user?.uid && userState) { 29 | // return setCurrentUser(null); 30 | // } 31 | 32 | // const userDoc = doc(firestore, "users", user?.uid as string); 33 | // const unsubscribe = onSnapshot(userDoc, (doc) => { 34 | // console.log("CURRENT DATA", doc.data()); 35 | // if (!doc.data()) return; 36 | // if (currentUser) return; 37 | // setCurrentUser(doc.data() as any); 38 | // }); 39 | 40 | // if (currentUser) { 41 | // unsubscribe(); 42 | // } 43 | 44 | // return () => unsubscribe(); 45 | // }, [user, currentUser]); 46 | }; 47 | export default useAuth; 48 | -------------------------------------------------------------------------------- /src/components/Navbar/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Flex, Image } from "@chakra-ui/react"; 3 | import { User } from "firebase/auth"; 4 | import { useAuthState } from "react-firebase-hooks/auth"; 5 | import { useSetRecoilState } from "recoil"; 6 | import { 7 | defaultMenuItem, 8 | directoryMenuState, 9 | } from "../../atoms/directoryMenuAtom"; 10 | import { auth } from "../../firebase/clientApp"; 11 | import Directory from "./Directory"; 12 | import RightContent from "./RightContent"; 13 | import SearchInput from "./SearchInput"; 14 | import router from "next/router"; 15 | import useDirectory from "../../hooks/useDirectory"; 16 | 17 | const Navbar: React.FC = () => { 18 | const [user] = useAuthState(auth); 19 | 20 | // Use for initial build; implement directory logic near end 21 | const { onSelectMenuItem } = useDirectory(); 22 | 23 | return ( 24 | 30 | onSelectMenuItem(defaultMenuItem)} 36 | > 37 | 38 | 43 | 44 | {user && } 45 | 46 | 47 | 48 | ); 49 | }; 50 | export default Navbar; 51 | -------------------------------------------------------------------------------- /src/components/Post/PostForm/TextInputs.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Stack, Input, Textarea, Flex, Button } from "@chakra-ui/react"; 3 | 4 | type TextInputsProps = { 5 | textInputs: { 6 | title: string; 7 | body: string; 8 | }; 9 | onChange: ( 10 | event: React.ChangeEvent 11 | ) => void; 12 | handleCreatePost: () => void; 13 | loading: boolean; 14 | }; 15 | 16 | const TextInputs: React.FC = ({ 17 | textInputs, 18 | onChange, 19 | handleCreatePost, 20 | loading, 21 | }) => { 22 | return ( 23 | 24 | 39 |