Skip to content

Commit

Permalink
Merge pull request #1747 from filipedeschamps/feat/ui-create-ad
Browse files Browse the repository at this point in the history
Ajusta UI para criar uma publicação patrocinada
  • Loading branch information
aprendendofelipe authored Jul 17, 2024
2 parents d6cc266 + cd45e7a commit 860aba3
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 60 deletions.
11 changes: 9 additions & 2 deletions pages/[username]/[slug]/index.public.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { getStaticPropsRevalidate } from 'next-swr';
import { useEffect, useState } from 'react';

import { Box, Button, Confetti, Content, DefaultLayout, Link, TabCoinButtons, Tooltip } from '@/TabNewsUI';
import { AdBanner, Box, Button, Confetti, Content, DefaultLayout, Link, TabCoinButtons, Tooltip } from '@/TabNewsUI';
import { CommentDiscussionIcon, CommentIcon, FoldIcon, UnfoldIcon } from '@/TabNewsUI/icons';
import { NotFoundError, ValidationError } from 'errors';
import webserver from 'infra/webserver.js';
import ad from 'models/advertisement';
import authorization from 'models/authorization.js';
import content from 'models/content.js';
import removeMarkdown from 'models/remove-markdown.js';
import user from 'models/user.js';
import { useCollapse } from 'pages/interface';

