Skip to content

Commit

Permalink
segundo dia nlw
Browse files Browse the repository at this point in the history
  • Loading branch information
heltonricardo committed Apr 21, 2021
1 parent d6de24a commit 150ad4e
Show file tree
Hide file tree
Showing 15 changed files with 1,561 additions and 31 deletions.
2 changes: 2 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
"start": "next start",
"server": "json-server server.json -w -d 750 -p 3333"
},
"dependencies": {
"date-fns": "^2.21.1",
"next": "10.1.3",
"react": "17.0.2",
"react-dom": "17.0.2"
"react-dom": "17.0.2",
"sass": "^1.32.11"
},
"devDependencies": {
"@types/node": "^14.14.41",
"@types/react": "^17.0.3",
"json-server": "^0.16.3",
"typescript": "^4.2.4"
}
}
5 changes: 0 additions & 5 deletions pages/_app.js

This file was deleted.

5 changes: 0 additions & 5 deletions pages/index.js

This file was deleted.

20 changes: 20 additions & 0 deletions src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import format from 'date-fns/format';
import ptBR from 'date-fns/locale/pt-BR';

import styles from './styles.module.scss'

export function Header() {
const currentDate = format(new Date(), 'EEEEEE, d MMMM', {
locale: ptBR,
});

return (
<header className={styles.headerContainer}>
<img src="logo.svg" alt="Podcastr" />

<p>O melhor para você ouvir, sempre</p>

<span>{currentDate}</span>
</header>
);
}
22 changes: 22 additions & 0 deletions src/components/Header/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.headerContainer {
background: var(--white);
height: 6.5rem;

display: flex;
align-items: center;

padding: 2rem 4rem;

border-bottom: 1px solid var(--gray-100);

p {
margin-left: 2rem;
padding: 0.25rem 0 0.25rem 2rem;
border-left: 1px solid var(--gray-100);
}

span {
margin-left: auto;
text-transform: capitalize;
}
}
44 changes: 44 additions & 0 deletions src/components/Player/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import styles from "./styles.module.scss";

export function Player() {
return (
<div className={styles.playerContainer}>
<header>
<img src="/playing.svg" alt="Tocando agora" />
<strong>Tocando agora</strong>
</header>

<div className={styles.emptyPlayer}>
<strong>Selecione um podcast para ouvir</strong>
</div>

<footer className={styles.empty}>
<div className={styles.progress}>
<span>00:00</span>
<div className={styles.slider}>
<div className={styles.emptySlider} />
</div>
<span>00:00</span>
</div>

<div className={styles.buttons}>
<button type="button">
<img src="/shuffle.svg" alt="Modo aleatório" />
</button>
<button type="button">
<img src="/play-previous.svg" alt="Anterior" />
</button>
<button type="button" className={styles.playButton}>
<img src="/play.svg" alt="Tocar" />
</button>
<button type="button">
<img src="/play-next.svg" alt="Próxima" />
</button>
<button type="button">
<img src="/repeat.svg" alt="Repetir" />
</button>
</div>
</footer>
</div>
);
}
95 changes: 95 additions & 0 deletions src/components/Player/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
.playerContainer {
padding: 3rem 4rem;
width: 26.5rem;
height: 100vh;

background: var(--purple-500);
color: var(--white);

display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;

header {
display: flex;
align-items: center;
gap: 1rem;
}

strong {
font-family: Lexend, sans-serif;
font-weight: 600;
}

footer {
align-self: stretch;
&.empty {
opacity: 0.5;
}
}
}

.emptyPlayer {
width: 100%;
height: 20rem;
border: 1.5px dashed var(--purple-300);
border-radius: 1.5rem;
background: linear-gradient(
143.8deg,
rgba(145, 100, 250, 0.8) 0%,
rgba(0, 0, 0, 0) 100%
);

padding: 4rem;
text-align: center;

display: flex;
align-items: center;
justify-content: center;
}

.progress {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;

span {
display: inline-block;
width: 4rem;
text-align: center;
}

.slider {
flex: 1;

.emptySlider {
width: 100%;
height: 4px;
background: var(--purple-300);
border-radius: 2px;
}
}
}

.buttons {
display: flex;
align-items: center;
justify-content: center;
margin-top: 2.5rem;
gap: 1.5rem;

button {
background: transparent;
border: 0;
font-size: 0;

&.playButton {
width: 4rem;
height: 4rem;
border-radius: 1rem;
background: var(--purple-400);
}
}
}
34 changes: 34 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import "../styles/global.scss";

