From 8a9348ef31aa8f3619336c0b6ca97dd80cc0a6a4 Mon Sep 17 00:00:00 2001 From: Rafael Tavares <26308880+Rafatcb@users.noreply.github.com> Date: Sun, 14 Jul 2024 17:36:27 -0300 Subject: [PATCH 1/3] feat(advertisement): add `Checkbox` to the UI to allow creating an sponsored content --- pages/interface/components/Content/index.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pages/interface/components/Content/index.js b/pages/interface/components/Content/index.js index f9e8127a6..9d183be72 100644 --- a/pages/interface/components/Content/index.js +++ b/pages/interface/components/Content/index.js @@ -8,6 +8,7 @@ import { BranchName, Button, ButtonWithLoader, + Checkbox, Editor, Flash, FormControl, @@ -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(''); @@ -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) { @@ -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; }); @@ -557,6 +561,17 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor )} + {!contentObject?.id && !contentObject?.parent_id && ( + + + Criar como publicação patrocinada. + + + Serão utilizados 100 TabCash para criar a publicação patrocinada. + + + )} + {!contentObject?.parent_id && ( Os campos marcados com um asterisco (*) são obrigatórios. )} From 3f72e426e3c2f00ff715842909c97a6e65319df9 Mon Sep 17 00:00:00 2001 From: Rafael Tavares <26308880+Rafatcb@users.noreply.github.com> Date: Sun, 14 Jul 2024 19:16:17 -0300 Subject: [PATCH 2/3] feat(advertisement): create FAQ entry about sponsored content --- pages/faq/index.public.js | 22 +++++++++++++++++++-- pages/interface/components/Content/index.js | 6 ++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pages/faq/index.public.js b/pages/faq/index.public.js index fcff7032c..c3c733391 100644 --- a/pages/faq/index.public.js +++ b/pages/faq/index.public.js @@ -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. @@ -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), 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?', diff --git a/pages/interface/components/Content/index.js b/pages/interface/components/Content/index.js index 9d183be72..9a5cb6164 100644 --- a/pages/interface/components/Content/index.js +++ b/pages/interface/components/Content/index.js @@ -564,10 +564,12 @@ function EditMode({ contentObject, setContentObject, setComponentMode, localStor {!contentObject?.id && !contentObject?.parent_id && ( - Criar como publicação patrocinada. + + Criar como publicação patrocinada. Saiba mais. + - Serão utilizados 100 TabCash para criar a publicação patrocinada. + Serão consumidos 100 TabCash para criar a publicação patrocinada. )} From cd45e7a695d385655a652a1c9cc2b8f8a1f71b39 Mon Sep 17 00:00:00 2001 From: Rafael Tavares <26308880+Rafatcb@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:57:00 -0300 Subject: [PATCH 3/3] feat(content): show an ad after the main content of the page --- pages/[username]/[slug]/index.public.js | 11 ++- pages/faq/index.public.js | 2 +- pages/interface/components/AdBanner/index.js | 50 +++++++++++++ .../interface/components/ContentList/index.js | 74 +++++-------------- pages/interface/components/TabNewsUI/index.js | 1 + 5 files changed, 80 insertions(+), 58 deletions(-) create mode 100644 pages/interface/components/AdBanner/index.js diff --git a/pages/[username]/[slug]/index.public.js b/pages/[username]/[slug]/index.public.js index 1ab70153d..e33d1acd6 100644 --- a/pages/[username]/[slug]/index.public.js +++ b/pages/[username]/[slug]/index.public.js @@ -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); @@ -82,6 +83,8 @@ export default function Post({ contentFound, rootContentFound, parentContentFoun /> + {adFound && } + { 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)), diff --git a/pages/faq/index.public.js b/pages/faq/index.public.js index c3c733391..d3ebad70b 100644 --- a/pages/faq/index.public.js +++ b/pages/faq/index.public.js @@ -56,7 +56,7 @@ Atualmente, o único espaço de anúncio disponível é o de [publicações patr 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), 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)\`. +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. diff --git a/pages/interface/components/AdBanner/index.js b/pages/interface/components/AdBanner/index.js new file mode 100644 index 000000000..877ed1024 --- /dev/null +++ b/pages/interface/components/AdBanner/index.js @@ -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 ( + + + + + {title} {domain} + + {isAdToExternalLink && } + + + + + Contribuindo com{' '} + + + {ad.owner_username} + + + + + ); +} diff --git a/pages/interface/components/ContentList/index.js b/pages/interface/components/ContentList/index.js index 471554c61..0d07d0ebe 100644 --- a/pages/interface/components/ContentList/index.js +++ b/pages/interface/components/ContentList/index.js @@ -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; @@ -19,8 +28,14 @@ export default function ContentList({ ad, contentList: list, pagination, paginat }} key={`content-list-${listNumberStart}`} start={listNumberStart}> - + {ad && ( + + + + )} + + ) : ( @@ -147,54 +162,3 @@ export default function ContentList({ ad, contentList: list, pagination, paginat return ; } } - -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 ( - - - - - {title} {domain} - - {isAdToExternalLink && } - - - - - Contribuindo com{' '} - - - {ad.owner_username} - - - - - ); -} diff --git a/pages/interface/components/TabNewsUI/index.js b/pages/interface/components/TabNewsUI/index.js index d98f9877a..f437ca07b 100644 --- a/pages/interface/components/TabNewsUI/index.js +++ b/pages/interface/components/TabNewsUI/index.js @@ -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';