├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── src ├── routes.jsx │ ├── Home.jsx │ ├── Account.jsx │ ├── Signin.jsx │ ├── Signup.jsx │ └── CoinPage.jsx ├── index.js ├── index.css ├── components │ ├── ThemeToggle.jsx │ ├── Trending.jsx │ ├── TrendingItem.jsx │ ├── CoinSearch.jsx │ ├── CoinItem.jsx │ ├── Footer.jsx │ ├── SavedCoin.jsx │ └── Navbar.jsx ├── firebase.js ├── context │ ├── AuthContext.jsx │ └── ThemeContext.jsx └── App.js ├── tailwind.config.js ├── README.md └── package.json /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tareq-Khalil/crypto-react-emyyrbpdq-pegahdp/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tareq-Khalil/crypto-react-emyyrbpdq-pegahdp/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tareq-Khalil/crypto-react-emyyrbpdq-pegahdp/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/routes.jsx/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CoinSearch from '../components/CoinSearch' 3 | import Trending from '../components/Trending' 4 | 5 | function Home({coins}) { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default Home -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | 8 | const root = ReactDOM.createRoot(document.getElementById('root')); 9 | root.render( 10 | 11 | 12 | 13 | ); 14 | 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 3 | darkMode: 'class', 4 | theme: { 5 | extend: { 6 | backgroundColor: { 7 | primary: 'var(--color-bg-primary)', 8 | secondary: 'var(--color-bg-secondary)', 9 | button: 'var(--color-bg-button)', 10 | }, 11 | textColor: { 12 | accent: 'var(--color-text-accent)', 13 | primary: 'var(--color-text-primary)', 14 | secondary: 'var(--color-text-secondary)', 15 | btnText: 'var(--color-bg-secondary)', 16 | }, 17 | borderColor: { 18 | primary: 'var(--color-bg-primary)', 19 | secondary: 'var(--color-bg-secondary)', 20 | input: 'var(--color-bg-input)', 21 | accent: 'var(--color-text-accent)', 22 | }, 23 | }, 24 | }, 25 | plugins: [], 26 | }; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | @apply bg-primary text-primary 7 | } 8 | 9 | .rounded-div { 10 | @apply border border-secondary rounded-2xl shadow-md bg-primary px-2 max-w-[1140px] w-full mx-auto 11 | } 12 | 13 | .dark { 14 | --color-bg-primary: #2d3748; 15 | --color-bg-primary: #2d3748; 16 | --color-bg-secondary: #283141; 17 | --color-text-primary: #dbdbdb; 18 | --color-text-secondary: #e2e8f0; 19 | --color-text-accent: #81e6d9; 20 | --color-bg-input: #4a5361; 21 | --color-bg-button: #81e6d9; 22 | } 23 | 24 | .light { 25 | --color-bg-primary: #ffffff; 26 | --color-bg-secondary: #edf2f7; 27 | --color-text-primary: #2d3748; 28 | --color-text-secondary: #4a5568; 29 | --color-text-accent: #2b6cb0; 30 | --color-bg-input: #edf2f7; 31 | --color-bg-button: #2b6cb0; 32 | } -------------------------------------------------------------------------------- /src/components/ThemeToggle.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { HiSun, HiMoon } from 'react-icons/hi'; 3 | import { ThemeContext } from '../context/ThemeContext'; 4 | 5 | const ThemeToggle = () => { 6 | const { theme, setTheme } = useContext(ThemeContext); 7 | 8 | return ( 9 |
10 | {theme === 'dark' ? ( 11 |
setTheme(theme === 'dark' ? 'light' : 'dark')}> 12 | Light Mode 13 |
14 | ) : ( 15 |
setTheme(theme === 'dark' ? 'light' : 'dark')}> 16 | Dark Mode 17 |
18 | )} 19 |
20 | ); 21 | }; 22 | 23 | export default ThemeToggle; -------------------------------------------------------------------------------- /src/components/Trending.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import axios from "axios"; 3 | import TrendingItem from "./TrendingItem"; 4 | 5 | function Trending() { 6 | const [trending, setTrending] = useState([]); 7 | const url = "https://api.coingecko.com/api/v3/search/trending"; 8 | 9 | const getResponse = async () => { 10 | const response = await axios.get(url); 11 | setTrending(response.data.coins); 12 | }; 13 | useEffect(() => { 14 | getResponse(); 15 | }, []); 16 | 17 | return ( 18 |
19 |

Trending

20 |
21 | {trending.map((coin, idx) => ( 22 | 23 | ))} 24 |
25 |
26 | ); 27 | } 28 | 29 | export default Trending; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | -------------------------------------------------------------------------------- /src/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import { getAuth } from "firebase/auth"; 4 | import { getFirestore } from "firebase/firestore"; 5 | 6 | 7 | // TODO: Add SDKs for Firebase products that you want to use 8 | // https://firebase.google.com/docs/web/setup#available-libraries 9 | 10 | // Your web app's Firebase configuration 11 | const firebaseConfig = { 12 | apiKey: process.env.REACT_APP_FIREBASE_API_KEY, 13 | authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, 14 | projectId:process.env.REACT_APP_FIREBASE_PROJECT_ID, 15 | storageBucket:process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, 16 | messagingSenderId:process.env.REACT_APP_FIREBASE_MESSAGING_SENDER, 17 | appId: process.env.REACT_APP_FIREBASE_APP_ID, 18 | }; 19 | 20 | // Initialize Firebase 21 | const app = initializeApp(firebaseConfig); 22 | 23 | export const auth = getAuth(app); 24 | export const db = getFirestore(app); 25 | 26 | export default app 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/components/TrendingItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function TrendingItem({coin}) { 4 | return ( 5 | 6 |
9 |
10 |
11 | / 12 |
13 |

