├── 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 |
34 | Sign Out
35 |
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 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default App;
48 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/components/CoinSearch.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import CoinItem from './CoinItem';
3 |
4 | const CoinSearch = ({ coins }) => {
5 | const [searchText, setSearchText] = useState('');
6 |
7 | return (
8 |
9 |
10 |
Search Crypto
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | #
26 | Coin
27 |
28 | Price
29 | 24h
30 | 24h Volume
31 | Mkt
32 | Last 7 Days
33 |
34 |
35 |
36 | {coins
37 | .filter((value) => {
38 | if (searchText === '') {
39 | return value;
40 | } else if (
41 | value.name.toLowerCase().includes(searchText.toLowerCase())
42 | ) {
43 | return value;
44 | }
45 | })
46 | .map((coin) => (
47 |
48 | ))}
49 |
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default CoinSearch;
--------------------------------------------------------------------------------
/src/routes.jsx/Signin.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { AiFillLock, AiOutlineMail } from 'react-icons/ai';
3 | import { Link, useNavigate } from 'react-router-dom';
4 | import { UserAuth } from '../context/AuthContext';
5 |
6 | const Signin = () => {
7 | const [email, setEmail] = useState('');
8 | const [password, setPassword] = useState('');
9 | const [error, setError] = useState('');
10 | const navigate = useNavigate();
11 | const { signIn } = UserAuth();
12 |
13 | const handleSignIn = async(e) =>{
14 | e.preventDefault()
15 | setError('')
16 | try {
17 | await signIn(email, password)
18 | navigate('/account')
19 | } catch (error) {
20 | setError(error)
21 | console.log(error.message)
22 | }
23 | }
24 |
25 | return (
26 |
27 |
28 |
Sign In
29 |
56 |
57 | Don't have an account?{' '}
58 |
59 | Sign up
60 |
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default Signin;
--------------------------------------------------------------------------------
/src/components/CoinItem.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 | import { AiFillStar, AiOutlineStar } from "react-icons/ai";
3 | import { Sparklines, SparklinesLine } from "react-sparklines";
4 | import { UserAuth } from '../context/AuthContext';
5 | import { db } from '../firebase';
6 | import { arrayUnion, doc, updateDoc } from 'firebase/firestore';
7 | import { useState } from "react";
8 |
9 |
10 | function CoinItem({ coin }) {
11 | const [savedCoin, setSavedCoin] = useState(false)
12 | const {user} = UserAuth()
13 | const coinPath = doc(db, 'users', `${user?.email}`);
14 |
15 | const saveCoin = async () => {
16 | if(user?.email){
17 | setSavedCoin(true);
18 | await updateDoc(coinPath, {
19 | watchList: arrayUnion({
20 | id: coin.id,
21 | name: coin.name,
22 | image: coin.image,
23 | rank: coin.market_cap_rank,
24 | symbol: coin.symbol,
25 | }),
26 | });
27 |
28 | }else {
29 | alert('Please sign in to save a coin to your watch list')
30 | }
31 | }
32 |
33 |
34 |
35 | return (
36 |
37 |
38 | {savedCoin? : }
39 |
40 |
41 | {coin.market_cap_rank}
42 |
43 |
44 |
45 |
46 |
{coin.name}
47 |
48 |
49 |
50 |
51 | {coin.symbol.toUpperCase()}
52 | ${coin.current_price.toLocaleString()}
53 |
54 | {coin.price_change_24h > 0 ? (
55 | {coin.price_change_24h.toFixed(1)}%
56 | ) : (
57 | {coin.price_change_24h.toFixed(1)}%
58 | )}{" "}
59 |
60 |
61 | ${coin.total_volume.toLocaleString()}
62 |
63 |
64 | ${coin.market_cap.toLocaleString()}
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | );
73 | }
74 |
75 | export default CoinItem;
76 |
--------------------------------------------------------------------------------
/src/routes.jsx/Signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { AiFillLock, AiOutlineMail } from 'react-icons/ai';
3 | import { Link, useNavigate } from 'react-router-dom';
4 | import { UserAuth } from '../context/AuthContext';
5 |
6 | const Signup = () => {
7 | const [email, setEmail] = useState('');
8 | const [password, setPassword] = useState('');
9 | const [error, setError] = useState('');
10 | const navigate = useNavigate();
11 | const { signUp } = UserAuth();
12 |
13 | const handleSubmit = async (e) => {
14 | e.preventDefault()
15 | setError('')
16 | try {
17 | await signUp(email,password)
18 | navigate('/account')
19 | } catch (e) {
20 | setError(e.message)
21 | console.log(e.message)
22 | }
23 | }
24 |
25 | return (
26 |
27 |
28 |
Sign Up
29 | {error ?
{error}
: null}
30 |
57 |
58 | Already have an account?{' '}
59 |
60 | Sign in
61 |
62 |
63 |
64 |
65 | );
66 | };
67 |
68 | export default Signup;
--------------------------------------------------------------------------------
/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AiOutlineInstagram } from 'react-icons/ai';
3 | import {
4 | FaFacebookF,
5 | FaGit,
6 | FaGithub,
7 | FaTiktok,
8 | FaTwitter,
9 | } from 'react-icons/fa';
10 | import ThemeToggle from './ThemeToggle';
11 |
12 | const Footer = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
Support
19 |
20 | Help Center
21 | Contact Us
22 | API Status
23 | Documentation
24 |
25 |
26 |
27 |
Info
28 |
29 | About Us
30 | Careers
31 | Invest
32 | Legal
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
Sign up for crypto news
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
Powered by Coin Gecko
61 |
62 | );
63 | };
64 |
65 | export default Footer;
--------------------------------------------------------------------------------
/src/components/SavedCoin.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { Link } from "react-router-dom";
3 | import { AiOutlineClose } from "react-icons/ai";
4 | import { doc, onSnapshot, updateDoc } from "firebase/firestore";
5 | import { db } from "../firebase";
6 | import { UserAuth } from "../context/AuthContext";
7 |
8 | function SavedCoin() {
9 | const [coins, setCoins] = useState([]);
10 |
11 | const { user } = UserAuth();
12 | useEffect(() => {
13 | onSnapshot(doc(db, "users", `${user?.email}`), (doc) => {
14 | setCoins(doc.data()?.watchList);
15 | });
16 | }, [user?.email]);
17 |
18 | const coinPath = doc(db, "users", `${user?.email}`);
19 | const deleteCoin = async (passedid) => {
20 | try {
21 | const result = coins.filter((item) => item.id !== passedid);
22 | await updateDoc(coinPath, {
23 | watchList: result,
24 | });
25 | } catch (e) {
26 | console.log(e.message);
27 | }
28 | };
29 | return (
30 |
31 | {coins?.length === 0 ? (
32 |
33 | You don't have any coins saved. Please save a coin to add it to your
34 | watch list. Click here to search coins.
35 |
36 | ) : (
37 |
38 |
39 |
40 | Rank #
41 | Coin
42 | Remove
43 |
44 |
45 |
46 | {coins?.map((coin) => (
47 |
48 | {coin.rank}
49 |
50 |
51 |
52 |
53 |
54 |
{coin?.name}
55 |
56 | {coin?.symbol}
57 |
58 |
59 |
60 |
61 |
62 |
63 | deleteCoin(coin.id)}
65 | className="cursor-pointer"
66 | />
67 |
68 |
69 | ))}
70 |
71 |
72 | )}
73 |
74 | );
75 | }
76 |
77 | export default SavedCoin;
78 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Link, useNavigate } from 'react-router-dom';
3 | import ThemeToggle from './ThemeToggle';
4 | import { AiOutlineClose, AiOutlineMenu } from 'react-icons/ai';
5 | import { UserAuth } from '../context/AuthContext';
6 |
7 | const Navbar = () => {
8 | const [nav, setNav] = useState(false);
9 | const { user, logout } = UserAuth();
10 | const navigate = useNavigate();
11 |
12 | const handleNav = () => {
13 | setNav(!nav);
14 | };
15 |
16 | const handleSignOut = async () => {
17 | try {
18 | await logout();
19 | navigate('/');
20 | } catch (e) {
21 | console.log(e.message);
22 | }
23 | };
24 |
25 | return (
26 |
27 |
28 |
Cryptobase
29 |
30 |
31 |
32 |
33 |
34 | {user?.email ? (
35 |
36 |
37 | Account
38 |
39 | Sign out
40 |
41 | ) : (
42 |
43 |
44 | Sign In
45 |
46 |
50 | Sign Up
51 |
52 |
53 | )}
54 |
55 | {/* Menu Icon */}
56 |
59 |
60 | {/* Mobile Menu */}
61 |
68 |
69 |
70 | Home
71 |
72 |
73 | Account
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
85 | Sign In
86 |
87 |
88 |
89 |
90 | Sign Up
91 |
92 |
93 |
94 |
95 |
96 | );
97 | };
98 |
99 | export default Navbar;
--------------------------------------------------------------------------------
/src/routes.jsx/CoinPage.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { useParams } from "react-router-dom";
3 | import { Sparklines, SparklinesLine } from "react-sparklines";
4 | import { FaTwitter, FaFacebook, FaReddit, FaGithub } from 'react-icons/fa';
5 | import DOMPurify from 'dompurify'
6 |
7 |
8 | import axios from "axios";
9 |
10 | function CoinPage() {
11 | const [coin, setCoin] = useState({});
12 | const params = useParams()
13 | const url =
14 | `https://api.coingecko.com/api/v3/coins/${params.coinId}?localization=false&sparkline=true`;
15 |
16 | const getCoin = async () => {
17 | const response = await axios.get(url);
18 | setCoin(response.data);
19 | console.log(response);
20 | };
21 |
22 | useEffect(() => {
23 | getCoin();
24 | }, [url]);
25 | return (
26 |
27 |
28 |
29 |
30 |
{coin?.name} price
31 |
({coin.symbol?.toUpperCase()} / USD)
32 |
33 |
34 |
35 |
36 |
37 |
38 | {coin.market_data?.current_price ? (
39 |
${coin.market_data.current_price.usd.toLocaleString()}
40 | ) : null}
41 |
7 Day
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Market Cap
51 | {coin.market_data?.market_cap ? (
52 |
${coin.market_data.market_cap.usd.toLocaleString()}
53 | ) : null}
54 |
55 |
56 |
Volume (24h)
57 | {coin.market_data?.market_cap ? (
58 |
${coin.market_data.total_volume.usd.toLocaleString()}
59 | ) : null}
60 |
61 |
62 |
63 |
64 |
65 |
24h High
66 | {coin.market_data?.high_24h ? (
67 |
${coin.market_data.high_24h.usd.toLocaleString()}
68 | ) : null}
69 |
70 |
71 |
24h Low
72 | {coin.market_data?.low_24h ? (
73 |
${coin.market_data.low_24h.usd.toLocaleString()}
74 | ) : null}
75 |
76 |
77 |
78 |
79 |
80 |
Market Stats
81 |
82 |
83 |
Market Rank
84 | {coin.market_cap_rank}
85 |
86 |
87 |
Hashing Algorithm
88 | {coin.hashing_algorithm ?
{coin.hashing_algorithm}
: null}
89 |
90 |
91 |
Trust Score
92 | {coin.tickers ?
{coin.liquidity_score.toFixed(2)}
: null}
93 |
94 |
95 |
96 |
97 |
98 |
Price Change (24h)
99 | {coin.market_data ? (
100 |
101 | {coin.market_data.price_change_percentage_24h.toFixed(2)}%
102 |
103 | ) : null}
104 |
105 |
106 |
Price Change (7d)
107 | {coin.market_data ? (
108 |
{coin.market_data.price_change_percentage_7d.toFixed(2)}%
109 | ) : null}
110 |
111 |
112 |
Price Change (14d)
113 | {coin.market_data ? (
114 |
115 | {coin.market_data.price_change_percentage_14d.toFixed(2)}%
116 |
117 | ) : null}
118 |
119 |
120 |
121 |
122 |
Price Change (30d)
123 | {coin.market_data ? (
124 |
125 | {coin.market_data.price_change_percentage_30d.toFixed(2)}%
126 |
127 | ) : null}
128 |
129 |
130 |
Price Change (60d)
131 | {coin.market_data ? (
132 |
133 | {coin.market_data.price_change_percentage_60d.toFixed(2)}%
134 |
135 | ) : null}
136 |
137 |
138 |
Price Change (1y)
139 | {coin.market_data ? (
140 |
{coin.market_data.price_change_percentage_1y.toFixed(2)}%
141 | ) : null}
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | {/* Description */}
154 |
155 |
About {coin.name}
156 |
157 |
158 |
159 |
160 | );
161 | }
162 |
163 | export default CoinPage;
164 |
--------------------------------------------------------------------------------