import { Header } from "../components/Header";
import { Player } from "../components/Player";

import styles from "../styles/app.module.scss";

function MyApp({ Component, pageProps }) {
return (
<div className={styles.wrapper}>
<main>
<Header />
<Component {...pageProps} />
</main>
<Player />
</div>
);
}

export default MyApp;

/* Toda página da aplicação é exibida dentro do App. Todo componente dentro do
* App estará presente em todas as páginas da aplicação
*
* No React, um componente é formado por uma tag e sua respectiva função que
* retorna código HTML.
*
* Como o return permite apenas o retorno de um componente, é necessário
* envolver tudo com uma tag. Porém, essa tag "pai" ficará sozinha dentro de
* "root" no HTML, sendo praticamente inútil. Por isso o React possui a tag em
* branco (<></>). Dessa maneira, o browser monta todo o conteúdo retornado
* pela função diretamente na tag root do HTML. Não utilizamos nesse trecho pois
* foi necessário incluir uma classe para essa tag externa.
*/
23 changes: 23 additions & 0 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Sempre que trocamos de rota, o _app é recarregado. Então usamos o arquivo
* _document para definir o formato do HTML que ficará "por fora" da aplicação.
* Ele é carregado uma única vez.
*/

import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Inter&family=Lexend:wght@500;600&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
82 changes: 82 additions & 0 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Utilizamos nesse projeto um simulador de servidor que devolve JSON.

/* SPA (single page application): a aplicação executa sempre na mesma página,
* alterando apenas os elementos necessários. Para isso usamos useEffect. Essa
* função dispara alguma coisa quando algo mudar. A arrow function será
* executada quando alguem valor dentro das chaves mudar. Para que uma função
* seja executada assim que o componente for exibido em tela (uma única vez),
* basta deixar as chaves sem valores dentro. Essa estratégia não é interessante
* quando se quer fazer a indexação de conteúdos da página. Os crawlers (robôs
* que procuram conteúdo para indexar nas páginas de empresas, exemplo: crawler
* do google, bing etc) não esperam a resposta do servidor com o conteúdo, assim
* entendendem como se a página estivesse vazia.
*
* import { useEffect } from "react";
*
* export default function Home() {
* useEffect(() => {
* fetch("http://localhost:3333/episodes")
* .then((response) => response.json())
* .then((data) => console.log(data));
* }, []);
*
* return <h1>Index</h1>;
* }
*/

/* SSR (server side rendering): uma página é renderizada estaticamente com todos
* os seus js e css. Para isso basta, dentro de qualquer página (pasta pages)
* exportarmos a função getServerSideProps. Porém esssa função é executada TODA
* vez que a home da aplicação é acessada. Se for uma página que sobre pouca
* alteração (exemplo: a cada dua apenas um podcast é liberado), não tem o
* porquê da home ter seus dados acessados na API toda vez (se o site tiver
* um milhão de acessos simultâneos, para cada acesso haverá uma requisição à
* API para o mesmo conteúdo). Isso será resolvido com a próxima abordagem.
*
* export default function Home(props) {
* console.log(props.episodes);
* return <h1>Index</h1>;
* }
*
* export async function getServerSideProps() {
* const response = await fetch("http://localhost:3333/episodes");
* const data = await response.json();
*
* return {
* props: {
* episodes: data,
* },
* };
* }
*/

/* SSG (static site generation): quando uma página é acessada, é gerada uma
* versão estática dela (HTML público). Todos os próximos acessos serão servidos
* com essa mesma página estática até que uma nova página seja gerada
* (definindo um intervalo de tempo em segundos para tal ação atrvés do
* revalidate). No exemplo foi utilizado 60 * 60 * 8 = 8 horas.
*/

export default function Home(props) {
return (
<div>
<h1>Index</h1>
<p>{JSON.stringify(props.episodes)}</p>
</div>
);
}
export async function getStaticProps() {
const response = await fetch("http://localhost:3333/episodes");
const data = await response.json();
return {
props: {
episodes: data,
},
revalidate: 60 * 60 * 8,
};
}

/* O recurso SSG só funciona em produção, então para simulação, precisaremos
* fazer uma build. Para isso, paramos a aplicação e deixamos somente o servidor
* rodando. Comando: yarn build
*/
7 changes: 7 additions & 0 deletions src/styles/app.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.wrapper {
display: flex;

main {
flex: 1;
}
}
Loading

0 comments on commit 150ad4e

Please sign in to comment.