Full rewrite in react
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
78
README.md
|
@ -1,36 +1,70 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
# Getting Started with Create React App
|
||||
|
||||
## Getting Started
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
First, run the development server:
|
||||
## Available Scripts
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
In the project directory, you can run:
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
### `npm start`
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
### Code Splitting
|
||||
|
||||
## Deploy on Vercel
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
|
|
BIN
app/favicon.ico
Before Width: | Height: | Size: 25 KiB |
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -1,21 +0,0 @@
|
|||
import type { Metadata } from "next";
|
||||
import { inter } from "@/app/ui/fonts";
|
||||
//import './globals.css'
|
||||
import "@/app/ui/global.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Inconnect",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
import Link from "next/link";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Login() {
|
||||
async function login(formData: FormData) {
|
||||
"use server";
|
||||
const rawFormData = {
|
||||
email: formData.get("email"),
|
||||
pass: formData.get("pass"),
|
||||
};
|
||||
const cookiesList = cookies();
|
||||
if (cookiesList.has("usersdata")) {
|
||||
var usersdata = cookiesList.get("usersdata");
|
||||
console.log(usersdata);
|
||||
var users = JSON.parse(usersdata.value);
|
||||
if (rawFormData.email in users) {
|
||||
var user = users[rawFormData.email];
|
||||
if (rawFormData.pass === user.pass1) {
|
||||
cookies().set("userdata", JSON.stringify(user));
|
||||
redirect("/");
|
||||
} else {
|
||||
redirect("/login");
|
||||
}
|
||||
} else {
|
||||
redirect("/login");
|
||||
}
|
||||
} else {
|
||||
redirect("/login");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl px-4 py-16 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-lg text-center">
|
||||
<h1 className="text-2xl font-bold sm:text-3xl">{"Login"}</h1>
|
||||
|
||||
<p className="mt-4 text-gray-500">
|
||||
Inconnect needs to login to view your chats and translate to sign
|
||||
language{" "}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action={login} className="mx-auto mb-0 mt-8 max-w-md space-y-4">
|
||||
<div>
|
||||
<label htmlFor="email" className="sr-only">
|
||||
Email
|
||||
</label>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
type="email"
|
||||
className="w-full rounded-lg border-gray-200 p-4 pe-12 text-sm shadow-sm"
|
||||
placeholder="Enter email"
|
||||
name="email"
|
||||
/>
|
||||
|
||||
<span className="absolute inset-y-0 end-0 grid place-content-center px-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password" className="sr-only">
|
||||
Password
|
||||
</label>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
type="password"
|
||||
className="w-full rounded-lg border-gray-200 p-4 pe-12 text-sm shadow-sm"
|
||||
placeholder="Enter password"
|
||||
name="pass"
|
||||
/>
|
||||
|
||||
<span className="absolute inset-y-0 end-0 grid place-content-center px-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-gray-500">
|
||||
No account?{" "}
|
||||
<Link className="underline" href="/signup">
|
||||
Sign up
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-block rounded-lg bg-blue-500 px-5 py-3 text-sm font-medium text-white"
|
||||
>
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
100
app/page.tsx
|
@ -1,100 +0,0 @@
|
|||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
function LoginButton(loggedIn = false, logoutFunction) {
|
||||
console.log(loggedIn);
|
||||
if (loggedIn) {
|
||||
return (
|
||||
<form action={logoutFunction}>
|
||||
<button
|
||||
className="block rounded-lg bg-indigo-600 px-5 py-3 text-sm font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring"
|
||||
type="submit"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Link
|
||||
href={"/login"}
|
||||
className="block rounded-lg bg-indigo-600 px-5 py-3 text-sm font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring"
|
||||
type="button"
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
var loggedIn = false;
|
||||
async function logout() {
|
||||
"use server";
|
||||
cookies().delete("userdata");
|
||||
}
|
||||
|
||||
const cookiesList = cookies();
|
||||
if (cookiesList.has("userdata")) {
|
||||
var userdata = cookiesList.get("userdata");
|
||||
console.log("Login page");
|
||||
console.log(userdata);
|
||||
if (userdata.value) {
|
||||
var user = JSON.parse(userdata.value);
|
||||
var username = user.name;
|
||||
loggedIn = true;
|
||||
} else {
|
||||
username = "Guest";
|
||||
loggedIn = false;
|
||||
}
|
||||
} else {
|
||||
username = "Guest";
|
||||
loggedIn = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<header>
|
||||
<div className="mx-auto max-w-screen-xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8">
|
||||
<div className="sm:flex sm:items-center sm:justify-between">
|
||||
<div className="text-center sm:text-left">
|
||||
<h1 className="text-2xl font-bold text-gray-900 sm:text-3xl">
|
||||
Welcome, {username}!
|
||||
</h1>
|
||||
|
||||
<p className="mt-1.5 text-sm text-gray-500">
|
||||
{(loggedIn && "") || "Login to access the features"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-col gap-4 sm:mt-0 sm:flex-row sm:items-center">
|
||||
{LoginButton(loggedIn, logout)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div className="place-content-center self-center flex">
|
||||
<label className="form-control w-full max-w-xs">
|
||||
<div className="label">
|
||||
<span className="label-text">Pick a file</span>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
capture="user"
|
||||
accept="audio/*"
|
||||
className="file-input file-input-bordered w-full max-w-xs"
|
||||
/>
|
||||
<div className="label">
|
||||
<span className="label-text-alt">Audio files</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function SignUp() {
|
||||
async function createUser(formData: FormData) {
|
||||
"use server";
|
||||
const rawFormData = {
|
||||
name: formData.get("name"),
|
||||
email: formData.get("email"),
|
||||
pass1: formData.get("pass1"),
|
||||
pass2: formData.get("pass2"),
|
||||
};
|
||||
const cookiesList = cookies();
|
||||
if (cookiesList.has("usersdata")) {
|
||||
var usersdata = cookiesList.get("usersdata");
|
||||
var users = JSON.parse(usersdata.value);
|
||||
} else {
|
||||
var users = {
|
||||
"abc@example.org": {
|
||||
name: "ABC",
|
||||
email: "abc@example.org",
|
||||
pass1: "abc",
|
||||
},
|
||||
};
|
||||
}
|
||||
users[rawFormData.email] = rawFormData;
|
||||
cookies().set("usersdata", JSON.stringify(users));
|
||||
redirect("/login");
|
||||
}
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="lg:grid lg:min-h-screen lg:grid-cols-12">
|
||||
<aside className="relative block h-16 lg:order-last lg:col-span-5 lg:h-full xl:col-span-6">
|
||||
<div className="hidden">
|
||||
Photo by{" "}
|
||||
<Link href="https://unsplash.com/@patrickian4?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
|
||||
Patrick Fore
|
||||
</Link>{" "}
|
||||
on{" "}
|
||||
<Link href="https://unsplash.com/photos/peace-sign-hfv2GImBsfM?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
|
||||
Unsplash
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Image
|
||||
alt="Peace"
|
||||
src="/patrick-fore-hfv2GImBsfM-unsplash.jpg"
|
||||
width={4707}
|
||||
height={3138}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<main className="flex items-center justify-center px-8 py-8 sm:px-12 lg:col-span-7 lg:px-16 lg:py-12 xl:col-span-6">
|
||||
<div className="max-w-xl lg:max-w-3xl">
|
||||
<h1 className="mt-6 text-2xl font-bold text-gray-900 dark:text-white sm:text-3xl md:text-4xl">
|
||||
Sign Up
|
||||
</h1>
|
||||
|
||||
<p className="mt-4 leading-relaxed text-gray-500 dark:text-gray-400">
|
||||
Create an account to use Inconnect and sign away!
|
||||
</p>
|
||||
|
||||
<form action={createUser} className="mt-8 grid grid-cols-6 gap-6">
|
||||
<div className="col-span-6">
|
||||
<input
|
||||
type="text"
|
||||
id="Name"
|
||||
name="name"
|
||||
placeholder="Name"
|
||||
required
|
||||
autoComplete="off"
|
||||
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6">
|
||||
<input
|
||||
type="email"
|
||||
id="Email"
|
||||
name="email"
|
||||
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
|
||||
placeholder="Email"
|
||||
required
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<input
|
||||
type="password"
|
||||
id="Password"
|
||||
name="pass1"
|
||||
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
|
||||
placeholder="Password"
|
||||
required
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<input
|
||||
type="password"
|
||||
id="PasswordConfirmation"
|
||||
name="pass2"
|
||||
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
|
||||
placeholder="Confirm Password"
|
||||
required
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:flex sm:items-center sm:gap-4 mt-6">
|
||||
<button className="inline-block shrink-0 rounded-md border border-blue-600 bg-blue-600 px-12 py-3 text-sm font-medium text-white transition hover:bg-transparent hover:text-blue-600 focus:outline-none focus:ring active:text-blue-500 dark:hover:bg-blue-700 dark:hover:text-white">
|
||||
Create an account
|
||||
</button>
|
||||
|
||||
<p className="mt-5 text-sm text-gray-500 dark:text-gray-400 sm:mt-0">
|
||||
Already have an account?{" "}
|
||||
<Link
|
||||
href="/login"
|
||||
className="text-gray-700 underline dark:text-gray-200"
|
||||
>
|
||||
Log in
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import { Inter } from "next/font/google";
|
||||
|
||||
export const inter = Inter({ subsets: ["latin"] });
|
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -1,4 +0,0 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
module.exports = nextConfig
|
67
package.json
|
@ -1,30 +1,53 @@
|
|||
{
|
||||
"name": "inconnect",
|
||||
"name": "inconnect-ui",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^1.0.10",
|
||||
"@prisma/client": "^5.7.0",
|
||||
"next": "14.0.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"firebase": "^10.9.0",
|
||||
"framer-motion": "^11.0.8",
|
||||
"gsap": "^3.12.5",
|
||||
"howler": "^2.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-intersection-observer": "^9.8.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"splitting": "^1.0.6",
|
||||
"styled-components": "^6.1.8",
|
||||
"uuid": "^9.0.1",
|
||||
"wavesurfer-react": "^3.0.2",
|
||||
"wavesurfer.js": "^7.7.4",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10.0.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.0.3",
|
||||
"postcss": "^8",
|
||||
"prisma": "^5.7.0",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"typescript": "^5"
|
||||
"sass": "^1.71.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 9.4 KiB |
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,38 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||
import LandingPage from './Components/LandingPage/LandingPage';
|
||||
import Uploader from './Components/FileUpload/Uploader';
|
||||
import Footer from './Components/Footer/Footer';
|
||||
import Login from './Components/reg_and_log/Login';
|
||||
import Register from './Components/reg_and_log/Register';
|
||||
import Dashboard from './Components/Chat/Dashboard';
|
||||
import { useContext } from 'react';
|
||||
import { AuthContext } from './Components/context/AuthContext';
|
||||
function App() {
|
||||
|
||||
const { currentUser } = useContext(AuthContext)
|
||||
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
if (!currentUser) {
|
||||
return <Navigate to="/Login" />
|
||||
}
|
||||
return children
|
||||
};
|
||||
return (
|
||||
<div className="App">
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<>
|
||||
<LandingPage />
|
||||
<Uploader />
|
||||
<Footer />
|
||||
</>} />
|
||||
<Route path='/Login' element={<Login />} />
|
||||
<Route path='/Register' element={<Register />} />
|
||||
<Route path='/Dashboard' element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,8 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
export const footerTextAnimation = {
|
||||
hidden: { x: -200, opacity: 0 },
|
||||
show: { x: 1, opacity: 1 },
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
import React, { useEffect, useState, useContext } from 'react';
|
||||
import Msg from './messages';
|
||||
import Input from './input';
|
||||
import { ChatContext } from '../context/ChatContext';
|
||||
|
||||
const Chat = () => {
|
||||
const { data } = useContext(ChatContext);
|
||||
|
||||
return (
|
||||
<div className='chat'>
|
||||
<div className="chatInfo">
|
||||
<span>{data.user.displayName || "Welcome to inconnect"}</span>
|
||||
</div>
|
||||
<Msg />
|
||||
<Input />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Chat;
|
|
@ -0,0 +1,50 @@
|
|||
import { doc, onSnapshot } from "firebase/firestore";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { AuthContext } from "../context/AuthContext";
|
||||
import { ChatContext } from "../context/ChatContext";
|
||||
import { db } from "../firebase";
|
||||
|
||||
const Chats = () => {
|
||||
const [chats, setChats] = useState([]);
|
||||
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
const { dispatch } = useContext(ChatContext);
|
||||
|
||||
useEffect(() => {
|
||||
const getChats = () => {
|
||||
const unsub = onSnapshot(doc(db, "userChats", currentUser.uid), (doc) => {
|
||||
setChats(doc.data());
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsub();
|
||||
};
|
||||
};
|
||||
|
||||
currentUser.uid && getChats();
|
||||
}, [currentUser.uid]);
|
||||
|
||||
const handleSelect = (u) => {
|
||||
dispatch({ type: "CHANGE_USER", payload: u });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chats">
|
||||
{Object.entries(chats)?.sort((a, b) => b[1].date - a[1].date).map((chat) => (
|
||||
<div
|
||||
className="userChat"
|
||||
key={chat[0]}
|
||||
onClick={() => handleSelect(chat[1].userInfo)}
|
||||
>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
|
||||
<div className="userChatInfo">
|
||||
<span>{chat[1].userInfo.displayName}</span>
|
||||
<p>{chat[1].lastMessage?.text}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chats;
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react'
|
||||
import Sidebar from './Sidebar'
|
||||
import Chat from './Chat'
|
||||
import './chat.css'
|
||||
|
||||
|
||||
const Dashboard = () => {
|
||||
return (
|
||||
<div className='dashboard'>
|
||||
<div className="dash-container">
|
||||
<Sidebar />
|
||||
<Chat />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
|
@ -0,0 +1,68 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AuthContext } from "../context/AuthContext";
|
||||
import { ChatContext } from "../context/ChatContext";
|
||||
import { FaPlay, FaPause } from 'react-icons/fa';
|
||||
|
||||
const Message = ({ message }) => {
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
const { data } = useContext(ChatContext);
|
||||
const [audioUrl, setAudioUrl] = useState(null);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const audioRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (message.audio) {
|
||||
setAudioUrl(message.audio);
|
||||
}
|
||||
}, [message]);
|
||||
const handlePlayPause = () => {
|
||||
if (audioRef.current) {
|
||||
if (isPlaying) {
|
||||
audioRef.current.pause();
|
||||
} else {
|
||||
audioRef.current.play();
|
||||
}
|
||||
setIsPlaying(!isPlaying);
|
||||
}
|
||||
};
|
||||
const handleAudioEnd = () => {
|
||||
setIsPlaying(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`message ${message.senderId === currentUser.uid && "owner"}`}
|
||||
>
|
||||
<div className="messageInfo">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
|
||||
<span>just now</span>
|
||||
</div>
|
||||
<div className="messageContent">
|
||||
{audioUrl ? (
|
||||
<div className="audiowrapper">
|
||||
{isPlaying ? (
|
||||
<FaPause
|
||||
style={{ marginRight: '5px', marginTop: '2px', cursor: 'pointer' }}
|
||||
onClick={handlePlayPause}
|
||||
/>
|
||||
) : (
|
||||
<FaPlay
|
||||
style={{ marginRight: '5px', marginTop: '2px', cursor: 'pointer' }}
|
||||
onClick={handlePlayPause}
|
||||
/>
|
||||
)}
|
||||
Audio
|
||||
<audio ref={audioRef} style={{ display: 'none' }} onEnded={handleAudioEnd}>
|
||||
<source src={audioUrl} type="audio/mpeg" />
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
</div>
|
||||
) : (
|
||||
<p>{message.text}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Message;
|
|
@ -0,0 +1,46 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { signOut } from "firebase/auth";
|
||||
import { auth, db } from "../firebase";
|
||||
import { AuthContext } from '../context/AuthContext';
|
||||
import { getDoc, doc } from "firebase/firestore";
|
||||
|
||||
const Nav = () => {
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
const [displayName, setDisplayName] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDisplayName = async () => {
|
||||
if (currentUser) {
|
||||
try {
|
||||
const userDoc = await getDoc(doc(db, "users", currentUser.uid));
|
||||
if (userDoc.exists()) {
|
||||
const userData = userDoc.data();
|
||||
setDisplayName(userData.displayName);
|
||||
} else {
|
||||
console.log("User document does not exist");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching user document:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchDisplayName();
|
||||
|
||||
}, [currentUser]);
|
||||
|
||||
return (
|
||||
<div className='navbar'>
|
||||
<span className='logo'>Inconnect Chat</span>
|
||||
<div className="user">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
|
||||
<span>{displayName || "Guest"}</span>
|
||||
{currentUser && (
|
||||
<button onClick={() => signOut(auth)}>Logout</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import Nav from './Nav';
|
||||
import BotPage from './bot_page';
|
||||
import Chatting from './Chatting';
|
||||
|
||||
const Sidebar = () => {
|
||||
return (
|
||||
<div className='sidebar'>
|
||||
<Nav />
|
||||
<BotPage />
|
||||
<Chatting />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
|
@ -0,0 +1,116 @@
|
|||
import React, { useContext, useState, useEffect } from "react";
|
||||
import {
|
||||
collection,
|
||||
query,
|
||||
where,
|
||||
getDocs,
|
||||
setDoc,
|
||||
doc,
|
||||
updateDoc,
|
||||
serverTimestamp,
|
||||
getDoc,
|
||||
} from "firebase/firestore";
|
||||
import { db } from "../firebase";
|
||||
import { AuthContext } from "../context/AuthContext";
|
||||
|
||||
const Search = () => {
|
||||
const [username, setUsername] = useState("");
|
||||
const [user, setUser] = useState(null);
|
||||
const [err, setErr] = useState(false);
|
||||
const [chatExists, setChatExists] = useState(false);
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
|
||||
const predefinedUserId = "el9VwVaAlzbwUtZqqHQtundVp1p1";
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPredefinedUser = async () => {
|
||||
try {
|
||||
const userDoc = await getDoc(doc(db, "users", predefinedUserId));
|
||||
if (userDoc.exists()) {
|
||||
setUser(userDoc.data());
|
||||
} else {
|
||||
setErr(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching predefined user:", error);
|
||||
setErr(true);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPredefinedUser();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
const combinedId =
|
||||
currentUser.uid > user.uid
|
||||
? currentUser.uid + user.uid
|
||||
: user.uid + currentUser.uid;
|
||||
|
||||
const checkChatExistence = async () => {
|
||||
try {
|
||||
const res = await getDoc(doc(db, "chats", combinedId));
|
||||
setChatExists(res.exists());
|
||||
} catch (error) {
|
||||
console.error("Error checking chat existence:", error);
|
||||
}
|
||||
};
|
||||
|
||||
checkChatExistence();
|
||||
}
|
||||
}, [user, currentUser.uid]);
|
||||
|
||||
const handleSelect = async () => {
|
||||
const combinedId =
|
||||
currentUser.uid > user.uid
|
||||
? currentUser.uid + user.uid
|
||||
: user.uid + currentUser.uid;
|
||||
|
||||
try {
|
||||
const res = await getDoc(doc(db, "chats", combinedId));
|
||||
|
||||
if (!res.exists()) {
|
||||
await setDoc(doc(db, "chats", combinedId), { messages: [] });
|
||||
const currentUserChatUpdate = {
|
||||
[combinedId + ".userInfo"]: {
|
||||
uid: user.uid,
|
||||
displayName: user.displayName,
|
||||
},
|
||||
[combinedId + ".date"]: serverTimestamp(),
|
||||
};
|
||||
await updateDoc(doc(db, "userChats", currentUser.uid), currentUserChatUpdate);
|
||||
|
||||
const otherUserChatUpdate = {
|
||||
[combinedId + ".userInfo"]: {
|
||||
uid: currentUser.uid,
|
||||
displayName: currentUser.displayName,
|
||||
},
|
||||
[combinedId + ".date"]: serverTimestamp(),
|
||||
};
|
||||
await updateDoc(doc(db, "userChats", user.uid), otherUserChatUpdate);
|
||||
setUser(null);
|
||||
setUsername("");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating chat:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="search">
|
||||
{user && !chatExists && (
|
||||
<div className="userChat" onClick={handleSelect}>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
|
||||
<div className="userChatInfo">
|
||||
{/* Render the display name only if the chat does not exist */}
|
||||
<span>{user.displayName}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
|
@ -0,0 +1,257 @@
|
|||
.dashboard {
|
||||
background-color: #a7bcff;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dash-container {
|
||||
border: 1px solid white;
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 1;
|
||||
border-right: 1px solid #3e3c61;
|
||||
background-color: #3e3c61;
|
||||
|
||||
.bot {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
|
||||
.userChat {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #2f2d52;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #2f2d52;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
justify-content: space-between;
|
||||
color: #ddddf7;
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
img {
|
||||
background-color: #ddddf7;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #5d5b8d;
|
||||
color: #ddddf7;
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.chat {
|
||||
flex: 2;
|
||||
|
||||
.chatInfo {
|
||||
height: 50px;
|
||||
background-color: #5d5b8d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
color: lightgray;
|
||||
}
|
||||
}
|
||||
|
||||
.preview {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.messages {
|
||||
background-color: #ddddf7;
|
||||
padding: 10px;
|
||||
height: calc(100% - 160px);
|
||||
overflow: hidden;
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
.messageInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: gray;
|
||||
font-weight: 300;
|
||||
|
||||
img {
|
||||
margin-top: 10px;
|
||||
margin-left: 0px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 15px;
|
||||
margin-left: -10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.messageContent {
|
||||
max-width: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
img {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.audiowrapper {
|
||||
margin-top: 15px;
|
||||
background-color: #8da4f1;
|
||||
padding: 10px 20px;
|
||||
color: white;
|
||||
border-radius: 0px 10px 10px 10px;
|
||||
max-width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
&.owner {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.message {
|
||||
align-items: flex-end;
|
||||
|
||||
.emptyBox {
|
||||
background-color: #8da4f1;
|
||||
color: white;
|
||||
border-radius: 10px 0px 10px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 50px;
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.left-buttons {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.right-buttons button {
|
||||
margin-left: 120px;
|
||||
background-color: #8da4f1;
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 15px;
|
||||
padding: 5px 20px;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.left-buttons button {
|
||||
margin-right: 80px;
|
||||
background-color: #8da4f1;
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 15px;
|
||||
padding: 5px 20px;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.recordButton {
|
||||
font-size: 15px;
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uploadButton {
|
||||
font-size: 15px;
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-right: 30px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.send {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 70px;
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
background-color: #8da4f1;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
import { Timestamp, arrayUnion, updateDoc, doc, serverTimestamp } from 'firebase/firestore';
|
||||
import React, { useRef, useState, useEffect, useContext } from 'react';
|
||||
import { FaMicrophone, FaUpload, FaPlay, FaPause, FaTimes } from 'react-icons/fa';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import { AuthContext } from "../context/AuthContext";
|
||||
import { ChatContext } from "../context/ChatContext";
|
||||
import { db, storage } from "../firebase";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
|
||||
|
||||
const Input = () => {
|
||||
const wavesurferRef = useRef(null);
|
||||
const [audioSrc, setAudioSrc] = useState(null);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [mediaRecorder, setMediaRecorder] = useState(null);
|
||||
const [audio, setAudio] = useState(null);
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
const { data } = useContext(ChatContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (audioSrc) {
|
||||
const wavesurfer = WaveSurfer.create({
|
||||
container: wavesurferRef.current,
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple',
|
||||
barWidth: 2,
|
||||
cursorWidth: 1,
|
||||
height: 70,
|
||||
responsive: true,
|
||||
});
|
||||
wavesurfer.load(audioSrc);
|
||||
wavesurfer.on('ready', function () {
|
||||
wavesurfer.setVolume(0.5);
|
||||
});
|
||||
wavesurfer.on('play', function () {
|
||||
setIsPlaying(true);
|
||||
});
|
||||
wavesurfer.on('pause', function () {
|
||||
setIsPlaying(false);
|
||||
});
|
||||
wavesurferRef.current = wavesurfer;
|
||||
}
|
||||
}, [audioSrc]);
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
if (file.type.startsWith('audio/')) {
|
||||
setAudio(file);
|
||||
setAudioSrc(URL.createObjectURL(file));
|
||||
} else {
|
||||
alert("Please select an audio file.");
|
||||
event.target.value = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startRecording = () => {
|
||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
.then(stream => {
|
||||
const recorder = new MediaRecorder(stream);
|
||||
setMediaRecorder(recorder);
|
||||
const chunks = [];
|
||||
recorder.ondataavailable = e => {
|
||||
chunks.push(e.data);
|
||||
};
|
||||
recorder.onstop = () => {
|
||||
const blob = new Blob(chunks, { type: 'audio/webm' });
|
||||
const audioFile = new File([blob], `${uuid()}.wav`, { type: 'audio/wav' });
|
||||
setAudioSrc(URL.createObjectURL(blob));
|
||||
setAudio(audioFile);
|
||||
};
|
||||
recorder.start();
|
||||
setIsRecording(true);
|
||||
})
|
||||
.catch(error => console.error("Error accessing the microphone:", error));
|
||||
}
|
||||
};
|
||||
|
||||
const stopRecording = () => {
|
||||
if (mediaRecorder && isRecording) {
|
||||
mediaRecorder.stop();
|
||||
setIsRecording(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecordClick = () => {
|
||||
if (!isRecording) {
|
||||
startRecording();
|
||||
} else {
|
||||
stopRecording();
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlayPause = () => {
|
||||
if (wavesurferRef.current) {
|
||||
if (isPlaying) {
|
||||
wavesurferRef.current.pause();
|
||||
} else {
|
||||
wavesurferRef.current.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setAudio(null);
|
||||
setAudioSrc(null);
|
||||
};
|
||||
|
||||
const handleSend = async () => {
|
||||
if (audio) {
|
||||
const storageRef = ref(storage, uuid());
|
||||
|
||||
try {
|
||||
await uploadBytes(storageRef, audio);
|
||||
|
||||
const downloadURL = await getDownloadURL(storageRef);
|
||||
await updateDoc(doc(db, "chats", data.chatId), {
|
||||
messages: arrayUnion({
|
||||
id: uuid(),
|
||||
senderId: currentUser.uid,
|
||||
date: Timestamp.now(),
|
||||
audio: downloadURL,
|
||||
}),
|
||||
});
|
||||
|
||||
setAudio(null);
|
||||
setAudioSrc(null);
|
||||
|
||||
const lastMessage = { text: "audio" };
|
||||
const userChatsUpdates = {
|
||||
[data.chatId + ".lastMessage"]: lastMessage,
|
||||
[data.chatId + ".date"]: serverTimestamp(),
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
updateDoc(doc(db, "userChats", currentUser.uid), userChatsUpdates),
|
||||
updateDoc(doc(db, "userChats", data.user.uid), userChatsUpdates),
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error("Error uploading audio:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className='input'>
|
||||
{audioSrc ? (
|
||||
<>
|
||||
<div className='left-buttons'>
|
||||
<button onClick={handlePlayPause}>
|
||||
{isPlaying ? <FaPause /> : <FaPlay />}
|
||||
</button>
|
||||
<button onClick={handleCancel}>
|
||||
<FaTimes />
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ width: '30%' }} ref={wavesurferRef}></div>
|
||||
<div className='right-buttons'>
|
||||
<button onClick={handleSend}>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<input
|
||||
type="file"
|
||||
accept="audio/*"
|
||||
onChange={handleFileChange}
|
||||
style={{ display: 'none' }}
|
||||
id="fileInput"
|
||||
/>
|
||||
<label htmlFor="fileInput" className="uploadButton">
|
||||
<FaUpload />
|
||||
</label>
|
||||
<h5>or</h5>
|
||||
<label htmlFor="recordButton" className="recordButton" onClick={handleRecordClick}>
|
||||
{isRecording ? <FaMicrophone style={{ color: 'red' }} /> : <FaMicrophone />}
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Input;
|
|
@ -0,0 +1,31 @@
|
|||
import { doc, onSnapshot } from "firebase/firestore";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { ChatContext } from "../context/ChatContext";
|
||||
import { db } from "../firebase";
|
||||
import Message from "./Message";
|
||||
const Messages = () => {
|
||||
const [messages, setMessages] = useState([]);
|
||||
const { data } = useContext(ChatContext);
|
||||
|
||||
useEffect(() => {
|
||||
const unSub = onSnapshot(doc(db, "chats", data.chatId), (doc) => {
|
||||
doc.exists() && setMessages(doc.data().messages);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unSub();
|
||||
};
|
||||
}, [data.chatId]);
|
||||
|
||||
console.log(messages);
|
||||
|
||||
return (
|
||||
<div className='messages'>
|
||||
{messages.map(
|
||||
m => <Message key={m.id} message={m} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Messages;
|
|
@ -0,0 +1,107 @@
|
|||
import WaveSurfer from 'wavesurfer.js';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import './uploader.css';
|
||||
import upload from './upload.svg';
|
||||
|
||||
const Uploader = () => {
|
||||
const [audioSrc, setAudioSrc] = useState(null);
|
||||
const waveformRef = useRef(null);
|
||||
const wavesurferRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (audioSrc) {
|
||||
const wavesurfer = WaveSurfer.create({
|
||||
container: waveformRef.current,
|
||||
waveColor: 'black',
|
||||
progressColor: 'gray',
|
||||
cursorWidth: 0,
|
||||
barWidth: 2,
|
||||
barRadius: 2,
|
||||
});
|
||||
wavesurfer.load(audioSrc);
|
||||
wavesurferRef.current = wavesurfer;
|
||||
}
|
||||
}, [audioSrc]);
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const fileType = file.type.split('/')[0];
|
||||
if (fileType === 'audio') {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
setAudioSrc(event.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
alert('Please upload an audio file.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleUpload = () => {
|
||||
console.log("Uploading audio:", audioSrc);
|
||||
};
|
||||
|
||||
const handlePlayPause = () => {
|
||||
if (wavesurferRef.current) {
|
||||
wavesurferRef.current.playPause();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = (e) => {
|
||||
e.preventDefault();
|
||||
setAudioSrc(null);
|
||||
if (wavesurferRef.current) {
|
||||
wavesurferRef.current.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleProceed = () => {
|
||||
// Proceed with uploaded audio
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='hero'>
|
||||
<h1 className='convert-text'>Convert</h1>
|
||||
<p className='convert-paragraph'>Convert speech to sign language easily</p>
|
||||
<label htmlFor="input-file" id="drop-area">
|
||||
<input
|
||||
type="file"
|
||||
accept="audio/*"
|
||||
id="input-file"
|
||||
hidden
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<div id="audio-play">
|
||||
{audioSrc ? (
|
||||
<>
|
||||
<div className="container-1">
|
||||
<button onClick={handlePlayPause} className='btn'>Play/Pause</button>
|
||||
<button onClick={(e) => handleCancel(e)} className='btn'>Cancel</button>
|
||||
</div>
|
||||
|
||||
{audioSrc && (
|
||||
<div id="audio-preview">
|
||||
<div ref={waveformRef} id="waveform" />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<img src={upload} alt="upload" />
|
||||
<p>Drag and drop or click here<br />to upload audio</p>
|
||||
<span>Upload Audio</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
<button onClick={handleProceed} className='btn-2'>Convert</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Uploader;
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" id="upload"><path d="M398.1 233.2c0-1.2.2-2.4.2-3.6 0-65-51.8-117.6-115.7-117.6-46.1 0-85.7 27.4-104.3 67-8.1-4.1-17.2-6.5-26.8-6.5-29.5 0-54.1 21.9-58.8 50.5C57.3 235.2 32 269.1 32 309c0 50.2 40.1 91 89.5 91H224v-80h-48.2l80.2-83.7 80.2 83.6H288v80h110.3c45.2 0 81.7-37.5 81.7-83.4 0-45.9-36.7-83.2-81.9-83.3z"></path></svg>
|
After Width: | Height: | Size: 412 B |
|
@ -0,0 +1,104 @@
|
|||
.hero {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background: #e4e8ed86;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.convert-text {
|
||||
font-size: 8rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.convert-paragraph {
|
||||
font-size: 2.3rem;
|
||||
padding: 0 0 5px 0;
|
||||
}
|
||||
|
||||
#drop-area {
|
||||
margin-top: 70px;
|
||||
width: 1000px;
|
||||
height: 300px;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#audio-play {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px dashed #000;
|
||||
background: #f7f8ff;
|
||||
}
|
||||
|
||||
#audio-play img {
|
||||
width: 100px;
|
||||
margin-top: 25px;
|
||||
|
||||
}
|
||||
|
||||
#audio-play p {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#audio-play span {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#audio-preview {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
gap: 13px;
|
||||
margin-top: 12px;
|
||||
margin-left: 10px;
|
||||
font-family: 'Primary', Arial, sans-serif;
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
background-color: black;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 10px 24px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.btn-2 {
|
||||
gap: 13px;
|
||||
margin-top: 30px;
|
||||
margin-left: 10px;
|
||||
font-size: 1.5rem;
|
||||
font-family: 'Primary', Arial, sans-serif;
|
||||
/* Adding fallback fonts */
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
background-color: #000;
|
||||
/* Green */
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 20px 30px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-2:hover {
|
||||
background-color: gray;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import React from 'react';
|
||||
import styled from "styled-components";
|
||||
import { BsFacebook, BsTwitter, BsYoutube, BsBehance } from 'react-icons/bs';
|
||||
import { motion } from "framer-motion";
|
||||
import { useScroll } from "../useScroll";
|
||||
import { footerTextAnimation } from "../Animation";
|
||||
|
||||
function Footer() {
|
||||
const [element, controls] = useScroll();
|
||||
return (
|
||||
<Foot ref={element}>
|
||||
<motion.span
|
||||
variants={footerTextAnimation}
|
||||
animate={controls}
|
||||
transition={{
|
||||
delay: 0.02,
|
||||
type: "tween",
|
||||
duration: 0.8,
|
||||
}}
|
||||
>
|
||||
© Neural Harbour AI
|
||||
</motion.span>
|
||||
<motion.div className="footer__social__icons"
|
||||
variants={footerTextAnimation}
|
||||
animate={controls}
|
||||
transition={{
|
||||
delay: 0.02,
|
||||
type: "tween",
|
||||
duration: 0.8,
|
||||
}}
|
||||
>
|
||||
<BsFacebook />
|
||||
<BsTwitter />
|
||||
<BsYoutube />
|
||||
<BsBehance />
|
||||
</motion.div>
|
||||
</Foot>
|
||||
)
|
||||
}
|
||||
|
||||
const Foot = styled(motion.footer)`
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5rem 10rem;
|
||||
.footer__social__icons {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
svg {
|
||||
font-size: 1.4rem;
|
||||
cursor: pointer;
|
||||
transition: 0.5s ease-in-out;
|
||||
&:hover {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 280px) and (max-width: 1080px) {
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Footer
|
|
@ -0,0 +1,147 @@
|
|||
$theme: #454cad;
|
||||
$dark-text: #5f6982;
|
||||
|
||||
.uploader {
|
||||
display: block;
|
||||
clear: both;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
|
||||
label {
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
border-radius: 7px;
|
||||
border: 3px solid #eee;
|
||||
transition: all .2s ease;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
border-color: $theme;
|
||||
}
|
||||
|
||||
&.hover {
|
||||
border: 3px solid $theme;
|
||||
box-shadow: inset 0 0 0 6px #eee;
|
||||
|
||||
#start i.fa {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#start {
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
i.fa {
|
||||
font-size: 50px;
|
||||
margin-bottom: 1rem;
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
#response {
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#messages {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
#file-image {
|
||||
display: inline;
|
||||
margin: 0 auto .5rem auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 180px;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#notimage {
|
||||
display: block;
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
progress,
|
||||
.progress {
|
||||
display: inline;
|
||||
clear: both;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 180px;
|
||||
height: 8px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background-color: #eee;
|
||||
overflow: hidden;
|
||||
|
||||
&[value]::-webkit-progress-bar {
|
||||
border-radius: 4px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
&[value]::-webkit-progress-value,
|
||||
&[value]::-moz-progress-bar {
|
||||
background: linear-gradient(to right, darken($theme, 8%) 0%, $theme 50%);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 0 0 .5rem 0;
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin: .5rem .5rem 1rem .5rem;
|
||||
clear: both;
|
||||
font-family: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
text-transform: initial;
|
||||
border: none;
|
||||
border-radius: .2rem;
|
||||
outline: none;
|
||||
padding: 0 1rem;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
transition: all 0.2s ease-in-out;
|
||||
box-sizing: border-box;
|
||||
background: $theme;
|
||||
border-color: $theme;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 642 KiB |
|
@ -0,0 +1,46 @@
|
|||
import Splitting from 'splitting';
|
||||
import { gsap } from 'gsap';
|
||||
|
||||
const nav = document.querySelector('.nav');
|
||||
const section = {
|
||||
title: Splitting({
|
||||
target: '.section_title',
|
||||
by: 'chars',
|
||||
}),
|
||||
paragraphs: Splitting({
|
||||
target: '.section_col',
|
||||
by: 'words',
|
||||
}),
|
||||
image: document.querySelector('.section_col_image'),
|
||||
overlay: document.querySelector('.section_col_overlay'),
|
||||
};
|
||||
|
||||
const animateSection = () => {
|
||||
const titleChars = section.title[0].chars;
|
||||
const paragraphsWords = section.paragraphs[0].words;
|
||||
|
||||
gsap.set(titleChars, { autoAlpha: 0, yPercent: -100, rotate: '-15deg' });
|
||||
gsap.set(paragraphsWords, { autoAlpha: 0, display: 'inline-flex' });
|
||||
gsap.set(section.image, { autoAlpha: 0, scale: 1.5 });
|
||||
|
||||
const t1 = gsap.timeline({ defaults: { duration: 1.64, ease: 'power4.inOut' } });
|
||||
|
||||
t1.addLabel('start')
|
||||
.from(nav, { yPercent: -100 })
|
||||
.addLabel('section')
|
||||
.to(titleChars, {
|
||||
autoAlpha: 1,
|
||||
yPercent: 0,
|
||||
rotate: '0deg',
|
||||
stagger: 0.024,
|
||||
}, 'start+=0.6')
|
||||
.to(paragraphsWords, {
|
||||
autoAlpha: 1,
|
||||
stagger: 0.024,
|
||||
}, 'start+=0.6')
|
||||
.addLabel('image')
|
||||
.to(section.overlay, { yPercent: 101 }, 'start+=0.6')
|
||||
.to(section.image, { autoAlpha: 1, scale: 1 }, 'start+=0.6');
|
||||
};
|
||||
|
||||
export default animateSection;
|
|
@ -0,0 +1,179 @@
|
|||
:root {
|
||||
--primary: #e4e8ed;
|
||||
--secondary: #191919;
|
||||
--alternate: #9db2ea;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Primary";
|
||||
src: url("../../fonts/PPMori-Regular.woff2") format("woff2");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: max(100vw / 1920 * 10);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Primary";
|
||||
background-color: var(--primary);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 10rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 8rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
li,
|
||||
span {
|
||||
font-size: 2.4rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.Lander {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
gap: 1rem 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 40;
|
||||
background-color: rgb(236, 244, 255);
|
||||
}
|
||||
|
||||
.nav_wrapper {
|
||||
grid-template-areas: "a a b b b b . . . c d d";
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.nav_logo {
|
||||
grid-area: a;
|
||||
}
|
||||
|
||||
.nav_pages {
|
||||
grid-area: b;
|
||||
display: inline-flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav_pages span a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav_menu {
|
||||
grid-area: c;
|
||||
}
|
||||
|
||||
.nav_menu_button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav_menu_line {
|
||||
display: block;
|
||||
width: 6.4rem;
|
||||
height: 0.2rem;
|
||||
background-color: var(--secondary);
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.section {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section_wrapper {
|
||||
display: flex;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.section_title {
|
||||
position: absolute;
|
||||
top: 16rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 20;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section_title h2,
|
||||
.section_title span {
|
||||
display: inline-flex;
|
||||
font-size: 10rem;
|
||||
}
|
||||
|
||||
.section_col {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section_col_overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--primary);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section_col_image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 808px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.section_col:nth-child(2) {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.section_col_left {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 18rem;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.section_col_left_text h4,
|
||||
.section_col_left_text span {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.section_col_left_cta {
|
||||
display: inline-flex;
|
||||
gap: 1.6rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.section_col_right {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import img from './IMG-KONVERT.jpg';
|
||||
import './Landing.css';
|
||||
import Splitting from 'splitting';
|
||||
import { gsap } from 'gsap';
|
||||
import { Link } from "react-router-dom";
|
||||
const LandingPage = () => {
|
||||
const navRef = useRef(null);
|
||||
const sectionTitleRef = useRef(null);
|
||||
const sectionColRef = useRef(null);
|
||||
const sectionImageRef = useRef(null);
|
||||
const sectionOverlayRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const nav = navRef.current;
|
||||
const sectionTitle = sectionTitleRef.current;
|
||||
const sectionCol = sectionColRef.current;
|
||||
const sectionImage = sectionImageRef.current;
|
||||
const sectionOverlay = sectionOverlayRef.current;
|
||||
|
||||
const section = {
|
||||
title: Splitting({
|
||||
target: sectionTitle,
|
||||
by: 'chars',
|
||||
}),
|
||||
paragraphs: Splitting({
|
||||
target: sectionCol,
|
||||
by: 'words',
|
||||
}),
|
||||
image: sectionImage,
|
||||
overlay: sectionOverlay,
|
||||
};
|
||||
|
||||
const titleChars = section.title[0].chars;
|
||||
const paragraphsWords = section.paragraphs[0].words;
|
||||
|
||||
gsap.set(titleChars, { autoAlpha: 0, yPercent: -100, rotate: '-15deg' });
|
||||
gsap.set(paragraphsWords, { autoAlpha: 0, display: 'inline-flex' });
|
||||
gsap.set(section.image, { autoAlpha: 0, scale: 1.5 });
|
||||
|
||||
const t1 = gsap.timeline({ defaults: { duration: 1.64, ease: 'power4.inOut' } });
|
||||
|
||||
t1.addLabel('section')
|
||||
.to(titleChars, {
|
||||
autoAlpha: 1,
|
||||
yPercent: 0,
|
||||
rotate: '0deg',
|
||||
stagger: 0.024,
|
||||
|
||||
}, 'start+=0.6')
|
||||
.to(paragraphsWords, {
|
||||
autoAlpha: 1,
|
||||
stagger: 0.024,
|
||||
}, 'start+=0.6')
|
||||
.to(section.overlay, { yPercent: 101 })
|
||||
.to(section.image, { autoAlpha: 1, scale: 1 }, '-=2');
|
||||
}, []);
|
||||
|
||||
const handleFileUpload = (event) => {
|
||||
const file = event.target.files[0];
|
||||
};
|
||||
|
||||
return (
|
||||
<body>
|
||||
<main className="Lander">
|
||||
<div className="nav" ref={navRef}>
|
||||
<div className="nav_wrapper grid">
|
||||
<div className="nav_logo">
|
||||
<span>INCONNECT</span>
|
||||
</div>
|
||||
<div className="nav_pages">
|
||||
<span>Home</span>
|
||||
<span><Link to="/login">Login</Link></span>
|
||||
<span><Link to="/Register">SignUp</Link></span>
|
||||
</div>
|
||||
<div className='nav_menu'>
|
||||
<div className="nav_menu_button menu-open">
|
||||
<span className='nav_menu_line'></span>
|
||||
<span className='nav_menu_line'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section className='section'>
|
||||
<div className="section_wrapper">
|
||||
<div className="section_title" ref={sectionTitleRef}>
|
||||
<h2>Inconnect</h2>
|
||||
</div>
|
||||
<div className="section_col" ref={sectionColRef}>
|
||||
<div className="section_col_left">
|
||||
<h4 className='section_col_left_text'>
|
||||
Next-Gen Speech to Sign Language Technology. Introducing our revolutionary Intelligent Sign Language Technology,Powered by cutting-edge AI technology.
|
||||
</h4>
|
||||
<div className="setion_col_left_cta">
|
||||
<span>→</span>
|
||||
<span>Powered By Neural Harbour AI</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="section_col_right">
|
||||
<p className="section_col_right_text">
|
||||
Our sophisticated algorithms delve into the speech's intricate details,enabling anyone to perform quick,accurate and comprehensive conversions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='section_col'>
|
||||
<div className="section_col_overlay" ref={sectionOverlayRef}></div>
|
||||
<img src={img} alt="dummy" className='section_col_image' ref={sectionImageRef} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body >
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingPage;
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
@ -0,0 +1,26 @@
|
|||
import { createContext, useEffect, useState } from "react";
|
||||
import { auth } from "../firebase";
|
||||
import { onAuthStateChanged } from "firebase/auth";
|
||||
|
||||
export const AuthContext = createContext();
|
||||
|
||||
export const AuthContextProvider = ({ children }) => {
|
||||
const [currentUser, setCurrentUser] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = onAuthStateChanged(auth, (user) => {
|
||||
setCurrentUser(user);
|
||||
console.log(user);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsub();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ currentUser }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useReducer,
|
||||
} from "react";
|
||||
import { AuthContext } from "./AuthContext";
|
||||
|
||||
export const ChatContext = createContext();
|
||||
|
||||
export const ChatContextProvider = ({ children }) => {
|
||||
const { currentUser } = useContext(AuthContext);
|
||||
const INITIAL_STATE = {
|
||||
chatId: "null",
|
||||
user: {},
|
||||
};
|
||||
|
||||
const chatReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CHANGE_USER":
|
||||
return {
|
||||
user: action.payload,
|
||||
chatId:
|
||||
currentUser.uid > action.payload.uid
|
||||
? currentUser.uid + action.payload.uid
|
||||
: action.payload.uid + currentUser.uid,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const [state, dispatch] = useReducer(chatReducer, INITIAL_STATE);
|
||||
|
||||
return (
|
||||
<ChatContext.Provider value={{ data: state, dispatch }}>
|
||||
{children}
|
||||
</ChatContext.Provider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import { initializeApp } from "firebase/app";
|
||||
import { getAuth } from "firebase/auth";
|
||||
import { getStorage } from "firebase/storage";
|
||||
import { getFirestore } from "firebase/firestore";
|
||||
const firebaseConfig = {
|
||||
apiKey: "AIzaSyDKHA6ZmllEUfjqmQLd0h5ujiPo0bObTc0",
|
||||
authDomain: "inconnect-fb444.firebaseapp.com",
|
||||
projectId: "inconnect-fb444",
|
||||
storageBucket: "inconnect-fb444.appspot.com",
|
||||
messagingSenderId: "114710194072",
|
||||
appId: "1:114710194072:web:ee8de5852a8995789f2ded"
|
||||
};
|
||||
|
||||
export const app = initializeApp(firebaseConfig);
|
||||
export const auth = getAuth();
|
||||
export const storage = getStorage();
|
||||
export const db = getFirestore();
|
|
@ -0,0 +1,159 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
.bod {
|
||||
display: grid;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
place-items: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.contain {
|
||||
max-width: 350px;
|
||||
width: 100%;
|
||||
padding: 25px 35px;
|
||||
}
|
||||
|
||||
.contain form .title {
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
margin: 20px 0 10px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contain form .title::before {
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
width: 33px;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
border-radius: 5px;
|
||||
background: skyblue;
|
||||
}
|
||||
|
||||
.contain form .input-box {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
margin-top: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contain form .input-box input {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.contain form .underline:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #ccc;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.contain form .underline:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: linear-gradient(to right, #7AE5F5 0%, #C9F6FF 100%);
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: all 0.3s ease-in;
|
||||
}
|
||||
|
||||
.contain form .input-box input:focus~.underline:after,
|
||||
.contain form .input-box input:valid~.underline:after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
.contain form .input-box input[type="submit"] {
|
||||
font-size: 17px;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background: linear-gradient(to right, #7AE5F5 0%, #C9F6FF 100%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contain form .input-box input[type="submit"]:hover {
|
||||
letter-spacing: 1px;
|
||||
background: linear-gradient(to left, #7AE5F5 0%, #C9F6FF 100%);
|
||||
}
|
||||
|
||||
.contain form .button {
|
||||
margin: 40px 0 20px 0;
|
||||
}
|
||||
|
||||
.contain .option {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contain .google a,
|
||||
.contain .facebook a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
font-size: 15px;
|
||||
text-decoration: none;
|
||||
padding-left: 20px;
|
||||
line-height: 45px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.contain .google i,
|
||||
.contain .facebook i {
|
||||
|
||||
padding-right: 12px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.contain .google a {
|
||||
margin: 20px 0px 15px 0px;
|
||||
background: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.contain .facebook a {
|
||||
margin: 20px 0px 15px 0px;
|
||||
background: #3b5998;
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contain .crac {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.crac a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.back-button.responsive {
|
||||
display: block;
|
||||
position: fixed;
|
||||
left: 50px;
|
||||
top: 1.9rem;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import './Login.css'
|
||||
import { auth } from '../firebase'
|
||||
import { signInWithEmailAndPassword } from 'firebase/auth';
|
||||
const Login = () => {
|
||||
|
||||
const history = useNavigate();
|
||||
|
||||
const handleGoBack = () => {
|
||||
history(-1);
|
||||
};
|
||||
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const email = e.target.email.value;
|
||||
const password = e.target.password.value;
|
||||
|
||||
try {
|
||||
await signInWithEmailAndPassword(auth, email, password);
|
||||
history("/Dashboard")
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<section className='bod'>
|
||||
<div onClick={handleGoBack} className="back-button responsive">
|
||||
<i className='fa-solid fa-arrow-left' />
|
||||
</div>
|
||||
<div className='contain'>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='title'>
|
||||
Login
|
||||
</div>
|
||||
<div className="input-box">
|
||||
<input type='Text' placeholder='Email' name="email" required />
|
||||
<div className='underline' />
|
||||
</div>
|
||||
<div className="input-box">
|
||||
<input type='password' placeholder='Password' name="password" required />
|
||||
<div className='underline' />
|
||||
</div>
|
||||
<div className="input-box button">
|
||||
<input type='submit' value="Login" />
|
||||
</div>
|
||||
{error && <span>{error}</span>}
|
||||
</form>
|
||||
<div className="crac">
|
||||
<Link to="/Register">Don't Have An Account ?</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
|
@ -0,0 +1,76 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import './Login.css';
|
||||
|
||||
import { createUserWithEmailAndPassword } from 'firebase/auth';
|
||||
import { auth, db } from '../firebase';
|
||||
import { doc, setDoc } from "firebase/firestore";
|
||||
|
||||
const Register = () => {
|
||||
const [error, setError] = useState(null);
|
||||
const history = useNavigate();
|
||||
|
||||
const handleGoBack = () => {
|
||||
history(-1);
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const displayName = e.target[0].value;
|
||||
const email = e.target[1].value;
|
||||
const password = e.target[2].value;
|
||||
|
||||
try {
|
||||
const res = await createUserWithEmailAndPassword(auth, email, password);
|
||||
await setDoc(doc(db, "users", res.user.uid), {
|
||||
uid: res.user.uid,
|
||||
displayName,
|
||||
email
|
||||
});
|
||||
|
||||
await setDoc(doc(db, "userChats", res.user.uid), {
|
||||
|
||||
});
|
||||
history("/Login");
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className='bod'>
|
||||
<div onClick={handleGoBack} className="back-button responsive">
|
||||
<i className='fa-solid fa-arrow-left' />
|
||||
</div>
|
||||
<div className='contain'>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='title'>
|
||||
SignUp
|
||||
</div>
|
||||
<div className="input-box">
|
||||
<input type='text' placeholder='Username' name='displayName' required />
|
||||
<div className='underline' />
|
||||
</div>
|
||||
<div className="input-box">
|
||||
<input type='email' placeholder='Email-ID' name='email' required />
|
||||
<div className='underline' />
|
||||
</div>
|
||||
<div className="input-box">
|
||||
<input type='password' placeholder='Password' name='password' required />
|
||||
<div className='underline' />
|
||||
</div>
|
||||
<div className="input-box button">
|
||||
<input type='submit' value="SignUp" />
|
||||
</div>
|
||||
{error && <span>{error}</span>}
|
||||
</form>
|
||||
<div className="crac">
|
||||
<Link to="/Login">Already Have An Account ?</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Register;
|
|
@ -0,0 +1,18 @@
|
|||
import { useInView } from "react-intersection-observer";
|
||||
import { useAnimation } from "framer-motion";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const useScroll = (thresh = 0.1) => {
|
||||
const controls = useAnimation();
|
||||
const [element, view] = useInView({ threshold: thresh });
|
||||
|
||||
useEffect(() => {
|
||||
if (view) {
|
||||
controls.start("show");
|
||||
} else {
|
||||
controls.start("hidden");
|
||||
}
|
||||
}, [controls, view]);
|
||||
|
||||
return [element, controls];
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { AuthContextProvider } from './Components/context/AuthContext';
|
||||
import { ChatContextProvider } from './Components/context/ChatContext';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<AuthContextProvider>
|
||||
<ChatContextProvider>
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
</ChatContextProvider>
|
||||
</AuthContextProvider>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,13 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
|
@ -0,0 +1,5 @@
|
|||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
|
@ -1,19 +0,0 @@
|
|||
import type { Config } from "tailwindcss";
|
||||
const config: Config = {
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
"gradient-conic":
|
||||
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
export default config;
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|