├── src ├── react-app-env.d.ts ├── assets │ ├── adam.png │ ├── bob.png │ ├── callum.png │ ├── daniel.png │ ├── valley.jpeg │ ├── VisbyRoundCF-Heavy.woff │ ├── VisbyRoundCF-DemiBold.woff │ └── VisbyRoundCF-Regular.woff ├── functions │ ├── dates.tsx │ ├── constants.tsx │ ├── getOtherUser.tsx │ ├── isMobile.tsx │ └── context.tsx ├── index.tsx ├── App.tsx ├── AuthPage │ ├── components │ │ ├── Link.tsx │ │ ├── Button.tsx │ │ ├── TextInput.tsx │ │ └── PhotoInput.tsx │ ├── index.tsx │ ├── LogInForm.tsx │ └── SignUpForm.tsx ├── ChatsPage │ ├── Sidebar.tsx │ ├── MessageForm.tsx │ ├── ChatCard.tsx │ ├── UserSearch.tsx │ ├── index.tsx │ └── ChatHeader.tsx ├── app.css └── theme.css ├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/assets/adam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/adam.png -------------------------------------------------------------------------------- /src/assets/bob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/bob.png -------------------------------------------------------------------------------- /src/assets/callum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/callum.png -------------------------------------------------------------------------------- /src/assets/daniel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/daniel.png -------------------------------------------------------------------------------- /src/assets/valley.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/valley.jpeg -------------------------------------------------------------------------------- /src/assets/VisbyRoundCF-Heavy.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/VisbyRoundCF-Heavy.woff -------------------------------------------------------------------------------- /src/assets/VisbyRoundCF-DemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/VisbyRoundCF-DemiBold.woff -------------------------------------------------------------------------------- /src/assets/VisbyRoundCF-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/pretty-chat-react-typescript/HEAD/src/assets/VisbyRoundCF-Regular.woff -------------------------------------------------------------------------------- /src/functions/dates.tsx: -------------------------------------------------------------------------------- 1 | export const nowTimeStamp = () => { 2 | return new Date() 3 | .toISOString() 4 | .replace("T", " ") 5 | .replace("Z", `${Math.floor(Math.random() * 1000)}+00:00`); 6 | }; 7 | -------------------------------------------------------------------------------- /src/functions/constants.tsx: -------------------------------------------------------------------------------- 1 | export const projectId = process.env.REACT_APP_PROJECT_ID 2 | ? process.env.REACT_APP_PROJECT_ID 3 | : ""; 4 | 5 | export const privateKey: string = process.env.REACT_APP_PROJECT_KEY 6 | ? process.env.REACT_APP_PROJECT_KEY 7 | : ""; 8 | -------------------------------------------------------------------------------- /src/functions/getOtherUser.tsx: -------------------------------------------------------------------------------- 1 | import { ChatObject, PersonObject } from "react-chat-engine-advanced"; 2 | 3 | export const getOtherUser = ( 4 | chat: ChatObject, 5 | username: string 6 | ): PersonObject | undefined => { 7 | const otherMember = chat.people.find( 8 | (member) => member.person.username !== username 9 | ); 10 | return otherMember?.person; 11 | }; 12 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom/client"; 2 | import App from "./App"; 3 | 4 | import "./assets/VisbyRoundCF-Regular.woff"; 5 | 6 | import { ContextProvider } from "./functions/context"; 7 | 8 | const root = ReactDOM.createRoot( 9 | document.getElementById("root") as HTMLElement 10 | ); 11 | root.render( 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import { Context } from "./functions/context"; 4 | 5 | import AuthPage from "./AuthPage"; 6 | import ChatsPage from "./ChatsPage"; 7 | 8 | import "./app.css"; 9 | 10 | function App() { 11 | const { user } = useContext(Context); 12 | 13 | if (user) { 14 | return ; 15 | } else { 16 | return ; 17 | } 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/functions/isMobile.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useState } from "react"; 2 | 3 | // TODO: Copy paste this: https://www.codegrepper.com/code-examples/javascript/react+get+window+width+on+resize 4 | export const useIsMobile = () => { 5 | const [size, setSize] = useState([0, 0]); 6 | useLayoutEffect(() => { 7 | function updateSize() { 8 | setSize([window.innerWidth, window.innerHeight]); 9 | } 10 | window.addEventListener("resize", updateSize); 11 | updateSize(); 12 | return () => window.removeEventListener("resize", updateSize); 13 | }, []); 14 | return size[0] < 820; 15 | }; 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/functions/context.tsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext, ReactNode } from "react"; 2 | import { PersonObject } from "react-chat-engine-advanced"; 3 | 4 | export interface ContextInterface { 5 | user: PersonObject | undefined; 6 | setUser: (u: PersonObject | undefined) => void; 7 | } 8 | 9 | interface ProviderInterface { 10 | children: ReactNode; 11 | } 12 | 13 | export const Context = createContext({ 14 | user: undefined, 15 | setUser: () => {}, 16 | }); 17 | 18 | export const ContextProvider = (props: ProviderInterface) => { 19 | const [user, setUser] = useState(undefined); 20 | const value = { user, setUser }; 21 | 22 | return {props.children}; 23 | }; 24 | -------------------------------------------------------------------------------- /src/AuthPage/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, ReactNode, useState } from "react"; 2 | 3 | interface LinkProps { 4 | children?: ReactNode; 5 | style?: CSSProperties; 6 | type?: string; 7 | onClick?: () => void; 8 | } 9 | 10 | const Link = (props: LinkProps) => { 11 | const [hovered, setHovered] = useState(false); 12 | 13 | return ( 14 | setHovered(true)} 17 | onMouseLeave={() => setHovered(false)} 18 | style={{ 19 | ...styles.style, 20 | ...(hovered && styles.hoverStyle), 21 | ...props.style, 22 | }} 23 | > 24 | {props.children} 25 | 26 | ); 27 | }; 28 | 29 | const styles = { 30 | style: { 31 | color: "#fa541c", 32 | cursor: "pointer", 33 | transition: "all .44s ease", 34 | WebkitTransition: "all .44s ease", 35 | MozTransition: "all .44s ease", 36 | } as CSSProperties, 37 | hoverStyle: { 38 | filter: "brightness(145%)", 39 | textDecoration: "underline", 40 | } as CSSProperties, 41 | }; 42 | 43 | export default Link; 44 | -------------------------------------------------------------------------------- /src/ChatsPage/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import { 4 | LogoutOutlined, 5 | HomeFilled, 6 | MessageFilled, 7 | SettingFilled, 8 | } from "@ant-design/icons"; 9 | 10 | import { Avatar } from "react-chat-engine-advanced"; 11 | 12 | import { Context } from "../functions/context"; 13 | 14 | const Sidebar = () => { 15 | const { user, setUser } = useContext(Context); 16 | 17 | return ( 18 |
19 |
20 | 21 | 22 | 23 |
24 | 25 | 31 | 32 | setUser(undefined)} 34 | className="signout-icon" 35 | /> 36 |
37 | ); 38 | }; 39 | 40 | export default Sidebar; 41 | -------------------------------------------------------------------------------- /src/AuthPage/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, ReactNode, useState } from "react"; 2 | 3 | interface ButtonProps { 4 | children?: ReactNode; 5 | style?: CSSProperties; 6 | type?: string; 7 | } 8 | 9 | const Button = (props: ButtonProps) => { 10 | const [hovered, setHovered] = useState(false); 11 | 12 | return ( 13 | 24 | ); 25 | }; 26 | 27 | const styles = { 28 | style: { 29 | width: "100%", 30 | height: "53px", 31 | color: "white", 32 | backgroundColor: "#fa541c", 33 | border: "none", 34 | outline: "none", 35 | borderRadius: "8px", 36 | fontFamily: "VisbyRoundCF-DemiBold", 37 | cursor: "pointer", 38 | transition: "all .44s ease", 39 | WebkitTransition: "all .44s ease", 40 | MozTransition: "all .44s ease", 41 | } as CSSProperties, 42 | hoverStyle: { filter: "brightness(145%)" } as CSSProperties, 43 | }; 44 | 45 | export default Button; 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pretty-chat-react-typescript", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^13.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "@types/jest": "^27.0.1", 10 | "@types/node": "^16.7.13", 11 | "@types/react": "^18.0.0", 12 | "@types/react-dom": "^18.0.0", 13 | "antd": "4.21.5", 14 | "axios": "^0.27.2", 15 | "react": "^18.2.0", 16 | "react-chat-engine-advanced": "0.1.21", 17 | "react-dom": "^18.2.0", 18 | "react-scripts": "5.0.1", 19 | "typescript": "^4.4.2", 20 | "web-vitals": "^2.1.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/AuthPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, useState } from "react"; 2 | 3 | import valley from "../assets/valley.jpeg"; 4 | import SignUpForm from "./SignUpForm"; 5 | import LogInForm from "./LogInForm"; 6 | 7 | const AuthPage = () => { 8 | const [hasAccount, setHasAccount] = useState(false); 9 | 10 | const backgroundImage = { 11 | backgroundImage: `url(${valley})`, // Here due to variable 12 | } as CSSProperties; 13 | 14 | return ( 15 |
16 |
17 |
18 |
Pretty
19 | 20 | {hasAccount ? ( 21 | setHasAccount(false)} /> 22 | ) : ( 23 | setHasAccount(true)} /> 24 | )} 25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | const styles = { 32 | formContainerStyle: { 33 | width: "100%", 34 | maxWidth: "650px", 35 | padding: "36px 72px", 36 | } as CSSProperties, 37 | titleStyle: { 38 | fontSize: "24px", 39 | fontFamily: "VisbyRoundCF-Heavy", 40 | letterSpacing: "0.5px", 41 | color: "white", 42 | paddingBottom: "11vw", 43 | } as CSSProperties, 44 | }; 45 | 46 | export default AuthPage; 47 | -------------------------------------------------------------------------------- /src/ChatsPage/MessageForm.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useState } from "react"; 2 | 3 | import { CaretUpFilled } from "@ant-design/icons"; 4 | 5 | import { MessageObject, MessageFormProps } from "react-chat-engine-advanced"; 6 | 7 | import { nowTimeStamp } from "../functions/dates"; 8 | import { Context } from "../functions/context"; 9 | 10 | const MessageForm = (props: MessageFormProps) => { 11 | const [text, setText] = useState(""); 12 | const { user } = useContext(Context); 13 | 14 | const onSubmit = (event: React.FormEvent) => { 15 | event.preventDefault(); 16 | 17 | if (text.trim().length === 0) { 18 | return; 19 | } 20 | if (!user || user.email === null) { 21 | return; 22 | } 23 | 24 | setText(""); 25 | 26 | const message: MessageObject = { 27 | text: text, 28 | sender_username: user.email, 29 | created: nowTimeStamp(), 30 | custom_json: {}, 31 | attachments: [], 32 | }; 33 | 34 | props.onSubmit && props.onSubmit(message); 35 | }; 36 | 37 | return ( 38 |
39 | setText(e.target.value)} 41 | value={text} 42 | placeholder="Type something..." 43 | className="ce-custom-message-input" 44 | /> 45 | 46 | 49 |
50 | ); 51 | }; 52 | 53 | export default MessageForm; 54 | -------------------------------------------------------------------------------- /src/AuthPage/components/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, ChangeEventHandler } from "react"; 2 | 3 | interface TextInputProps { 4 | label: string; 5 | name: string; 6 | type?: string; 7 | placeholder?: string; 8 | style?: CSSProperties; 9 | onChange: ChangeEventHandler; 10 | } 11 | 12 | const TextInput = (props: TextInputProps) => { 13 | return ( 14 |
20 |
{props.label}
21 | 22 | 29 |
30 | ); 31 | }; 32 | 33 | const styles = { 34 | style: { 35 | position: "relative", 36 | display: "inline-block", 37 | width: "100%", 38 | paddingBottom: "12px", 39 | } as CSSProperties, 40 | labelStyle: { 41 | position: "absolute", 42 | top: "8px", 43 | left: "18px", 44 | fontSize: "11px", 45 | color: "rgb(175, 175, 175)", 46 | fontFamily: "VisbyRoundCF-DemiBold", 47 | width: "100px", 48 | } as CSSProperties, 49 | inputStyle: { 50 | backgroundColor: "#3e404b", 51 | color: "white", 52 | fontFamily: "VisbyRoundCF-DemiBold", 53 | outline: "none", 54 | border: "none", 55 | borderRadius: "8px", 56 | padding: "24px 18px 12px 18px", 57 | width: "100%", // For the padding 18px + 18px 58 | } as CSSProperties, 59 | }; 60 | 61 | export default TextInput; 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pretty Chat React/Typescript 2 | 3 | This project is a pretty full-stack chat app built with React and Typesctipt. 4 | 5 | To learn how this project works, watch the following YouTube tutorial. 6 | 7 | ## Setup 8 | 9 | Go to [chatengine.io](https://chatengine.io) and create your own project. There you will get a Project ID and Private Key which are needed for user signup and authentication. 10 | 11 | ### `.env.local` 12 | 13 | Create a `.env.local` file at the top-level of your project, and replace the UUIDs with your own Project ID and Private Key from [chatengine.io](https://chatengine.io). 14 | 15 | ``` 16 | REACT_APP_PROJECT_ID=12341234-1234-1234-1234-123412341234 17 | REACT_APP_PROJECT_KEY=abcdabcd-abcd-abcd-abcd-abcdabcdabcd 18 | ``` 19 | 20 | This will link your new React App to the right Chat Engine project. 21 | 22 | ### `npm install` 23 | 24 | Build out your node modules by running `npm install`. Then you cn start the app. 25 | 26 | ### `npm run start` 27 | 28 | This will start the app. By default it will run on [localhost:3000](http://localhost:3000/) 29 | 30 | ### `npm run build` 31 | 32 | Builds the app for production to the `build` folder.\ 33 | It correctly bundles React in production mode and optimizes the build for the best performance. 34 | 35 | The build is minified and the filenames include the hashes.\ 36 | Your app is ready to be deployed! 37 | 38 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | -------------------------------------------------------------------------------- /src/ChatsPage/ChatCard.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChatCard, 3 | ChatCardProps, 4 | ChatObject, 5 | } from "react-chat-engine-advanced"; 6 | 7 | import { getOtherUser } from "../functions/getOtherUser"; 8 | 9 | interface CustomChatCardProps extends ChatCardProps { 10 | username: string; 11 | isActive: boolean; 12 | onChatCardClick: (chatId: number) => void; 13 | chat?: ChatObject; 14 | } 15 | 16 | const CustomChatCard = (props: CustomChatCardProps) => { 17 | if (!props.chat) return
; 18 | 19 | const otherMember = getOtherUser(props.chat, props.username); 20 | const firstName = otherMember ? otherMember.first_name : ""; 21 | const lastName = otherMember ? otherMember.last_name : ""; 22 | const username = otherMember ? otherMember.username : ""; 23 | const messageText = props.chat.last_message.text; 24 | const hasNotification = 25 | props.chat.last_message.sender_username !== props.username; 26 | 27 | return ( 28 | props.chat && props.onChatCardClick(props.chat.id)} 48 | /> 49 | ); 50 | }; 51 | 52 | export default CustomChatCard; 53 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import url("https://cdnjs.cloudflare.com/ajax/libs/antd/4.21.5/antd.min.css"); 2 | 3 | @font-face { 4 | /* TODO: Font Family https://www.dfonts.org/assets/visby-round-font-family/ */ 5 | font-family: 'VisbyRoundCF-Regular'; 6 | src: local('VisbyRoundCF-Regular'), url(./assets/VisbyRoundCF-Regular.woff) format('woff'); 7 | font-weight: normal; 8 | } 9 | 10 | @font-face { 11 | /* TODO: Font Family https://www.dfonts.org/assets/visby-round-font-family/ */ 12 | font-family: 'VisbyRoundCF-DemiBold'; 13 | src: local('VisbyRoundCF-DemiBold'), url(./assets/VisbyRoundCF-DemiBold.woff) format('woff'); 14 | font-weight: normal; 15 | } 16 | 17 | @font-face { 18 | /* TODO: Font Family https://www.dfonts.org/assets/visby-round-font-family/ */ 19 | font-family: 'VisbyRoundCF-Heavy'; 20 | src: local('VisbyRoundCF-Heavy'), url(./assets/VisbyRoundCF-Heavy.woff) format('woff'); 21 | font-weight: normal; 22 | } 23 | 24 | body { 25 | font-family: 'VisbyRoundCF-Regular'; 26 | margin: 0px; 27 | } 28 | 29 | .form-title { 30 | font-size: 42px; 31 | font-family: 'VisbyRoundCF-Heavy'; 32 | letter-spacing: 0.5px; 33 | color: #e8e8e8; 34 | padding-bottom: 12px; 35 | } 36 | 37 | .form-subtitle { 38 | font-size: 18px; 39 | font-family: 'VisbyRoundCF-Regular'; 40 | letter-spacing: 0.5px; 41 | color: #afafaf; 42 | padding-bottom: 24px; 43 | } 44 | 45 | .background-image { 46 | width: 100vw; 47 | height: 100vh; 48 | background-repeat: no-repeat; 49 | background-size: cover; 50 | } 51 | 52 | .background-gradient-dark { 53 | width: 100vw; 54 | height: 100%; 55 | overflow-y: scroll; 56 | background: linear-gradient(66deg, rgb(40,43,54) 0%, rgb(40,43,54) 50%, rgba(40,43,54,0.8) 100%); 57 | /* ^ Built with https://cssgradient.io/ */ 58 | } 59 | 60 | .background-gradient-light { 61 | width: 100vw; 62 | height: 100%; 63 | overflow-y: scroll; 64 | background: linear-gradient(66deg, rgba(150, 157, 166, 0.9) 0%, rgba(150, 157, 166, 0.8) 50%, rgba(150,157,166,0.7) 100%); 65 | /* ^ Built with https://cssgradient.io/ */ 66 | } 67 | -------------------------------------------------------------------------------- /src/AuthPage/LogInForm.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext } from "react"; 2 | 3 | import axios from "axios"; 4 | 5 | import TextInput from "./components/TextInput"; 6 | import Button from "./components/Button"; 7 | import Link from "./components/Link"; 8 | 9 | import { Context } from "../functions/context"; 10 | import { projectId } from "../functions/constants"; 11 | import { PersonObject } from "react-chat-engine-advanced"; 12 | 13 | interface LogInFormProps { 14 | onHasNoAccount: () => void; 15 | } 16 | 17 | const LogInForm = (props: LogInFormProps) => { 18 | // State 19 | const [email, setEmail] = useState(""); 20 | const [password, setPassword] = useState(""); 21 | // Hooks 22 | const { setUser } = useContext(Context); 23 | 24 | const onSubmit = (event: React.FormEvent) => { 25 | event.preventDefault(); 26 | 27 | const headers = { 28 | "Project-ID": projectId, 29 | "User-Name": email, 30 | "User-Secret": password, 31 | }; 32 | 33 | axios 34 | .get("https://api.chatengine.io/users/me/", { 35 | headers, 36 | }) 37 | .then((r) => { 38 | if (r.status === 200) { 39 | const user: PersonObject = { 40 | first_name: r.data.first_name, 41 | last_name: r.data.last_name, 42 | email: email, 43 | username: email, 44 | secret: password, 45 | avatar: r.data.avatar, 46 | custom_json: {}, 47 | is_online: true, 48 | }; 49 | setUser(user); 50 | } 51 | }) 52 | .catch((e) => console.log("Error", e)); 53 | }; 54 | 55 | return ( 56 |
57 |
Welcome Back
58 | 59 |
60 | New here? props.onHasNoAccount()}>Sign Up 61 |
62 | 63 |
64 | setEmail(e.target.value)} 69 | /> 70 | 71 | setPassword(e.target.value)} 77 | /> 78 | 79 | 80 | 81 |
82 | ); 83 | }; 84 | 85 | export default LogInForm; 86 | -------------------------------------------------------------------------------- /src/AuthPage/components/PhotoInput.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, ChangeEventHandler, useState } from "react"; 2 | 3 | interface PhotoInputProps { 4 | label: string; 5 | id: string; 6 | name: string; 7 | placeholder?: string; 8 | style?: CSSProperties; 9 | onChange: ChangeEventHandler; 10 | } 11 | 12 | const PhotoInput = (props: PhotoInputProps) => { 13 | const [selectedImage, setSelectedImage] = useState( 14 | undefined 15 | ); 16 | 17 | const onChange: ChangeEventHandler = (event) => { 18 | if (event.target.files !== null) { 19 | const image = event.target.files[0]; 20 | setSelectedImage(image); 21 | props.onChange(event); 22 | } 23 | }; 24 | 25 | return ( 26 |
32 |