Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementa Paginação #398

Merged
merged 5 commits into from
May 28, 2022
Merged

Implementa Paginação #398

merged 5 commits into from
May 28, 2022

Conversation

filipedeschamps
Copy link
Owner

@filipedeschamps filipedeschamps commented May 27, 2022

Atenção: Este PR é um Work In Progress.


Destaques do primeiro commit com foco no backend d86c421

  1. Foco no endpoint /api/v1/contents
  2. Novos query strings disponíveis: page e per_page
  3. Se você chamar o endpoint sem nada, os defaults serão utilizados: page=1 e per_page=30
  4. Exemplo com outros valores: /api/v1/contents?page=2&per_page=15
  5. Refiz a validação do content.findAll() para validar o options somente usando o validator.js

Como os valores padrão são aplicados no model content, isso automaticamente filtrou os últimos 30 conteúdos publicados no ambiente de Homologação: https://tabnews-git-pagination-tabnews.vercel.app/

E se alguém quiser testar a API: https://tabnews-git-pagination-tabnews.vercel.app/api/v1/contents?page=1&per_page=2


Closes #339
Closes #341
Closes #343

@vercel
Copy link

vercel bot commented May 27, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
tabnews ✅ Ready (Inspect) Visit Preview May 28, 2022 at 3:03PM (UTC)

@filipedeschamps
Copy link
Owner Author

Destaques do segundo commit, ainda com foco no backend c312553

  1. Foco no endpoint /api/v1/contents e /api/v1/contents/[username] para retornarem pelo Header da resposta todas as informações de paginação para que seja possível construir uma navegação em qualquer tipo de client.
  2. Novos cabeçalhos disponíveis: X-Pagination-Total-Rows e Link
  3. O cabeçalho X-Pagination-Total-Rows é simples e retorna o número total de itens no banco de dados para aquela busca. Por exemplo, se você estiver usando o fetch para buscar esse valor, basta executar response.headers.get('X-Pagination-Total-Rows'); e que irá retornar o valor total de linhas para aquela query. Se o banco possui 60 linhas para aquela query, aquele código ali em cima vai retornar a string '60'.
  4. Cabeçalho Link segue a RFC 5988, a mesma utilizada pelo GitHub, e ela devolve as URLs absolutas para que o seu próprio código consiga sozinho navegar pela paginação e sem você precisar montar nenhuma URL manualmente, basta utilizar o que vem no cabeçalho.
  5. E no cabeçalho Link é disponibilizado todos os tipos de página que você precisa:
    1. first - URL da primeira página
    2. prev - URL da página anterior
    3. next - URL da página seguinte
    4. last - URL da última página
  6. Esse cabeçalho pode ser adquirido da mesma forma que antes response.headers.get('Link') e caso você não queira fazer o parsing dele na mão, há bibliotecas para isso como a parse-link-header para Node.js e que irá devolver um objeto como este:
       // Localhost com 60 conteúdos e paginação default (page=1 e per_page=30)
       // GET http://localhost:3000/api/v1/contents
       
       {
         first: {
           page: '1',
           per_page: '30',
           rel: 'first',
           url: 'http://localhost:3000/api/v1/contents?page=1&per_page=30',
         },
         next: {
           page: '2',
           per_page: '30',
           rel: 'next',
           url: 'http://localhost:3000/api/v1/contents?page=2&per_page=30',
         },
         last: {
           page: '2',
           per_page: '30',
           rel: 'last',
           url: 'http://localhost:3000/api/v1/contents?page=2&per_page=30',
         },
       }
  7. Note que no exemplo acima não existe a chave prev, pois de fato, na página 1 não há página anterior. Da mesma forma, a última página não retornaria next, pois não há página seguinte. E isso é ótimo caso você queira mostrar ou não botões como "próxima página" ou "página anterior".
  8. E caso queira, você pode computar e interpolar todas as páginas intermediárias utilizando as informações acima.
  9. Tudo isso está testado, inclusive um dos testes testa a navegação automática para a próxima página usando next do Link.

@filipedeschamps
Copy link
Owner Author

Oh não, as getStaticProps() no Next.js não devolvem as query strings pelo context. @omariosouto sem saída nessa né? Vou ter que fazer a paginação das Páginas pelo path mesmo.

@omariosouto
Copy link
Contributor

omariosouto commented May 28, 2022

@filipedeschamps como tu quer que seja o link das páginas no navegador 🤔? Daria pra vc gerar as páginas com o incremental static generation

Fiquei com dúvida

@omariosouto
Copy link
Contributor

@filipedeschamps realmente via query string com getStaticProps tu não vai ter acesso, achei a issue aqui descrevendo a feature: #343

@filipedeschamps filipedeschamps changed the title [WIP] Implementa Paginação Implementa Paginação May 28, 2022
@filipedeschamps
Copy link
Owner Author

filipedeschamps commented May 28, 2022

@omariosouto isso, acabou ficando como descrito nesse comentário


