Skip to content

Commit 90dc7f9

Browse files
committed
Project Completed
0 parents  commit 90dc7f9

22 files changed

+760
-0
lines changed

package.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "google-docs-clone",
3+
"version": "0.1.0",
4+
"dependencies": {
5+
"bootstrap": "^5.3.2",
6+
"firebase": "^10.5.2",
7+
"react": "^18.2.0",
8+
"react-bootstrap": "^2.9.1",
9+
"react-dom": "^18.2.0",
10+
"react-quill": "^2.0.0",
11+
"react-router-dom": "^6.18.0",
12+
"react-scripts": "5.0.1"
13+
},
14+
"scripts": {
15+
"start": "react-scripts start",
16+
"build": "react-scripts build",
17+
"test": "react-scripts test",
18+
"eject": "react-scripts eject"
19+
},
20+
"eslintConfig": {
21+
"extends": [
22+
"react-app",
23+
"react-app/jest"
24+
]
25+
},
26+
"browserslist": {
27+
"production": [
28+
">0.2%",
29+
"not dead",
30+
"not op_mini all"
31+
],
32+
"development": [
33+
"last 2 chrome version",
34+
"last 2 firefox version",
35+
"last 2 safari version"
36+
]
37+
}
38+
}

public/favicon.ico

3.78 KB
Binary file not shown.

public/index.html

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<meta
9+
name="description"
10+
content="Web site created using create-react-app"
11+
/>
12+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13+
<!--
14+
manifest.json provides metadata used when your web app is installed on a
15+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16+
-->
17+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18+
<!--
19+
Notice the use of %PUBLIC_URL% in the tags above.
20+
It will be replaced with the URL of the `public` folder during the build.
21+
Only files inside the `public` folder can be referenced from the HTML.
22+
23+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24+
work correctly both with client-side routing and a non-root public URL.
25+
Learn how to configure a non-root public URL by running `npm run build`.
26+
-->
27+
<title>Google Docs Clone</title>
28+
</head>
29+
<body>
30+
<noscript>You need to enable JavaScript to run this app.</noscript>
31+
<div id="root"></div>
32+
<!--
33+
This HTML file is a template.
34+
If you open it directly in the browser, you will see an empty page.
35+
36+
You can add webfonts, meta tags, or analytics to this file.
37+
The build step will place the bundled scripts into the <body> tag.
38+
39+
To begin the development, run `npm start` or `yarn start`.
40+
To create a production bundle, use `npm run build` or `yarn build`.
41+
-->
42+
</body>
43+
</html>

public/logo192.png

5.22 KB
Loading

public/logo512.png

9.44 KB
Loading

public/manifest.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
}

public/robots.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://www.robotstxt.org/robotstxt.html
2+
User-agent: *
3+
Disallow:

src/components/App.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Outlet } from "react-router-dom";
2+
import { Container } from "react-bootstrap";
3+
import NavBar from "./Navbar";
4+
5+
function App() {
6+
return (
7+
<Container style={{"fontFamily" : "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"}}>
8+
<NavBar/>
9+
<Outlet/>
10+
</Container>
11+
);
12+
}
13+
14+
export default App;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useState } from "react";
2+
import './style/CreateNewDoc.css'
3+
import Modal from 'react-bootstrap/Modal';
4+
import Button from 'react-bootstrap/Button';
5+
// import { Container } from "react-bootstrap";
6+
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
7+
import { db } from "../../firebaseConfig";
8+
import { useAuth } from "../../context/AuthContext";
9+
import { useNavigate } from "react-router-dom";
10+
11+
export default function CreateNewDoc() {
12+
const [show,setShow] = useState(false);
13+
const [enteredTitle,setEnteredTitle] = useState("Untitled");
14+
const handleClose = () => setShow(false);
15+
const handleShow = () => setShow(true);
16+
const { currentUser } = useAuth();
17+
const colRef = collection(db,"users",currentUser.uid,"docs");
18+
const navigate = useNavigate()
19+
const createHandler = async() => {
20+
const querySnapShot = await addDoc(colRef,{
21+
title : enteredTitle,
22+
content : "",
23+
createdOn : serverTimestamp()
24+
})
25+
navigate(`/edit/${querySnapShot.id}`)
26+
}
27+
return (
28+
<div className="container new-doc">
29+
<p>Start a new Doc</p>
30+
<img
31+
src="https://ssl.gstatic.com/docs/templates/thumbnails/docs-blank-googlecolors.png"
32+
alt = "Click to create new doc"
33+
style={{
34+
"objectFit" : "contain",
35+
"border" : "1px solid black",
36+
"width" : "10rem",
37+
"marginBottom" : "1rem"
38+
}}
39+
onClick={() => handleShow()}
40+
/>
41+
<Modal show={show} onHide={handleClose}>
42+
<Modal.Header closeButton>
43+
<Modal.Title>Enter a Document title</Modal.Title>
44+
</Modal.Header>
45+
<Modal.Body>
46+
<input type="text" placeholder="Enter title..." value={enteredTitle} onChange={(e) => setEnteredTitle(e.target.value)} autoFocus/>
47+
</Modal.Body>
48+
<Modal.Footer>
49+
<Button variant="secondary" onClick={handleClose}>
50+
Close
51+
</Button>
52+
<Button variant="primary" onClick={()=>createHandler()}>
53+
Save Changes
54+
</Button>
55+
</Modal.Footer>
56+
</Modal>
57+
<h6>Blank</h6>
58+
</div>
59+
)
60+
}
61+

src/components/DashBoard/Dashboard.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React, { createContext, useContext, useEffect, useState } from "react";
2+
import CreateNewDoc from "./CreateNewDoc";
3+
import ListDoc from "./ListDoc";
4+
import { collection, getDocs,orderBy, query} from "firebase/firestore";
5+
import { db } from "../../firebaseConfig";
6+
import { useAuth } from "../../context/AuthContext";
7+
// import Editor from "../Editor";
8+
9+
function Dashboard() {
10+
const [userDocs, setUserDocs] = useState();
11+
const { currentUser } = useAuth();
12+
const docCollectionRef = query(collection(db,"users",currentUser.uid,'docs'),orderBy('createdOn','desc'))
13+
const getUserDocs = async () => {
14+
const querySnapShot = await getDocs(
15+
docCollectionRef
16+
);
17+
const udocs = querySnapShot.docs.map((doc) => ({
18+
id: doc.id,
19+
...doc.data(),
20+
}));
21+
// console.log(udocs);
22+
setUserDocs(udocs);
23+
};
24+
25+
useEffect(() => {
26+
getUserDocs();
27+
// eslint-disable-next-line react-hooks/exhaustive-deps
28+
}, []);
29+
30+
return (
31+
<>
32+
<CreateNewDoc />
33+
<ListDoc docs={userDocs} />
34+
</>
35+
);
36+
}
37+
38+
export default Dashboard;

src/components/DashBoard/Editor.css

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
body {
2+
background: #f3f1f2;
3+
font-family: sans-serif;
4+
}
5+
6+
#root {
7+
margin: 1rem 4rem;
8+
}
9+
10+
#root .ql-container {
11+
border-bottom-left-radius: 0.5em;
12+
border-bottom-right-radius: 0.5em;
13+
background: #fefcfc;
14+
}
15+
16+
/* Snow Theme */
17+
#root .ql-snow.ql-toolbar {
18+
display: block;
19+
background: #eaecec;
20+
border-top-left-radius: 0.5em;
21+
border-top-right-radius: 0.5em;
22+
}
23+
24+
#root .ql-editor {
25+
min-height: 18em;
26+
}
27+

