Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28,671 changes: 9,258 additions & 19,413 deletions twitter-clone/package-lock.json

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions twitter-clone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@
"@types/node": "^16.11.21",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"husky": "^9.1.7",
"lint-staged": "^15.5.1",
"prettier": "^3.5.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.14.2",
"react-scripts": "5.0.0",
"typescript": "^4.5.5",
"web-vitals": "^2.1.4"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier --write"
]
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
Expand All @@ -28,6 +37,11 @@
"react-app/jest"
]
},
"husk": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"browserslist": {
"production": [
">0.2%",
Expand Down
8 changes: 4 additions & 4 deletions twitter-clone/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
import React from "react";
import { render, screen } from "@testing-library/react";
import App from "./components/App";

test('renders learn react link', () => {
test("renders learn react link", () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
Expand Down
26 changes: 0 additions & 26 deletions twitter-clone/src/App.tsx

This file was deleted.

23 changes: 23 additions & 0 deletions twitter-clone/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Login } from "./Login";
import { Signup } from "./Signup";
import { NotFound } from './NotFound'
import { HomePage } from "./HomePage";

function App() {
return (
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/signup"
element={<Signup />}
/>
<Route path="/" element={<HomePage />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
}

export default App;
58 changes: 58 additions & 0 deletions twitter-clone/src/components/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import "../styles/home-page.css"

import { tweets } from '../constants';
import { getInitials } from '../helper';

export const HomePage = () => {
const navigate = useNavigate();
const [tweet, setTweet] = useState('');

const storedItem = localStorage.getItem('user');
const userObject = storedItem ? JSON.parse(storedItem) : null;


useEffect(() => {
if ((!userObject?.name && !userObject?.email) || !userObject) {
navigate('/login');
}
}, [])


const handleAddTweet = () => {
tweets.unshift({ user: userObject?.name, content: tweet});
setTweet('');
};

return (
<div className="app">
<header className="header">
<div className="logo">🐦 Another Twitter Clone</div>
<div className="user-info">
{userObject.name} <span className="avatar">{getInitials(userObject.name)}</span>
</div>
</header>
<main className="main">
<textarea
className="tweet-box"
placeholder="What&apos;s happening?"
value={tweet}
onChange={(e) => setTweet(e.target.value)}
/>
<button className="tweet-button" onClick={handleAddTweet}>Tweet</button>
<div className="feed">
{tweets.map((t, index) => (
<div key={index} className="tweet">
<div className="avatar">{getInitials(t.user)}</div>
<div className="tweet-content">
<strong>{t.user}</strong>
<p>{t.content}</p>
</div>
</div>
))}
</div>
</main>
</div>
)
}
47 changes: 47 additions & 0 deletions twitter-clone/src/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Link, useNavigate } from 'react-router-dom';
import { useState, useEffect } from 'react';
import '../styles/login.css';
import { emailRegexp } from '../constants';

export const Login = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [isLoading, setIsLoading] = useState(false)

const navigate = useNavigate();

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true)
if (!!name.trim() && email.trim().match(emailRegexp)) {
localStorage.setItem('user', JSON.stringify({ name, email }));
navigate('/')
}
setIsLoading(false)
};

useEffect(() => {
const storedItem = localStorage.getItem('user');
const userObject = storedItem ? JSON.parse(storedItem) : null;
if (userObject?.name && userObject?.email) {
navigate('/');
}
}, [])

return (

<div className="login-page" >
<form onSubmit={handleSubmit}>
<h3 className="login-header" > Login </h3>
<div>
<input type='text' placeholder = 'Your name' onChange={(e) => setName(e.target.value)}/>
<input type='email' placeholder = 'Your email' onChange={(e) => setEmail(e.target.value)} />
</div>
<button type = 'submit' disabled={isLoading}> Login {isLoading && '...'}</button>


</form>
<p> Don&apos;t have an account ? <Link to="/signup"> Sign up </Link></p >
</div>
)
};
15 changes: 15 additions & 0 deletions twitter-clone/src/components/NotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Link } from 'react-router-dom';
import '../styles/not-found.css';

export const NotFound = () => {
return (
<div className='center'>
<div className='wrapper'>
<p>
Oops, looks like this page doesn&apos;t exist
</p>
<Link to='/'>Back to Home</Link>
</div>
</div>
)
}
49 changes: 49 additions & 0 deletions twitter-clone/src/components/Signup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Link, useNavigate } from 'react-router-dom';
import { useState, useEffect } from 'react';
import '../styles/login.css';
import { emailRegexp } from '../constants';

export const Signup = () => {
const [name, setName] = useState('');
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isLoading, setIsLoading] = useState(false);

const navigate = useNavigate();

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true);
if (!!name.trim() && email.trim().match(emailRegexp) && !!password.trim() && !!username.trim())
{
localStorage.setItem('user', JSON.stringify({ name, email }));
navigate('/');
}
setIsLoading(false);
};

useEffect(() => {
const storedItem = localStorage.getItem('user');
const userObject = storedItem ? JSON.parse(storedItem) : null;
if (userObject?.name && userObject?.email) {
navigate('/');
}
}, [])

return (
<div className= "login-page">
<form onSubmit={handleSubmit}>
<h3 className="login-header"> Sign up </h3>
<div>
<input type='text' placeholder='Your full name' onChange={(e) => setName(e.target.value)}/>
<input type='text' placeholder='Your user name' onChange={(e) => setUsername(e.target.value)}/>
<input type='email' placeholder='Your email' onChange={(e) => setEmail(e.target.value)} />
<input type='password' placeholder='Your password' onChange={(e) => setPassword(e.target.value)}/>
</div>
<button type='submit' disabled={ isLoading }> Signup { isLoading && '...' } </button>
</form>
<p> Already have an account ? <Link to="/login"> Login </Link></p>
</div>
)
}
20 changes: 20 additions & 0 deletions twitter-clone/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const emailRegexp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

export const tweets = [
{
user: 'John Smith',
content: 'What are the pros of object-oriented programming?\n\nPlease, explain as if I\'m 5.'
},
{
user: 'Carla Notarobot',
content: 'Why use a debugger when you can fill your code with hundreds of print() statements?'
},
{
user: 'Amelia Warner',
content: 'Describe your relationship with JavaScript with one word.'
},
{
user: 'John Smith',
content: 'What are the pros of object-oriented programming?\n\nPlease, explain as if I\'m 5.'
}
];
7 changes: 7 additions & 0 deletions twitter-clone/src/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getInitials = (name: string) => {
return name
.split(/[\s-]+/) // Split on spaces or dashes
.filter(Boolean) // Remove any empty strings
.map(word => word[0].toUpperCase()) // Take the first letter and capitalize
.join('');
}
13 changes: 0 additions & 13 deletions twitter-clone/src/index.css

This file was deleted.

12 changes: 6 additions & 6 deletions twitter-clone/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import React from "react";
import ReactDOM from "react-dom";
import "./styles/index.css";
import App from "./components/App";
import reportWebVitals from "./reportWebVitals";

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
document.getElementById("root"),
);

// If you want to start measuring performance in your app, pass a function
Expand Down
4 changes: 2 additions & 2 deletions twitter-clone/src/reportWebVitals.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ReportHandler } from 'web-vitals';
import { ReportHandler } from "web-vitals";

const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
Expand Down
2 changes: 1 addition & 1 deletion twitter-clone/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
// 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';
import "@testing-library/jest-dom";
File renamed without changes.
Loading