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';