export default function Post({ contentFound, rootContentFound, parentContentFound, contentMetadata }) {
export default function Post({ adFound, contentFound, rootContentFound, parentContentFound, contentMetadata }) {
const [childrenToShow, setChildrenToShow] = useState(108);
const [showConfetti, setShowConfetti] = useState(false);

Expand Down Expand Up @@ -82,6 +83,8 @@ export default function Post({ contentFound, rootContentFound, parentContentFoun
/>
</Box>

{adFound && <AdBanner ad={adFound} sx={{ ml: 5, pl: 1, width: '100%' }} />}

<RenderChildrenTree
key={contentFound.id}
childrenDeepCount={contentFound.children_deep_count}
Expand Down Expand Up @@ -415,8 +418,12 @@ export const getStaticProps = getStaticPropsRevalidate(async (context) => {
secureParentContentFound.body = removeMarkdown(secureParentContentFound.body, { maxLength: 50 });
}

const adsFound = await ad.getRandom(1);
const secureAdValues = authorization.filterOutput(userTryingToGet, 'read:ad:list', adsFound);

return {
props: {
adFound: secureAdValues[0] ?? null,
contentFound: JSON.parse(JSON.stringify(secureContentFound)),
rootContentFound: JSON.parse(JSON.stringify(secureRootContentFound)),
parentContentFound: JSON.parse(JSON.stringify(secureParentContentFound)),
Expand Down
22 changes: 20 additions & 2 deletions pages/faq/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Queremos ter conteúdo de qualidade tanto na publicação principal quanto nos c
{
id: 'qualidade-tabnews',
question: 'Como criar um bom conteúdo no TabNews?',
answer: `A forma como cada pessoa avalia a qualidade de um conteúdo é subjetiva, mas temos algumas recomendações que podem ajudar a criar uma publicação mais relevante:
answer: `A forma como cada pessoa avalia a qualidade de um conteúdo é subjetiva, mas temos algumas recomendações que podem ajudar a criar uma publicação mais relevante:
- **Atenção à gramática e aos erros de digitação:** antes de publicar, confirme se precisa corrigir algum erro gramatical ou de digitação. O uso correto da língua portuguesa ajudará a transmitir a sua mensagem para os leitores.
- **Formate o conteúdo para facilitar a leitura:** o editor de texto do TabNews aceita a sintaxe Markdown, então você pode usá-la para identificar no seu texto títulos e subtítulos, trechos de código, citações, enfatizar trechos específicos, exibir diagramas etc.
Expand All @@ -37,13 +37,31 @@ Queremos ter conteúdo de qualidade tanto na publicação principal quanto nos c
{
id: 'tabcash',
question: 'O que é TabCash?',
answer: `O TabCash é uma moeda digital para recompensar pessoas que estão criando conteúdos com valor concreto e também ajudando a qualificar outros conteúdos. O saldo de TabCash poderá ser utilizado no sistema de Revenue Share, onde você poderá usar espaços de anúncio para compartilhar o que desejar, desde que respeite os [Termos de Uso](/termos-de-uso). Esse sistema ainda não está implementado, mas você pode [acompanhar o progresso no GitHub](https://github.com/filipedeschamps/tabnews.com.br/issues/1490).`,
answer: `O TabCash é uma moeda digital para recompensar pessoas que estão criando conteúdos com valor concreto e também ajudando a qualificar outros conteúdos. O saldo de TabCash pode ser utilizado no sistema de Revenue Share, onde você pode usar espaços de anúncio para compartilhar o que desejar, desde que respeite os [Termos de Uso](/termos-de-uso). Esse sistema está em desenvolvimento e você pode [acompanhar o progresso no GitHub](https://github.com/filipedeschamps/tabnews.com.br/issues/1490).`,
},
{
id: 'ganhar-tabcash',
question: 'Como ganhar TabCash?',
answer: `Para ganhar TabCash, é necessário contribuir com a qualificação de conteúdos de outras pessoas, consumindo 2 TabCoins a cada qualificação realizada e, ao mesmo tempo, ganhando 1 TabCash.`,
},
{
id: 'utilizar-tabcash',
question: 'Como utilizar meu TabCash?',
answer: `O TabCash pode ser utilizado para publicar o que você quiser em espaços de anúncio, desde que respeite os [Termos de Uso](/termos-de-uso).
Atualmente, o único espaço de anúncio disponível é o de [publicações patrocinadas](#publicacao-patrocinada). Para criar esse tipo de anúncio, acesse a página [Publicar novo conteúdo](/publicar) e marque a caixa de seleção "**Criar como publicação patrocinada**". Você precisa ter ao menos **100 TabCash**, que serão consumidos ao criar a publicação patrocinada.`,
},
{
id: 'publicacao-patrocinada',
question: 'Como funciona uma publicação patrocinada?',
answer: `_Esse tipo de anúncio está em desenvolvimento, então está em constante evolução. Você pode acompanhar o que está sendo feito no [issue #1491 do GitHub](https://github.com/filipedeschamps/tabnews.com.br/issues/1491)._
No topo das listas de conteúdos [Relevantes](/) e [Recentes](/recentes/pagina/1), e também nas páginas de publicações e comentários, após o conteúdo principal, uma publicação patrocinada escolhida de forma aleatória é exibida. Caso a publicação tenha um link de "**fonte**", o visitante que clicar no título da publicação será redirecionado para o link. Caso o link seja para um site externo, o domínio será identificado após o título, por exemplo: \`Título da publicação patrocinada (site-externo.com.br)\`.
Para criar uma publicação patrocinada, você investirá **100 TabCash**. A cada dia que passar, **10 TabCash** serão consumidos da publicação patrocinada.
Recomendamos que o título tenha até 70 caracteres para que possa ser exibido sem reticências ao final.`,
},
{
id: 'tabcoin',
question: 'O que é TabCoin?',
Expand Down
50 changes: 50 additions & 0 deletions pages/interface/components/AdBanner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Box, Link, Text, Tooltip } from '@/TabNewsUI';
import { LinkExternalIcon } from '@/TabNewsUI/icons';
import { getDomain, isExternalLink, isTrustedDomain } from 'pages/interface';

export default function AdBanner({ ad, ...props }) {
const link = ad.source_url || `/${ad.owner_username}/${ad.slug}`;
const isAdToExternalLink = isExternalLink(link);
const domain = isAdToExternalLink ? `(${getDomain(link)})` : '';
const title = ad.title.length > 70 ? ad.title.substring(0, 67).trim().concat('...') : ad.title;

return (
<Box {...props} as="aside" sx={{ display: 'grid', ...props.sx }}>
<Box>
<Link
sx={{
overflow: 'auto',
fontWeight: 'semibold',
wordWrap: 'break-word',
':link': {
color: 'success.fg',
},
':visited': {
color: 'success.fg',
},
}}
href={link}
rel={isTrustedDomain(link) ? undefined : 'nofollow'}>
<Text sx={{ wordBreak: 'break-word', marginRight: 1 }}>
{title} {domain}
</Text>
{isAdToExternalLink && <LinkExternalIcon verticalAlign="middle" />}
</Link>
</Box>

<Text sx={{ whiteSpace: 'nowrap', fontSize: 0, color: 'neutral.emphasis' }}>
Contribuindo com{' '}
<Tooltip
aria-label={`Autor: ${ad.owner_username}`}
direction="nw"
sx={{ position: 'absolute', display: 'grid' }}>
<Link
sx={{ overflow: 'hidden', textOverflow: 'ellipsis', color: 'neutral.emphasis', mr: 2 }}
href={`/${ad.owner_username}`}>
{ad.owner_username}
</Link>
</Tooltip>
</Text>
</Box>
);
}
19 changes: 18 additions & 1 deletion pages/interface/components/Content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BranchName,
Button,
ButtonWithLoader,
Checkbox,
Editor,
Flash,
FormControl,
Expand Down Expand Up @@ -270,6 +271,7 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor
title: contentObject?.title || '',
body: contentObject?.body || '',
source_url: contentObject?.source_url || '',
isSponsoredContent: contentObject?.type === 'ad',
});
const [titlePlaceholder, setTitlePlaceholder] = useState('');

Expand Down Expand Up @@ -343,6 +345,7 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor
: `/api/v1/contents`;
const requestBody = {
status: 'published',
type: newData.isSponsoredContent ? 'ad' : 'content',
};

if (title || contentObject?.title) {
Expand Down Expand Up @@ -439,7 +442,8 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor
(event) => {
setErrorObject(undefined);
setNewData((oldData) => {
const newData = { ...oldData, [event.target?.name || 'body']: event.target?.value ?? event };
const value = event.target?.name === 'isSponsoredContent' ? event.target.checked : event.target?.value ?? event;
const newData = { ...oldData, [event.target?.name || 'body']: value };
localStorage.setItem(localStorageKey, JSON.stringify(newData));
return newData;
});
Expand Down Expand Up @@ -557,6 +561,19 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor
</FormControl>
)}

{!contentObject?.id && !contentObject?.parent_id && (
<FormControl>
<Checkbox name="isSponsoredContent" onChange={handleChange} checked={newData.isSponsoredContent} />
<FormControl.Label>
Criar como publicação patrocinada. <Link href="/faq#publicacao-patrocinada">Saiba mais.</Link>
</FormControl.Label>

<FormControl.Caption>
Serão consumidos 100 TabCash para criar a publicação patrocinada.
</FormControl.Caption>
</FormControl>
)}

{!contentObject?.parent_id && (
<Text sx={{ fontSize: 1 }}>Os campos marcados com um asterisco (*) são obrigatórios.</Text>
)}
Expand Down
74 changes: 19 additions & 55 deletions pages/interface/components/ContentList/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { Box, EmptyState, Link, Pagination, PastTime, TabCoinBalanceTooltip, Text, Tooltip } from '@/TabNewsUI';
import { CommentIcon, LinkExternalIcon } from '@/TabNewsUI/icons';
import { getDomain, isExternalLink, isTrustedDomain } from 'pages/interface';
import {
AdBanner,
Box,
EmptyState,
Link,
Pagination,
PastTime,
TabCoinBalanceTooltip,
Text,
Tooltip,
} from '@/TabNewsUI';
import { CommentIcon } from '@/TabNewsUI/icons';

export default function ContentList({ ad, contentList: list, pagination, paginationBasePath, emptyStateProps }) {
const listNumberStart = pagination.perPage * (pagination.currentPage - 1) + 1;
Expand All @@ -19,8 +28,14 @@ export default function ContentList({ ad, contentList: list, pagination, paginat
}}
key={`content-list-${listNumberStart}`}
start={listNumberStart}>
<Ad ad={ad} />
{ad && (
<Box as="li" sx={{ display: 'block', gridColumnStart: 2, '::marker': 'none' }}>
<AdBanner ad={ad} />
</Box>
)}

<RenderItems />

<EndOfRelevant />
</Box>
) : (
Expand Down Expand Up @@ -147,54 +162,3 @@ export default function ContentList({ ad, contentList: list, pagination, paginat
return <EmptyState title="Nenhum conteúdo encontrado" {...props} />;
}
}

function Ad({ ad }) {
if (!ad) {
return;
}

const link = ad.source_url || `/${ad.owner_username}/${ad.slug}`;
const isAdToExternalLink = isExternalLink(link);
const domain = isAdToExternalLink ? `(${getDomain(link)})` : '';
const title = ad.title.length > 70 ? ad.title.substring(0, 67).trim().concat('...') : ad.title;

return (
<Box as="li" sx={{ display: 'grid', gridColumnStart: 2, '::marker': 'none' }}>
<Box>
<Link
sx={{
overflow: 'auto',
fontWeight: 'semibold',
wordWrap: 'break-word',
':link': {
color: 'success.fg',
},
':visited': {
color: 'success.fg',
},
}}
href={link}
rel={isTrustedDomain(link) ? undefined : 'nofollow'}>
<Text sx={{ wordBreak: 'break-word', marginRight: 1 }}>
{title} {domain}
</Text>
{isAdToExternalLink && <LinkExternalIcon verticalAlign="middle" />}
</Link>
</Box>

<Text sx={{ whiteSpace: 'nowrap', fontSize: 0, color: 'neutral.emphasis' }}>
Contribuindo com{' '}
<Tooltip
aria-label={`Autor: ${ad.owner_username}`}
direction="nw"
sx={{ position: 'absolute', display: 'grid' }}>
<Link
sx={{ overflow: 'hidden', textOverflow: 'ellipsis', color: 'neutral.emphasis', mr: 2 }}
href={`/${ad.owner_username}`}>
{ad.owner_username}
</Link>
</Tooltip>
</Text>
</Box>
);
}
1 change: 1 addition & 0 deletions pages/interface/components/TabNewsUI/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as AdBanner } from '@/AdBanner';
export { default as ButtonWithLoader } from '@/ButtonWithLoader';
export { default as Confetti } from '@/Confetti';
export { default as Content } from '@/Content';
Expand Down

0 comments on commit 860aba3

Please sign in to comment.