src/components/DashBoard/Editor.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React, { useEffect, useState } from "react";
2+
import { Container } from "react-bootstrap";
3+
import ReactQuill, { Quill } from "react-quill";
4+
// import ImageResize from "quill-image-resize-module-react";
5+
import "./Editor.css";
6+
import "react-quill/dist/quill.snow.css";
7+
import { useNavigate, useParams } from "react-router-dom";
8+
import { doc, getDoc,setDoc, updateDoc } from "firebase/firestore";
9+
import { db } from "../../firebaseConfig";
10+
import { useAuth } from "../../context/AuthContext";
11+
12+
function Editor() {
13+
const [value, setValue] = useState("");
14+
const [docData,setDocData] = useState({});
15+
const { currentUser } = useAuth();
16+
const navigate = useNavigate();
17+
const param = useParams();
18+
// console.log(param.id);
19+
useEffect(() => {
20+
console.log("Mounted")
21+
const getData = async() => {
22+
try{
23+
const querySnapShot = await getDoc(doc(db,"users",currentUser.uid,"docs",param.id))
24+
if(querySnapShot.exists() === false){
25+
navigate("/");
26+
}
27+
setDocData(querySnapShot.data())
28+
setValue(querySnapShot.data().content);
29+
}
30+
catch{
31+
console.log("Trying to access someone else document");
32+
}
33+
}
34+
getData();
35+
// eslint-disable-next-line react-hooks/exhaustive-deps
36+
},[])
37+
const handlerEditorChange = (newContent) => {
38+
setValue(newContent);
39+
updateDoc(doc(db,"users",currentUser.uid,"docs",param.id),{
40+
"content" : value
41+
})
42+
}
43+
// Quill.register("modules/imageResize", ImageResize);
44+
return (
45+
<Container className="mt-2">
46+
<h1 className="text-center">{docData?.title}</h1>
47+
<ReactQuill
48+
theme="snow"
49+
onChange={(event)=>{handlerEditorChange(event)}}
50+
modules={module}
51+
value={value}
52+
formats={formats}
53+
bounds={"#root"}
54+
placeholder="Write Something"
55+
/>
56+
{/* <p>{value}</p> */}
57+
</Container>
58+
);
59+
}
60+
const module = {
61+
toolbar: [
62+
[{ header: "1" }, { header: "2" }, { font: [] }],
63+
[{ size: [] }],
64+
["bold", "italic", "underline", "strike", "blockquote"],
65+
[
66+
{ list: "ordered" },
67+
{ list: "bullet" },
68+
{ indent: "-1" },
69+
{ indent: "+1" },
70+
],
71+
],
72+
clipboard: {
73+
// toggle to add extra line breaks when pasting HTML:
74+
matchVisual: false,
75+
},
76+
// imageResize: {
77+
// parchment: Quill.import("parchment"),
78+
// modules: ["Resize", "DisplaySize"],
79+
// },
80+
};
81+
82+
const formats = [
83+
"header",
84+
"font",
85+
"size",
86+
"bold",
87+
"italic",
88+
"underline",
89+
"strike",
90+
"blockquote",
91+
"list",
92+
"bullet",
93+
"indent"
94+
];
95+
export default Editor;

src/components/DashBoard/ListDoc.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// import { useContext } from 'react';
2+
import Table from 'react-bootstrap/Table';
3+
import { useNavigate } from 'react-router-dom';
4+
// import { useEditContext } from './Dashboard';
5+
6+
function ListDoc({ docs }) {
7+
// const { edit } = useEditContext();
8+
// console.log(useEditContext);
9+
const navigate = useNavigate()
10+
return (
11+
<Table borderless hover responsive>
12+
<thead>
13+
<tr>
14+
<th>#</th>
15+
<th>Title</th>
16+
<th>Created On</th>
17+
<th>Delete</th>
18+
</tr>
19+
</thead>
20+
<tbody>
21+
{docs ? docs.map(doc => (
22+
<tr key={doc.id} onClick={() => {
23+
navigate(`/edit/${doc.id}`)
24+
}}>
25+
<td>-</td>
26+
<td>{doc?.title}</td>
27+
<td>{doc?.createdOn.toDate().toDateString()}</td>
28+
<td> - </td>
29+
</tr>
30+
31+
)):
32+
<>
33+
<tr>
34+
<td colSpan={4}>Create a new document by clicking on '+' icon</td>
35+
</tr>
36+
</>}
37+
</tbody>
38+
</Table>
39+
);
40+
}
41+
42+
export default ListDoc;

0 commit comments

Comments
 (0)