{coin.item.name}

14 |

{coin.item.symbol}

15 |
16 |
17 |
18 | / 23 |

{coin.item.price_btc.toFixed(7)}

24 |
25 |
26 |
27 | 28 | ); 29 | } 30 | 31 | export default TrendingItem; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.2.2", 10 | "dompurify": "^2.4.1", 11 | "firebase": "^9.15.0", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-icons": "^4.7.1", 15 | "react-router-dom": "^6.6.1", 16 | "react-scripts": "5.0.1", 17 | "react-sparklines": "^1.7.0", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "tailwindcss": "^3.2.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/context/AuthContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useState, useEffect } from 'react'; 2 | import { auth, db } from '../firebase'; 3 | import { 4 | createUserWithEmailAndPassword, 5 | signInWithEmailAndPassword, 6 | signOut, 7 | onAuthStateChanged, 8 | } from 'firebase/auth'; 9 | 10 | import { doc, setDoc } from 'firebase/firestore'; 11 | 12 | const UserContext = createContext(); 13 | 14 | export const AuthContextProvider = ({ children }) => { 15 | const [user, setUser] = useState({}); 16 | 17 | const signUp = (email, password) => { 18 | createUserWithEmailAndPassword(auth, email, password); 19 | return setDoc(doc(db, 'users', email), { 20 | watchList: [], 21 | }); 22 | }; 23 | const signIn = (email, password) => { 24 | return signInWithEmailAndPassword(auth, email, password); 25 | }; 26 | 27 | const logout = () => { 28 | return signOut(auth); 29 | }; 30 | 31 | useEffect(() => { 32 | const unsubscribe = onAuthStateChanged(auth, (currentUser) => { 33 | setUser(currentUser); 34 | }); 35 | return () => { 36 | unsubscribe(); 37 | }; 38 | }, []); 39 | 40 | return ( 41 | 42 | {children} 43 | 44 | ); 45 | }; 46 | 47 | export const UserAuth = () => { 48 | return useContext(UserContext); 49 | }; -------------------------------------------------------------------------------- /src/context/ThemeContext.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect, createContext} from 'react' 2 | 3 | const getInitialTheme = () => { 4 | if (typeof window !== 'undefined' && window.localStorage) { 5 | const storedPrefs = window.localStorage.getItem('color-theme') 6 | if (typeof storedPrefs === 'string') { 7 | return storedPrefs 8 | } 9 | 10 | const userMedia = window.matchMedia('(prefers-color-scheme: dark)') 11 | if (userMedia.matches) { 12 | return 'dark' 13 | } 14 | } 15 | return 'light' 16 | } 17 | 18 | export const ThemeContext = createContext() 19 | 20 | export const ThemeProvider = ({initialTheme, children}) => { 21 | const [theme, setTheme] = useState(getInitialTheme) 22 | 23 | const rawSetTheme = (theme) => { 24 | const root = window.document.documentElement; 25 | const isDark = theme === 'dark' 26 | 27 | root.classList.remove(isDark ? 'light' : 'dark') 28 | root.classList.add(theme) 29 | 30 | localStorage.setItem('color-theme', theme) 31 | } 32 | 33 | if (initialTheme) { 34 | rawSetTheme(initialTheme) 35 | } 36 | 37 | useEffect(()=> { 38 | rawSetTheme(theme) 39 | },[theme]) 40 | 41 | return ( 42 | 43 | {children} 44 | 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /src/routes.jsx/Account.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SavedCoin from '../components/SavedCoin'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | import { Navigate, useNavigate } from 'react-router-dom'; 5 | 6 | const Account = () => { 7 | const { user, logout } = UserAuth(); 8 | const navigate = useNavigate(); 9 | 10 | const handleSignOut = async () => { 11 | try { 12 | await logout(); 13 | navigate('/'); 14 | } catch (e) { 15 | console.log(e.message); 16 | } 17 | }; 18 | 19 | if (user) { 20 | return ( 21 |
22 |
23 |
24 |

Account

25 |
26 |

Welcome, {user?.email}

27 |
28 |
29 |
30 | 36 |
37 |
38 |
39 |
40 |

Watch List

41 | 42 |
43 |
44 |
45 | ); 46 | } else { 47 | return ; 48 | } 49 | }; 50 | 51 | export default Account; -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { Route, Routes } from "react-router-dom"; 2 | import Navbar from "./components/Navbar"; 3 | import { ThemeProvider } from "./context/ThemeContext"; 4 | import CoinPage from "./routes.jsx/CoinPage"; 5 | import Home from "./routes.jsx/Home"; 6 | import Footer from "./components/Footer"; 7 | import Signin from "./routes.jsx/Signin"; 8 | import Signup from "./routes.jsx/Signup"; 9 | import Account from "./routes.jsx/Account"; 10 | import axios from "axios"; 11 | import { useEffect, useState } from "react"; 12 | import { AuthContextProvider } from "./context/AuthContext"; 13 | function App() { 14 | const [coins, setCoins] = useState([]); 15 | const url = 16 | "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=10&page=1&sparkline=true "; 17 | 18 | const getCoinInfo = async () => { 19 | const response = await axios.get(url); 20 | setCoins(response.data); 21 | 22 | }; 23 | 24 | useEffect(() => { 25 | getCoinInfo(); 26 | }, [url]); 27 | 28 | return ( 29 | 30 | 31 | 32 | 33 | } /> 34 | } /> 35 | } /> 36 | } /> 37 | }> 38 | 39 | 40 | 41 |