diff --git a/pages/[username]/pagina/[page]/index.public.js b/pages/[username]/conteudos/[page]/index.public.js similarity index 64% rename from pages/[username]/pagina/[page]/index.public.js rename to pages/[username]/conteudos/[page]/index.public.js index f2464a2da..5af9b75d1 100644 --- a/pages/[username]/pagina/[page]/index.public.js +++ b/pages/[username]/conteudos/[page]/index.public.js @@ -1,24 +1,41 @@ +import { useRouter } from 'next/router'; import { getStaticPropsRevalidate } from 'next-swr'; -import { ContentList, DefaultLayout } from '@/TabNewsUI'; +import { ContentList, DefaultLayout, UserHeader } from '@/TabNewsUI'; +import { FaUser } from '@/TabNewsUI/icons'; import { NotFoundError } from 'errors'; 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 validator from 'models/validator.js'; +import { useUser } from 'pages/interface'; export default function Home({ contentListFound, pagination, username }) { + const { push } = useRouter(); + const { user, isLoading } = useUser(); + const isAuthenticatedUser = user && user.username === username; + return ( - <> - - - - + + + + push('/publicar'), + }, + }} + /> + ); } @@ -55,6 +72,7 @@ export const getStaticProps = getStaticPropsRevalidate(async (context) => { results = await content.findWithStrategy({ strategy: 'new', where: { + parent_id: null, owner_id: secureUserFound.id, status: 'published', }, diff --git a/pages/[username]/index.public.js b/pages/[username]/index.public.js index 15c94630c..78752c042 100644 --- a/pages/[username]/index.public.js +++ b/pages/[username]/index.public.js @@ -1,49 +1,44 @@ -import { useRouter } from 'next/router'; import { getStaticPropsRevalidate } from 'next-swr'; import { useState } from 'react'; import useSWR from 'swr'; import { - ActionList, - ActionMenu, Box, - ContentList, + Button, DefaultLayout, Flash, - IconButton, Label, LabelGroup, - NavItem, - NavList, - Pagehead, + Link, PastTime, TabCashCount, TabCoinCount, - Text, useConfirm, + UserHeader, Viewer, } from '@/TabNewsUI'; -import { CircleSlashIcon, FaUser, GearIcon, KebabHorizontalIcon } from '@/TabNewsUI/icons'; +import { CircleSlashIcon, GearIcon } from '@/TabNewsUI/icons'; import { NotFoundError } from 'errors'; 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 validator from 'models/validator.js'; import { useUser } from 'pages/interface'; -export default function Home({ contentListFound, pagination, userFound: userFoundFallback }) { +export default function Home({ userFound: userFoundFallback }) { const { data: userFound, mutate: userFoundMutate } = useSWR(`/api/v1/users/${userFoundFallback.username}`, { fallbackData: userFoundFallback, revalidateOnMount: false, }); - const { user, isLoading } = useUser(); - const { push } = useRouter(); + const { user } = useUser(); const confirm = useConfirm(); const [globalErrorMessage, setGlobalErrorMessage] = useState(false); const isAuthenticatedUser = user && user.username === userFound.username; + const canNuke = + !isAuthenticatedUser && user?.features?.includes('ban:user') && !userFound?.features?.includes('nuked'); + const canUpdate = isAuthenticatedUser && user?.features?.includes('update:user'); async function handleClickNuke() { setGlobalErrorMessage(null); @@ -90,115 +85,73 @@ export default function Home({ contentListFound, pagination, userFound: userFoun return; } - function OptionsMenu() { - const canNuke = - !isAuthenticatedUser && user?.features?.includes('ban:user') && !userFound?.features?.includes('nuked'); - const canUpdate = isAuthenticatedUser && user?.features?.includes('update:user'); - if (!canNuke && !canUpdate) { - return null; - } - - return ( - - - - - - - {canUpdate && ( - - - - - Editar perfil - - )} - {canNuke && ( - - - - - Nuke - - )} - - - - ); - } - function UserFeatures() { if (!userFound?.features?.length) return null; return ( - + {userFound.features.includes('nuked') && } ); } return ( - <> - - {globalErrorMessage && ( - - {globalErrorMessage} - - )} - - - - - {userFound.username} - - - - - - {' · '} - `Membro há ${date}.`} sx={{ pl: 2 }} /> - - - {OptionsMenu()} - - - {userFound.description && ( - - - - )} - - push('/publicar'), - }, - }} - /> - - + + {globalErrorMessage && ( + + {globalErrorMessage} + + )} + + + + + + + + {' · '} + `Membro há ${date}`} sx={{ pl: 2 }} /> + + + + + {canUpdate && ( + + )} + {canNuke && ( + + )} + + + + {userFound.description && ( + + + + )} + ); } @@ -245,23 +198,12 @@ export const getStaticProps = getStaticPropsRevalidate(async (context) => { }; } - let results; let secureUserFound; try { const userFound = await user.findOneByUsername(context.params.username, { withBalance: true }); secureUserFound = authorization.filterOutput(userTryingToGet, 'read:user', userFound); - - results = await content.findWithStrategy({ - strategy: 'new', - where: { - owner_id: secureUserFound.id, - status: 'published', - }, - page: context.params.page, - per_page: context.params.per_page, - }); } catch (error) { // `user` model will throw a `NotFoundError` if the user is not found. if (error instanceof NotFoundError) { @@ -274,22 +216,8 @@ export const getStaticProps = getStaticPropsRevalidate(async (context) => { throw error; } - const contentListFound = results.rows; - - const secureContentListFound = authorization.filterOutput(userTryingToGet, 'read:content:list', contentListFound); - - for (const content of secureContentListFound) { - if (content.parent_id) { - content.body = removeMarkdown(content.body, { maxLength: 255 }); - } else { - delete content.body; - } - } - return { props: { - contentListFound: JSON.parse(JSON.stringify(secureContentListFound)), - pagination: results.pagination, userFound: JSON.parse(JSON.stringify(secureUserFound)), }, diff --git a/pages/[username]/respostas/[page]/index.public.js b/pages/[username]/respostas/[page]/index.public.js new file mode 100644 index 000000000..b1b036f50 --- /dev/null +++ b/pages/[username]/respostas/[page]/index.public.js @@ -0,0 +1,114 @@ +import { useRouter } from 'next/router'; +import { getStaticPropsRevalidate } from 'next-swr'; + +import { ContentList, DefaultLayout, UserHeader } from '@/TabNewsUI'; +import { FaUser } from '@/TabNewsUI/icons'; +import { NotFoundError } from 'errors'; +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 validator from 'models/validator.js'; +import { useUser } from 'pages/interface'; + +export default function Home({ contentListFound, pagination, username }) { + const { push } = useRouter(); + const { user, isLoading } = useUser(); + const isAuthenticatedUser = user && user.username === username; + + return ( + + + + push('/publicar'), + }, + }} + /> + + ); +} + +export async function getStaticPaths() { + return { + paths: [], + fallback: 'blocking', + }; +} + +export const getStaticProps = getStaticPropsRevalidate(async (context) => { + const userTryingToGet = user.createAnonymous(); + + try { + context.params = validator(context.params, { + username: 'required', + page: 'optional', + per_page: 'optional', + }); + } catch (error) { + return { + notFound: true, + }; + } + + let results; + let secureUserFound; + + try { + const userFound = await user.findOneByUsername(context.params.username); + + secureUserFound = authorization.filterOutput(userTryingToGet, 'read:user', userFound); + + results = await content.findWithStrategy({ + strategy: 'new', + where: { + owner_id: secureUserFound.id, + status: 'published', + $not_null: ['parent_id'], + }, + page: context.params.page, + per_page: context.params.per_page, + }); + } catch (error) { + if (error instanceof NotFoundError) { + return { + notFound: true, + revalidate: 1, + }; + } + + throw error; + } + + const contentListFound = results.rows; + + const secureContentListFound = authorization.filterOutput(userTryingToGet, 'read:content:list', contentListFound); + + for (const content of secureContentListFound) { + if (content.parent_id) { + content.body = removeMarkdown(content.body, { maxLength: 255 }); + } else { + delete content.body; + } + } + + return { + props: { + contentListFound: JSON.parse(JSON.stringify(secureContentListFound)), + pagination: results.pagination, + username: secureUserFound.username, + }, + + revalidate: 10, + }; +}); diff --git a/pages/interface/components/TabNewsUI/index.js b/pages/interface/components/TabNewsUI/index.js index 402b29200..dc236b645 100644 --- a/pages/interface/components/TabNewsUI/index.js +++ b/pages/interface/components/TabNewsUI/index.js @@ -20,6 +20,7 @@ export { default as TabCoinButtons } from '@/TabCoinButtons'; export { default as TabCoinCount } from '@/TabCoinCount'; export { default as ThemeProvider } from '@/ThemeProvider'; export { default as ThemeSelector, ThemeSwitcher } from '@/ThemeSelector'; +export { default as UserHeader } from '@/UserHeader'; export { ActionList, ActionMenu, @@ -29,6 +30,7 @@ export { BranchName, Button, Checkbox, + CounterLabel, Flash, FormControl, Heading, @@ -44,6 +46,7 @@ export { SSRProvider, SegmentedControl, Spinner, + TabNav, Text, TextInput, Tooltip, diff --git a/pages/interface/components/UserHeader/index.js b/pages/interface/components/UserHeader/index.js new file mode 100644 index 000000000..98e6bf267 --- /dev/null +++ b/pages/interface/components/UserHeader/index.js @@ -0,0 +1,38 @@ +import { useRouter } from 'next/router'; + +import { Box, CounterLabel, Link, Pagehead, TabNav, Text } from '@/TabNewsUI'; + +export default function UserHeader({ username, conteudosCount, respostasCount }) { + const router = useRouter(); + + return ( + <> + + + + {username} + + + + + + + Perfil + + + + Publicações {!!conteudosCount && {conteudosCount}} + + + Respostas {!!respostasCount && {respostasCount}} + + + + ); +}