Destaques do terceiro commit, agora focado no frontend e que finaliza esse PR b9598f7 (mas ainda tem um teste que está quebrando no CI que não consigo reproduzir localmente).

  1. A Home do TabNews e Página do Usuário agora contam com paginação.
  2. Por limitações do Next.js, não foi possível usar query strings e a página é acessada pelo path mesmo.
  3. Na Home, as páginas ficarão assim: /pagina/1
  4. Na Página do Usuário, ficará assim: /filipedeschamps/pagina/1
  5. Já está bloqueado no model user de alguém criar conta com o username pagina. Não está bloqueado de alguém criar um conteúdo com o slug pagina, mas se criar, ele não vai ser acessível.
  6. Antes, toda vez que um usuário entrava na Home, ele fazia um hit extra contra a API para pegar os dados mais atualizados. Isso acontecia pois um usuário sempre poderia abrir uma página atualizada por conta do revalidate. A necessidade desse primeiro hit foi substituído pelo response.unstable_revalidate do Next.js e agora a cada nova publicação, ele atualiza a primeira página da Home e a primeira Página do Usuário. O revalidate continua a cada 1 segundo para garantir que outras atualizações paralelas continuem fazendo a página ser atualizada, como por exemplo, um PATCH em um content e outros lugares onde ainda não utilizamos o response.unstable_revalidate.
  7. Tudo está usando o validate.js.
  8. Todo o resultado desse PR pode ser conferido na URL de Homologação: https://tabnews-git-pagination-tabnews.vercel.app/

Agora se houver uma próxima página, aparece isso aqui no fim da lista:

image

O que não sei consertar, mas que deverão ficar para outros PRs:

  1. Largura da coluna: quando você está numa página muito longe, o offset dos números começarão a quebrar a coluna. Então deveria ter uma espécie de largura mínima, mas que aumenta quando necessário.
  2. O texto que mostra a quanto tempo o conteúdo foi postado continua "flickando" no primeiro load.

@omariosouto
Copy link
Contributor

omariosouto commented May 28, 2022

Sensacionaaal!!! @filipedeschamps

  1. Largura da coluna: quando você está numa página muito longe, o offset dos números começarão a quebrar a coluna. Então deveria ter uma espécie de largura mínima, mas que aumenta quando necessário.

R: Acho que esse aqui eu tenho uma solução, vamos mergear esse PR e eu separo um timebox aqui pra atuar nisso hoje ainda haha

  1. O texto que mostra a quanto tempo o conteúdo foi postado continua "flickando" no primeiro load.

R: Esse eu to testando agora...

@filipedeschamps
Copy link
Owner Author

O CI ta consistentemente quebrando no teste abaixo, e eu não consigo por nada reproduzir em localhost:

FAIL tests/integration/api/v1/contents/post.test.js (25.231 s)
  ● POST /api/v1/contents › Default user › Content with "owner_id" pointing to another user
    thrown: "Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
      164 |     });
      165 |
    > 166 |     test('Content with "owner_id" pointing to another user', async () => {
          |     ^
      167 |       const firstUser = await orchestrator.createUser();
      168 |       await orchestrator.activateUser(firstUser);
      169 |       const firstUserSessionObject = await orchestrator.createSession(firstUser);
      at test (tests/integration/api/v1/contents/post.test.js:166:5)
      at describe (tests/integration/api/v1/contents/post.test.js:116:3)
      at Object.describe (tests/integration/api/v1/contents/post.test.js:11:1)

@filipedeschamps
Copy link
Owner Author

Algo está acontecendo com a performance do POST no contents:

PASS tests/integration/api/v1/contents/post.test.js (26.886 s)

26 segundos, eita... sendo que os outros estão em milisegundos.

Vou investigar se é reflexo do response.unstable_revalidate 🤝

@filipedeschamps
Copy link
Owner Author

Confirmado, é algum comportamento maluco do response.unstable_revalidate 👍

@filipedeschamps
Copy link
Owner Author

Tudo certo agora 👍

@aprendendofelipe
Copy link
Collaborator

aprendendofelipe commented May 28, 2022

Algum bug por tentar revalidar uma pagina que não existia?

Edit.
Talvez seja isso, pois nos testes estava tentando revalidar a página do usuário que acabou de ser criado, ou seja, uma página que ainda não tinha sido gerada estaticamente

@@ -107,7 +107,6 @@ async function postHandler(request, response) {
const secureOutputValues = authorization.filterOutput(userTryingToCreate, 'read:content', createdContent);

await response.unstable_revalidate(`/`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Como a revalidação ainda está em beta e também por ter sido mantida a revalidação das páginas a cada 1 segundo, talvez seja bom envolver essa parte com um try catch.

@filipedeschamps
Copy link
Owner Author

Talvez seja isso, pois nos testes estava tentando revalidar a página do usuário que acabou de ser criado, ou seja, uma página que ainda não tinha sido gerada estaticamente

Excelente ponto! Revalidar antes de existir. Espero que o método leve isso em conta (pelo menos quando virar stable)

@filipedeschamps
Copy link
Owner Author

@aprendendofelipe feito no commit 2379a92 🤝

@filipedeschamps filipedeschamps merged commit 2c34a1f into main May 28, 2022
@filipedeschamps filipedeschamps deleted the pagination branch May 28, 2022 15:07
@filipedeschamps
Copy link
Owner Author

Mergeeed!!!! Let's gooooo!!!

@omariosouto
Copy link
Contributor

Aeeeeee 🎉🎉🎉🎉🎉

@aprendendofelipe
Copy link
Collaborator

Massa!

@rodrigoKulb
Copy link
Contributor

@filipedeschamps maravilha!! 🚀️

Observando os sites normalmente os próximos resultados ficam na "Próxima >" página e só depois de avançar que tenho a opção de voltar a página Anterior.

Perdi alguma informação sobre essa opção?

@filipedeschamps
Copy link
Owner Author

Show! Ótimos pontos 🤝

  1. Eu acho muito estranho "ir para frente" para ver itens do passado. Eu compreendo melhor o que está acontecendo sabendo que estou indo para o passado, voltando.
  2. Sobre o outro botão para navegar para frente nesse caso, eu só não implementei 😂 mas ta na mão implementar, porque já vem tudo no pagination 🤝

@rodrigoKulb
Copy link
Contributor

@filipedeschamps entendo a lógica de publicações anteriores, porem como a nomenclatura está Página anterior, minha cabeça deu uma travada! Talvez alterar a nomenclatura, segue um exemplo:
Captura de tela em 2022-05-28 13-11-36

@omariosouto
Copy link
Contributor

@rodrigoKulb concordo contigo, eu me perdi forte auhshusa

@aprendendofelipe
Copy link
Collaborator

@rodrigoKulb também curti sua sugestão

@filipedeschamps
Copy link
Owner Author

Mais uma evidência de que vocês estão certos:

https://www.tabnews.com.br/acnahmias/f445c7d0-9698-4361-a7ab-17761919f30d

👍 🤝

E é muito doido que pra mim naquele contexto eu não consigo diferenciar o que é “voltar” e “mais publicações”.

Como podemos deixar claro que avançar na paginação vai fazer mostrar publicações mais antigas?

@rodrigoKulb
Copy link
Contributor

rodrigoKulb commented May 29, 2022

Gosto de utilizar o Google como referencia, porque o publico alvo é praticamente 100% da população, ele utiliza o CSS default do browser então sempre utilizo como referencia, gosto da abordagem simples que utilizam:
Termo: Mais
Outra questão é que sempre imaginamos que o lado direito é o avançar e esquerdo voltar. Mesmo pensando na ideia de artigos anteriores, o usuário quer avançar para os artigos mais antigos. (essa lógica já está na cabeça dos usuários)
Captura de tela em 2022-05-29 12-56-04
Captura de tela em 2022-05-29 13-00-50

Edit: O site referencia para HOME também utiliza o mesmo termo (more)
Captura de tela em 2022-05-29 13-32-42

@vitoropereira
Copy link
Contributor

@rodrigoKulb perfeito, ao utilizar pela primeira vez tive este mesmo pensamento: "Pagina anterior? Mas eu quero ir pra frente..."
Acho interessante a HOME com o "MORE.../MAIS..."

@aprendendofelipe
Copy link
Collaborator

Como podemos deixar claro que avançar na paginação vai fazer mostrar publicações mais antigas?

Será mesmo que é preciso deixar claro que são mais antigas? Talvez seja suficiente deixar claro que existem mais publicações, pois isso irá continuar funcionando independente do método de ordenação.

@filipedeschamps
Copy link
Owner Author

Github mobile 👍

D8EA2B2C-FAD2-441B-92B3-BA0C5EE7DF1C

@aprendendofelipe
Copy link
Collaborator

Excelente ponto! Revalidar antes de existir. Espero que o método leve isso em conta (pelo menos quando virar stable)

Isso resolve o problema de revalidar antes de existir:

vercel/next.js#38136

@filipedeschamps
Copy link
Owner Author

@aprendendofelipe tava pensando essa semana, como seria legal ter um comando para apenas limpar o cache (sem recriar).

@aprendendofelipe
Copy link
Collaborator

@aprendendofelipe tava pensando essa semana, como seria legal ter um comando para apenas limpar o cache (sem recriar).

Fiquei curioso... Qual uso está pensando para esse comando?

Aproveitando para complementar... On-Demand ISR ficou estável na v12.2.0, mas a correção citada acima só entrou na v12.2.1

@filipedeschamps
Copy link
Owner Author

Fiquei curioso... Qual uso está pensando para esse comando?

Eu estava pensando em habilitar o stale-while-revalidate para as 3 primeiras páginas do endpoint /contents e no ato de um POST nessa rota, ao invés de quem está postando ter que aguardar gerar esses 3 caches, limpar o cache instantâneamente e deixar o POST correr rápido... aí cabe a primeira próxima request pagar o custo de gerar o cache.

Não sei se ficou claro, mas em resumo o revalidate fica muito devagar quando precisa revalidar algo custoso, ou várias coisas custosas.

@aprendendofelipe
Copy link
Collaborator

Ficou claro sim... Então seria legal se pudesse expirar manualmente o cache ao invés de limpar. Assim as páginas seriam revalidadas em segundo plano sem ninguém ter que esperar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants