Individual Crypto Page done

This commit is contained in:
2022-02-02 14:19:51 -03:00
parent 29b49a94a8
commit d5d23e7174
10 changed files with 580 additions and 45 deletions

View File

@@ -15,6 +15,7 @@
"react": "^17.0.2",
"react-chartjs-2": "^4.0.1",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "5.0.0",
"styled-components": "^5.3.3",
"web-vitals": "^2.1.4"

View File

@@ -1,42 +1,22 @@
import React, { useState } from 'react'
import styled from 'styled-components'
import Loading from './components/Loading/Loading';
import Loading from './components/CryptoGalleryLoading/CryptoGalleryLoading';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import Crypto from './components/Crypto/Crypto';
function App() {
const Div = styled.div`
const GlobalStyles = styled.div`
/* height: 100%; */
background-image: radial-gradient( circle farthest-corner at 12.3% 19.3%, rgba(85,88,218,1) 0%, rgba(95,209,249,1) 100.2% );
.paths {
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
.path {
&::before {
content: "";
background: red;
}
/* background: #ffffff45; */
background: rgb(255,255,255);
background: linear-gradient(135deg, #ffffff4e 0%, #2e426965 100%);
position: absolute;
}
}`
background-image: radial-gradient( circle farthest-corner at 12.3% 19.3%, rgba(85,88,218,1) 0%, rgba(95,209,249,1) 100.2% );
`
const [loading, setLoading] = useState(true)
@@ -47,14 +27,30 @@ function App() {
document.body.style.overflow = "hidden"
} else {
setTimeout(() => {
document.body.style.overflow = "visible"
document.body.style.overflow = "visible"
}
}, 2000);
} //https://stackoverflow.com/questions/39962757/prevent-scrolling-using-css-on-react-rendered-components
}, [loading])
return (
<Div>
<Loading loading={loading} setLoading={setLoading}/>
</Div>
<GlobalStyles>
<Router>
<Switch>
<Route path="/crypto/:cryptoID">
<Crypto />
</Route>
<Route path="/">
<Loading loading={loading} setLoading={setLoading}/>
</Route>
</Switch>
</Router>
</GlobalStyles>
);
}

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { CustomPath } from './CustomPath.styled';
const Background = ({loading}) => {
@@ -97,6 +98,33 @@ const Background = ({loading}) => {
transform: "rotate(40deg)"
}
]
const BackgroundStyles = styled.div`
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
.path {
&::before {
content: "";
background: red;
}
/* background: #ffffff45; */
background: rgb(255,255,255);
background: linear-gradient(135deg, #ffffff4e 0%, #2e426965 100%);
position: absolute;
}
`
const pathFunction = () => {
@@ -119,7 +147,7 @@ const Background = ({loading}) => {
}, [])
return <>
return <BackgroundStyles>
{
seriesOfPaths.map(serie => (
@@ -152,7 +180,7 @@ const Background = ({loading}) => {
))
}
</>
</BackgroundStyles>
};
export default Background;

View File

@@ -0,0 +1,363 @@
import React, { useState } from 'react';
import moment from 'moment';
import {useParams} from 'react-router-dom'
import { Chart as ChartJS } from 'chart.js/auto'
import { Chart, Line } from 'react-chartjs-2' //WTF https://stackoverflow.com/questions/67727603/error-category-is-not-a-registered-scale
import { Grid, Button, capitalize } from '@mui/material';
import styled from 'styled-components';
import CryptoPricesModule from './CryptoPricesModule';
import CryptoButtonModule from './CryptoButtonModule';
import { Box } from '@mui/system';
import Loading from './Loading';
const Crypto = () => {
const CryptoStyles = styled(Grid)`
height: 100vh;
header {
display: flex;
align-items: center;
img {
margin: 1vw;
}
h1 {
font-family: 'Raleway', 'Arial';
color: #fff;
font-size: 2.5rem;
user-select: none;
}
}
.line {
margin-right: 5vw;
margin-top: 3vh;
}
@media (max-width: 900px) {
height: 100%;
.line {
margin: 1vw;
}
}
`
const StylesCryptoPricesModule = styled.div`
.card {
margin: 1vw;
background-color: #ffffff44;
}
`
const StylesCryptoButtonModule = styled(Button)`
background-color: #555555 !important;
`
const BackToTheGalleryStyles = styled(Button)`
width: 94%;
box-sizing: border-box;
/* padding: 0 !important; */
background: #4CAF50 !important;
svg {
width: 30px;
}
@media(max-width: 900px) {
width: 98%;
}
`
const plugin = {
id: 'custom_canvas_background_color',
beforeDraw: (chart) => {
const ctx = chart.canvas.getContext('2d');
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
}
const cryptoID = useParams().cryptoID
const [cryptoData, setCryptoData] = useState(false)
const [cryptoPrices, setCryptoPrices] = useState(false)
const [dates, setDates] = useState(false)
const [loading, setLoading] = useState(true)
const [loadingURL, setLoadingURL] = useState('https://i.ibb.co/Dwygw0t/Logo-reduced.png')
const [contentLoaded, setContentLoaded] = useState(false)
const [lineDatesInterval, setLineDatesInterval] = useState('week')
const getCryptoData = async () => {
try {
let today = moment().format('l').split('/')
today = `${today[1]}-${today[0]}-${today[2]}`
const requestData = await fetch(`https://api.coingecko.com/api/v3/coins/${cryptoID}/history?date=${today}`)
const data = await requestData.json()
return data
} catch (error) {
console.log(error);
}
}
const getCryptoPrice = async (days) => {
if (typeof(days) === 'number') {
days -= 1
}
console.log(days)
try {
const requestData = await fetch(`https://api.coingecko.com/api/v3/coins/${cryptoID}/market_chart?vs_currency=usd&days=${days}&interval=daily`)
const data = await requestData.json()
return data.prices.map(arr => {
return arr[1]
})
} catch (error) {
console.log(error);
}
}
const getDates = (days) => {
let result = []
for (let i = 0; i < days; i++) {
const day = moment().subtract(i, 'days').format('DD-MM-YY')
result.push(day)
}
return result.reverse()
}
const effectHandler = async () => {
const data = await getCryptoData()
console.log(data);
const cryptoPricesPrevious = {}
cryptoPricesPrevious.week = await getCryptoPrice(7)
cryptoPricesPrevious.month = await getCryptoPrice(30)
cryptoPricesPrevious.threeMonths = await getCryptoPrice(90)
cryptoPricesPrevious.year = await getCryptoPrice(365)
setCryptoPrices(cryptoPricesPrevious)
const datesPrevious = {}
datesPrevious.week = getDates(7)
datesPrevious.month = getDates(30)
datesPrevious.threeMonths = getDates(90)
datesPrevious.year = getDates(365)
setDates(datesPrevious)
console.log(dates);
const maxValue = Math.max(...cryptoPricesPrevious.year)
const minValue = Math.min(...cryptoPricesPrevious.year)
data.maxValue = {
price: maxValue,
date: datesPrevious.year[cryptoPricesPrevious.year.indexOf(maxValue)]
}
data.minValue = {
price: minValue,
date: datesPrevious.year[cryptoPricesPrevious.year.indexOf(minValue)]
}
setCryptoData(data)
}
React.useEffect(() => {
effectHandler()
}, [])
React.useEffect(() => {
if (cryptoData) {
setLoadingURL(cryptoData.image.small)
}
if (cryptoData && cryptoPrices && dates) {
setLoading(false)
}
}, [cryptoData, cryptoPrices, dates])
React.useEffect(() => {
if (loading) {
document.body.style.overflow = "hidden"
} else {
setTimeout(() => {
document.body.style.overflow = "visible"
setContentLoaded(true)
}, 2000);
} //https://stackoverflow.com/questions/39962757/prevent-scrolling-using-css-on-react-rendered-components
}, [loading])
return (
<>
{
!contentLoaded ?
<Loading loading={loading} loadingURL={loadingURL}/>
: null
}
{
loading ? null :
<CryptoStyles container columnSpacing={2}>
<Grid item md={4} sm={12} xs={12}>
<header>
<img src={cryptoData.image.small} alt="crypto" />
<h1>{cryptoData.name}</h1>
</header>
<CryptoPricesModule
text="Highest value (Last year)"
price={cryptoData.maxValue.price}
date={cryptoData.maxValue.date}
Styles={StylesCryptoPricesModule}
/>
<CryptoPricesModule
text="Lowest value (Last year)"
price={cryptoData.minValue.price}
date={cryptoData.minValue.date}
Styles={StylesCryptoPricesModule}
/>
<BackToTheGalleryStyles
variant="contained"
onClick={() => window.location = '../../'}
sx={{
// width: "100%",
margin: "0px 0px 0px 1vw"
}}
>
<svg xmlns="http://www.w3.org/2000/svg" className="icon icon-tabler icon-tabler-arrow-narrow-left" width="44" height="44" viewBox="0 0 24 24" strokeWidth="1.5" stroke="#ffffff" fill="none" strokeLinecap="round" strokeLinejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="5" y1="12" x2="19" y2="12" />
<line x1="5" y1="12" x2="9" y2="16" />
<line x1="5" y1="12" x2="9" y2="8" />
</svg>
Back to the gallery
</BackToTheGalleryStyles>
</Grid>
<Grid item md={8} sm={12} xs={12}>
<Line
data={{
labels: dates[lineDatesInterval],
label: false,
datasets: [
{
data: cryptoPrices[lineDatesInterval],
borderColor: "#fff"
}
]
}}
options={{
responsive: true,
maintainAspectRatio: true,
aspectRatio: 1.8,
scales: {
x: {
grid: {
color: 'rgba(253, 91, 91, 0)',
borderColor: 'rgba(187, 187, 187, 0)',
opacity: 0
},
ticks: {
color: "#fff"
}
},
y: {
grid: {
color: 'rgba(253, 91, 91, 0)',
borderColor: 'rgba(187, 187, 187, 0)',
opacity: 0
},
ticks: {
color: "#fff"
}
}
},
plugins: {
plugin,
legend: {
display: false, // https://stackoverflow.com/a/67055974
labels: {
fontColor: '#666'
}
}
},
}}
className='line'
/>
<Box
sx={{display: 'flex', justifyContent: "space-around", padding: {md: "1.5vh 13vw", xs: "1vh 13vw 5vh 13vw"}}}
>
{
[
{text: "Week", propierty: "week"},
{text: "Month", propierty: "month"},
{text: "Three months", propierty: "threeMonths"},
{text: "Year", propierty: "year"}
].map((obj) => (
<CryptoButtonModule
Styles={StylesCryptoButtonModule}
text={obj.text}
onClickFunction={setLineDatesInterval}
value={obj.propierty}
key={obj.text}
/>
))
}
</Box>
</Grid>
</CryptoStyles>
}
</>
)
};
export default Crypto;

View File

@@ -0,0 +1,24 @@
import React from 'react';
const CryptoButtonModule = ({text, onClickFunction, value, Styles}) => {
return (
<Styles
onClick={(e) => {
e.preventDefault()
onClickFunction(value)
setTimeout(() => {
window.scrollTo(0,document.body.scrollHeight); //https://stackoverflow.com/questions/11715646/scroll-automatically-to-the-bottom-of-the-page
}, 50)
}}
variant="contained"
>
{text}
</Styles>
)
};
export default CryptoButtonModule;

View File

@@ -0,0 +1,39 @@
import { Card, CardContent, List, ListItem, ListItemText, Typography } from '@mui/material';
import React from 'react';
const CryptoPricesModule = ({text, price, date, Styles}) => {
return (
<Styles>
<Card className="card">
<CardContent>
<Typography
variant="h5"
fontWeight="bold"
fontFamily="Raleway"
color="#fff"
>
{text}
</Typography>
<List>
<ListItem>
<ListItemText
primary={`USD ${Math.round(price * 100) / 100}`} //https://stackoverflow.com/a/11832950
primaryTypographyProps={{
color: '#eee'
}}
secondary={date}
secondaryTypographyProps={{
color: '#ddd'
}}
/>
</ListItem>
</List>
</CardContent>
</Card>
</Styles>
)
};
export default CryptoPricesModule;

View File

@@ -0,0 +1,74 @@
import styled from 'styled-components';
import React, {useState} from 'react';
const Loading = ({loading, loadingURL}) => {
const LoadingStyles = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000;
background-image: radial-gradient( circle farthest-corner at 10% 20%, rgba(90,92,106,1) 0%, rgba(32,45,58,1) 81.3% );
img {
width: 108px;
margin-bottom: 3vh;
animation: imgRotate 2s linear infinite;
}
h2 {
color: #fff;
font-family: "Raleway"
}
@keyframes imgRotate{
0% {
transform: rotate(0deg)
}
100% {
transform: rotate(360deg)
}
}
&.hidden {
animation: hiddeLoading 2s ease-in-out forwards;
}
@keyframes hiddeLoading {
50%{
transform: translate(0, 0%)
}
100% {
transform: translate(0, -100%)
}
}
`
return (
<LoadingStyles className={loading ? null : 'hidden'}>
<img src={loadingURL} alt="loading" />
<h2>Loading</h2>
</LoadingStyles>
)
};
export default Loading;

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Grid } from '@mui/material';
import styled from 'styled-components';
import Crypto from './Crypto';
import Crypto from './CryptoGalleryItem';
import moment from 'moment';

View File

@@ -7,8 +7,12 @@ import { Chart, Line } from 'react-chartjs-2' //WTF https://stackover
import Cookies from 'js-cookie';
import { borderRadius } from '@mui/system';
import { withRouter, useHistory } from 'react-router-dom';
const Crypto = ({CryptoStyles, data, cryptoPrices, dates, index, cryptoListLength, setLoading}) => {
const history = useHistory()
const plugin = {
id: 'custom_canvas_background_color',
beforeDraw: (chart) => {
@@ -40,7 +44,13 @@ const Crypto = ({CryptoStyles, data, cryptoPrices, dates, index, cryptoListLengt
return (
<>
<CryptoStyles item md={4} sm={6} xs={12}>
<CryptoStyles
item
lg={4}
md={6}
sm={12}
onClick={() => history.push(`/crypto/${data.id}`)}
>
<Card className="card">
<CardContent
className="container"
@@ -137,4 +147,4 @@ const Crypto = ({CryptoStyles, data, cryptoPrices, dates, index, cryptoListLengt
)
};
export default Crypto;
export default withRouter(Crypto);

View File

@@ -1,7 +1,7 @@
import {React, useState} from 'react';
import styled from 'styled-components';
import Background from '../Background/Background';
import Cryptos from '../Cryptos/Cryptos';
import Cryptos from '../CryptoGallery/CryptoGallery';
import Header from '../Header/Header';
const Loading = ({loading, setLoading}) => {
@@ -53,11 +53,11 @@ const Loading = ({loading, setLoading}) => {
&.hidden {
animation: hiddeLoading 1s ease-in-out forwards;
animation: hiddeLoading 2s ease-in-out forwards;
}
@keyframes hiddeLoading {
0%{
50%{
transform: translate(0, 0%)
}
100% {