diff --git a/apps/auth/.eslintrc.js b/apps/auth/.eslintrc.js index 895847a1eefd0fb84ac9f0bb832c75d12d74fd55..306e7aa5c6bb365d67dbae8e2de5f69a2e04d7b4 100644 --- a/apps/auth/.eslintrc.js +++ b/apps/auth/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { root: true, - extends: ['@hive/eslint-config-custom'] + extends: ['@hive/eslint-config-custom'], + ignorePatterns: ['.next/**', 'dist/**', 'build/**', 'node_modules/**'] }; diff --git a/apps/auth/package.json b/apps/auth/package.json index aa1cb309681841d70ab3953661d54b1364347834..05d94e8bde032479c45e5ca46889fcd69215c372 100644 --- a/apps/auth/package.json +++ b/apps/auth/package.json @@ -9,8 +9,8 @@ "copy:worker": "cp ../../node_modules/@hiveio/hb-auth/dist/worker.js ./public/auth/", "copy:assets": "cp -r ../../node_modules/@hiveio/hb-auth/dist/assets ./public/auth/assets", "predev": "pnpm run clean", - "dev": "react-env -- next dev -p 5000 | pnpm dlx pino-pretty --colorize", - "devssl": "react-env -- next dev --port 5000 --experimental-https --experimental-https-key ../../ssl/server-key.pem --experimental-https-cert ../../ssl/server-cert.pem | pnpm dlx pino-pretty --colorize", + "dev": "react-env -- next dev -p 5000", + "devssl": "react-env -- next dev --port 5000 --experimental-https --experimental-https-key ../../ssl/server-key.pem --experimental-https-cert ../../ssl/server-cert.pem", "prebuild": "pnpm run clean", "build": "react-env -- next build", "postbuild": "pnpm run copy:worker && pnpm run copy:assets", diff --git a/apps/blog/.eslintrc.js b/apps/blog/.eslintrc.js index 895847a1eefd0fb84ac9f0bb832c75d12d74fd55..306e7aa5c6bb365d67dbae8e2de5f69a2e04d7b4 100644 --- a/apps/blog/.eslintrc.js +++ b/apps/blog/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { root: true, - extends: ['@hive/eslint-config-custom'] + extends: ['@hive/eslint-config-custom'], + ignorePatterns: ['.next/**', 'dist/**', 'build/**', 'node_modules/**'] }; diff --git a/apps/blog/app/(main-and-community)/created/[tag]/content.tsx b/apps/blog/app/(main-and-community)/created/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eaed0a960641a9c9b50967f02e7a13786c8e33bb --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/[tag]/content.tsx @@ -0,0 +1,11 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'created'; + +const Content = ({ tag }: { tag: string }) => { + return ; +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/created/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/created/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/created/[tag]/page.tsx b/apps/blog/app/(main-and-community)/created/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c1c1e5c7a1d883bd8db3fb35afde099b7abb868c --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/[tag]/page.tsx @@ -0,0 +1,21 @@ +import SortPage from '@/blog/features/community-profile/sort-page'; +import Content from './content'; + +interface PageProps { + params: { + tag: string; + }; +} + +const sort = 'created'; + +const Page = ({ params }: PageProps) => { + const { tag } = params; + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/(main-and-community)/created/content.tsx b/apps/blog/app/(main-and-community)/created/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..760473a6c97129d80cbe3fd8733963892ee1901e --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'created'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/created/my/content.tsx b/apps/blog/app/(main-and-community)/created/my/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..81eed369363fb5d7729c844b9d933c27b214e766 --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/my/content.tsx @@ -0,0 +1,10 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'created'; +const tag = 'my'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/created/my/page.tsx b/apps/blog/app/(main-and-community)/created/my/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..412b926bbeba906124a2972f3a6125c8265132d6 --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/my/page.tsx @@ -0,0 +1,13 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'created'; +const tag = 'my'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/created/page.tsx b/apps/blog/app/(main-and-community)/created/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..18161b86e4b5526b41547ffdb063df607ae497dc --- /dev/null +++ b/apps/blog/app/(main-and-community)/created/page.tsx @@ -0,0 +1,12 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'created'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/hot/[tag]/content.tsx b/apps/blog/app/(main-and-community)/hot/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e8de99df64da55fae99182a3664f5155142262f2 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/[tag]/content.tsx @@ -0,0 +1,11 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'hot'; + +const Content = ({ tag }: { tag: string }) => { + return ; +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/hot/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/hot/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/hot/[tag]/page.tsx b/apps/blog/app/(main-and-community)/hot/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..85e69c60dab814b7f301fa7fef32d24d3a2ce817 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/[tag]/page.tsx @@ -0,0 +1,21 @@ +import SortPage from '@/blog/features/community-profile/sort-page'; +import Content from './content'; + +interface PageProps { + params: { + tag: string; + }; +} + +const sort = 'hot'; + +const Page = ({ params }: PageProps) => { + const { tag } = params; + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/(main-and-community)/hot/content.tsx b/apps/blog/app/(main-and-community)/hot/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4020135856590563a7fd44484ab0c82f9b2a839f --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'hot'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/hot/my/content.tsx b/apps/blog/app/(main-and-community)/hot/my/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b0bca12e48ead20e3b8dbc224368331b6a0b5680 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/my/content.tsx @@ -0,0 +1,10 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'hot'; +const tag = 'my'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/hot/my/page.tsx b/apps/blog/app/(main-and-community)/hot/my/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..60fae65485397756843074fc677a095f01dcda32 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/my/page.tsx @@ -0,0 +1,13 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'hot'; +const tag = 'my'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/hot/page.tsx b/apps/blog/app/(main-and-community)/hot/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e4d320a307b1a5dc21dc699a820fbe9bac8def38 --- /dev/null +++ b/apps/blog/app/(main-and-community)/hot/page.tsx @@ -0,0 +1,12 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'hot'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/layout.tsx b/apps/blog/app/(main-and-community)/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..33ab2a0578ad4fed0ea808b494e09693f267881e --- /dev/null +++ b/apps/blog/app/(main-and-community)/layout.tsx @@ -0,0 +1,12 @@ +import ClientSideLayout from '@/blog/features/layouts/sorts/client-side-layout'; +import ServerSideLayout from '@/blog/features/layouts/sorts/server-side-layout'; +import { ReactNode } from 'react'; + +const Layout = ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/muted/[tag]/content.tsx b/apps/blog/app/(main-and-community)/muted/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..90c645170903fcb12a960fee2c386906f98fb3e9 --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/[tag]/content.tsx @@ -0,0 +1,11 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'muted'; + +const Content = ({ tag }: { tag: string }) => { + return ; +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/muted/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/muted/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/muted/[tag]/page.tsx b/apps/blog/app/(main-and-community)/muted/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b37db8d98d614a55e7a7500cd04081d1b3f7491d --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/[tag]/page.tsx @@ -0,0 +1,20 @@ +import SortPage from '@/blog/features/community-profile/sort-page'; +import Content from './content'; + +interface PageProps { + params: { + tag: string; + }; +} +const sort = 'muted'; + +const Page = ({ params }: PageProps) => { + const { tag } = params; + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/(main-and-community)/muted/content.tsx b/apps/blog/app/(main-and-community)/muted/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c333489ee70fad9d1e04b8504d9d7e4f173e4ecd --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'muted'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/muted/my/content.tsx b/apps/blog/app/(main-and-community)/muted/my/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..84f3e07821baaa2483b15982e49505a4e6b0503b --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/my/content.tsx @@ -0,0 +1,10 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'muted'; +const tag = 'my'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/muted/my/page.tsx b/apps/blog/app/(main-and-community)/muted/my/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eee520fb781d3d2de552fb72db00fe15021117a3 --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/my/page.tsx @@ -0,0 +1,13 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'muted'; +const tag = 'my'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/muted/page.tsx b/apps/blog/app/(main-and-community)/muted/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bf60f93991e46c2c3da47a96b7f69a61a2e54bf3 --- /dev/null +++ b/apps/blog/app/(main-and-community)/muted/page.tsx @@ -0,0 +1,12 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'muted'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/payout/[tag]/content.tsx b/apps/blog/app/(main-and-community)/payout/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5c87987ff6e66137ea59f292db83c2f974a53685 --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/[tag]/content.tsx @@ -0,0 +1,11 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'payout'; + +const Content = ({ tag }: { tag: string }) => { + return ; +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/payout/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/payout/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/payout/[tag]/page.tsx b/apps/blog/app/(main-and-community)/payout/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d3c431ea39ff5387d57b679f7285afcf53804f64 --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/[tag]/page.tsx @@ -0,0 +1,21 @@ +import SortPage from '@/blog/features/community-profile/sort-page'; +import Content from './content'; + +interface PageProps { + params: { + tag: string; + }; +} + +const sort = 'payout'; + +const Page = ({ params }: PageProps) => { + const { tag } = params; + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/(main-and-community)/payout/content.tsx b/apps/blog/app/(main-and-community)/payout/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fb50cfb11f272f71a992f8a04f51fe449d8e2dbc --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'payout'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/payout/my/content.tsx b/apps/blog/app/(main-and-community)/payout/my/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d0e3d4e9d0a49d3041ee87f3f600d78e8911169f --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/my/content.tsx @@ -0,0 +1,10 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'payout'; +const tag = 'my'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/payout/my/page.tsx b/apps/blog/app/(main-and-community)/payout/my/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..abd3488850a7dd3b85d0984d2b5d94aca984fe57 --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/my/page.tsx @@ -0,0 +1,13 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'payout'; +const tag = 'my'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/payout/page.tsx b/apps/blog/app/(main-and-community)/payout/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bad0c97a5a5bec4be82566109851fb05c78b486b --- /dev/null +++ b/apps/blog/app/(main-and-community)/payout/page.tsx @@ -0,0 +1,12 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'payout'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/roles/[tag]/content.tsx b/apps/blog/app/(main-and-community)/roles/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..30b82af4fe89d0bb743c139040277a184d8de472 --- /dev/null +++ b/apps/blog/app/(main-and-community)/roles/[tag]/content.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { useQuery } from '@tanstack/react-query'; +import Loading from '@ui/components/loading'; +import AddRole from '@/blog/features/community-profile/add-role'; +import { Table, TableBody, TableHead, TableHeader, TableRow } from '@ui/components/table'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import { getRoleValue, Roles, rolesLevels } from '@/blog/features/community-profile/lib/utils'; +import TableItem from '@/blog/features/community-profile/table-item'; +import NoDataError from '@/blog/components/no-data-error'; +import { getListCommunityRoles } from '@transaction/lib/bridge-api'; +import { useTranslation } from '@/blog/i18n/client'; + +const Content = ({ community }: { community: string }) => { + const { user } = useUser(); + const { t } = useTranslation('common_blog'); + + const { data, isLoading, isError } = useQuery({ + queryKey: ['rolesList', community], + queryFn: () => getListCommunityRoles(community), + enabled: Boolean(community), + select: (list) => + list + ? list.map((e) => ({ + name: e[0], + value: getRoleValue(e[1] as Roles), + role: e[1] as Roles, + title: e[2], + temprary: !!e[3] + })) + : [] + }); + + const loggedUser = data?.find((e) => e.name === user.username) ?? { + value: 1, + role: 'guest', + name: user.username, + title: '' + }; + + if (isLoading) return ; + if (isError) return ; + + return ( +
+
+

{t('communities.user_roles')}

+ + + + {t('communities.account')} + {t('communities.role')} + {t('communities.title')} + + + + {data.map((e) => ( + + ))} + +
+ {loggedUser.value >= 3 && } +
+

{t('communities.role_permissions')}

+
+ {rolesLevels.map((role) => ( +
+ {t(`communities.${role.name}`)} + - {t(`communities.description_${role.name}`)} +
+ ))} +
+
+
+
+ ); +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/roles/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/roles/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/roles/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/roles/[tag]/page.tsx b/apps/blog/app/(main-and-community)/roles/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5752f91464cac9308af40837436a027d91c44d7b --- /dev/null +++ b/apps/blog/app/(main-and-community)/roles/[tag]/page.tsx @@ -0,0 +1,15 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import Content from './content'; +import { getListCommunityRoles } from '@transaction/lib/bridge-api'; + +const Page = async ({ params }: { params: { tag: string } }) => { + const queryClient = getQueryClient(); + await queryClient.prefetchQuery({ + queryKey: ['community', params.tag], + queryFn: () => getListCommunityRoles(params.tag) + }); + + return ; +}; + +export default Page; diff --git a/apps/blog/app/(main-and-community)/trending/[tag]/content.tsx b/apps/blog/app/(main-and-community)/trending/[tag]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..129364afb7f621864b0037fa8afc1ecd9464792b --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/[tag]/content.tsx @@ -0,0 +1,11 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'trending'; + +const Content = ({ tag }: { tag: string }) => { + return ; +}; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/trending/[tag]/layout.tsx b/apps/blog/app/(main-and-community)/trending/[tag]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..822947f5580c3710bafee92b14c2b1c6ba40ec80 --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/[tag]/layout.tsx @@ -0,0 +1,7 @@ +import PrefetchComponent from '@/blog/features/layouts/community/prefetch-component'; +import { ReactNode } from 'react'; + +const Layout = ({ children, params }: { children: ReactNode; params: { tag: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/(main-and-community)/trending/[tag]/page.tsx b/apps/blog/app/(main-and-community)/trending/[tag]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3039b62fe3556596fb8b6eded7d2ff4cef77e9a9 --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/[tag]/page.tsx @@ -0,0 +1,21 @@ +import SortPage from '@/blog/features/community-profile/sort-page'; +import Content from './content'; + +interface PageProps { + params: { + tag: string; + }; +} + +const sort = 'trending'; + +const Page = ({ params }: PageProps) => { + const { tag } = params; + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/(main-and-community)/trending/content.tsx b/apps/blog/app/(main-and-community)/trending/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..92c5dd1e38979c9af65d8805af2c508d30dd1d91 --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'trending'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/trending/my/content.tsx b/apps/blog/app/(main-and-community)/trending/my/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f2798668f36ee3dede180599c51f8ee25ef4404f --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/my/content.tsx @@ -0,0 +1,10 @@ +'use client'; + +import SortedPagesPosts from '@/blog/features/tags-pages/list-of-posts'; + +const sort = 'trending'; +const tag = 'my'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/(main-and-community)/trending/my/page.tsx b/apps/blog/app/(main-and-community)/trending/my/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4d1f54321cebec176f5540f2042f60566b12d4d2 --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/my/page.tsx @@ -0,0 +1,13 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'trending'; +const tag = 'my'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/(main-and-community)/trending/page.tsx b/apps/blog/app/(main-and-community)/trending/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2919bf8b0592d1f8ebcbe4df4bbed9f50eaa4609 --- /dev/null +++ b/apps/blog/app/(main-and-community)/trending/page.tsx @@ -0,0 +1,12 @@ +import Content from './content'; +import SortPage from '@/blog/features/tags-pages/sort-page'; + +const sort = 'trending'; + +const Page = () => ( + + + +); + +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/content.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..73c43a021356cc5886f76ea9290bab58909b9916 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'comments'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/page.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..de66e0014da9435a0f1b0a2647b5da15b68b01c1 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/comments/page.tsx @@ -0,0 +1,13 @@ +import PostsPage from '@/blog/features/account-profile/posts-page'; +import Content from './content'; + +const query = 'comments'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/layout.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..051d353279a9963b239c834b9e01866f582c643a --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/layout.tsx @@ -0,0 +1,7 @@ +import AccountPostsTabs from '@/blog/features/layouts/account-posts/tabs'; +import { ReactNode } from 'react'; + +const Layout = async ({ children, params }: { children: ReactNode; params: { param: string } }) => { + return {children}; +}; +export default Layout; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/content.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..85941f5b0fe4c0d0fba32eb8060ac1613fa61268 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'payout'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/page.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5116f7e590640f52436d338e3e2a37ea0ec69bef --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/payout/page.tsx @@ -0,0 +1,13 @@ +import PostsPage from '@/blog/features/account-profile/posts-page'; +import Content from './content'; + +const query = 'payout'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/content.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..33898c08f4078b2024948cbe59c629038b3c3fe4 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'posts'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/page.tsx b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b56618c8c0dda66f3b8a4226d1dcbbd6c68d7885 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/(posts-tabs)/posts/page.tsx @@ -0,0 +1,13 @@ +import PostsPage from '@/blog/features/account-profile/posts-page'; +import Content from './content'; + +const query = 'posts'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/communities/content.tsx b/apps/blog/app/[param]/(user-profile)/communities/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..271bb5b7f62d0b4d2a67f7b45fa75295d00d571f --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/communities/content.tsx @@ -0,0 +1,73 @@ +'use client'; + +import SocialActivities from '@/blog/features/account-social/social-activities'; +import SubscriptionList from '@/blog/features/account-social/subscription-list'; +import { useTranslation } from '@/blog/i18n/client'; +import { useQuery } from '@tanstack/react-query'; +import { getSubscriptions } from '@transaction/lib/bridge-api'; +import { getHivebuzzBadges, getPeakdBadges } from '@transaction/lib/custom-api'; +import { getUserAvatarUrl } from '@ui/lib/avatar-utils'; +import Link from 'next/link'; + +const CommunityContent = ({ username }: { username: string }) => { + const { t } = useTranslation('common_blog'); + const { data } = useQuery({ + queryKey: ['listAllSubscription', username], + queryFn: () => getSubscriptions(username) + }); + + const { data: hivebuzz } = useQuery({ + queryKey: ['hivebuzz', username], + queryFn: () => getHivebuzzBadges(username) + }); + + const { data: peakd } = useQuery({ + queryKey: ['peakd', username], + queryFn: () => getPeakdBadges(username), + select: (data) => + data.map((e: { id: string; name: string; title: string }) => ({ + id: e.title, + url: getUserAvatarUrl(e.name, 'medium'), + title: e.title + })) + }); + + return ( +
+

+ {t('user_profile.social_tab.community_subscriptions_title')} +

+

+ {t('user_profile.social_tab.the_author_has_subscribed_to_the_following')} +

+ {data && data.length > 0 ? ( + + ) : ( +
+ {t('user_profile.social_tab.you_dont_have_any_subscriptions')} +
+ )} +

+ {t('user_profile.social_tab.badges_and_achievements_title')} +

+

+ {t('user_profile.social_tab.these_are_badges_received_by_the_author')} + + Peakd + + {` & `} + + Hivebuzz + + . +

+ + +
+ ); +}; +export default CommunityContent; diff --git a/apps/blog/app/[param]/(user-profile)/communities/page.tsx b/apps/blog/app/[param]/(user-profile)/communities/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3030ed70ec744c45ce6763c520a9fdd418adc123 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/communities/page.tsx @@ -0,0 +1,32 @@ +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import CommunityContent from './content'; +import { getQueryClient } from '@/blog/lib/react-query'; +import { getHivebuzzBadges, getPeakdBadges } from '@transaction/lib/custom-api'; +import { getSubscriptions } from '@transaction/lib/bridge-api'; + +const CommunitiesPage = async ({ params }: { params: { param: string } }) => { + const queryClient = getQueryClient(); + const username = params.param.replace('%40', ''); + + await queryClient.prefetchQuery({ + queryKey: ['listAllSubscription', username], + queryFn: () => getSubscriptions(username) + }); + + await queryClient.prefetchQuery({ + queryKey: ['hivebuzz', username], + queryFn: () => getHivebuzzBadges(username) + }); + + await queryClient.prefetchQuery({ + queryKey: ['peakd', username], + queryFn: () => getPeakdBadges(username) + }); + return ( + + + + ); +}; + +export default CommunitiesPage; diff --git a/apps/blog/app/[param]/(user-profile)/content.tsx b/apps/blog/app/[param]/(user-profile)/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..126b6c48f359c4481e7cc8c4c9ccfd1b2ea442d0 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'blog'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/(user-profile)/followed/content.tsx b/apps/blog/app/[param]/(user-profile)/followed/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ca05efd0664700a7bfa617ba32610723e36f9fd9 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/followed/content.tsx @@ -0,0 +1,99 @@ +'use client'; + +import BasePathLink from '@/blog/components/base-path-link'; +import PrevNextButtons from '@/blog/features/account-lists/prev-next-buttons'; +import { useFollowingInfiniteQuery } from '@/blog/features/account-lists/hooks/use-following-infinitequery'; +import { useTranslation } from '@/blog/i18n/client'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import { useQuery } from '@tanstack/react-query'; +import { getAccountFull } from '@transaction/lib/hive-api'; +import { useState } from 'react'; +import ButtonsContainer from '@/blog/features/mute-follow/buttons-container'; + +const LIMIT = 50; + +const FollowedContent = ({ username }: { username: string }) => { + const { t } = useTranslation('common_blog'); + + const { data: profileData } = useQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + const [page, setPage] = useState(0); + + const { user } = useUser(); + const followingData = useFollowingInfiniteQuery(username, LIMIT); + const following = useFollowingInfiniteQuery(user?.username || '', 1000, 'blog', ['blog']); + const mute = useFollowingInfiniteQuery(user.username, 1000, 'ignore', ['ignore']); + + const handleNextPage = () => { + if (!followingData.data) return; + + if (page + 1 < followingData.data.pages.length) { + return setPage((prev) => prev + 1); + } + followingData.fetchNextPage().then(() => setPage((prev) => prev + 1)); + }; + const handlePrevPage = () => { + if (page <= 0) return; + setPage((prev) => prev - 1); + }; + + return ( +
+

+ {t('user_profile.lists.followed_pages', { + current: page + 1, + total: profileData?.follow_stats?.following_count + ? Math.ceil(profileData?.follow_stats?.following_count / LIMIT) + : '?' + })} +

+ 0} + isLoading={followingData.isFetchingNextPage} + /> +
    + {followingData.data?.pages[page].map((e) => ( +
  • + {e.following} + {!user.isLoggedIn || user.username === e.following ? null : ( +
    + +
    + )} +
  • + ))} +
+ 0} + isLoading={followingData.isFetchingNextPage} + /> +

+ {t('user_profile.lists.followed_pages', { + current: page + 1, + total: profileData?.follow_stats?.following_count + ? Math.ceil(profileData?.follow_stats?.following_count / LIMIT) + : '?' + })} +

+
+ ); +}; + +export default FollowedContent; diff --git a/apps/blog/app/[param]/(user-profile)/followed/page.tsx b/apps/blog/app/[param]/(user-profile)/followed/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..abeeacbaa1b2daa41495d347d147e3c857e5fcbb --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/followed/page.tsx @@ -0,0 +1,29 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import FollowedContent from './content'; +import { getAccountFull, getFollowing } from '@transaction/lib/hive-api'; + +const FollowedUsersPage = async ({ params }: { params: { param: string } }) => { + const queryClient = getQueryClient(); + const username = params.param.replace('%40', ''); + + await queryClient.prefetchQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + await queryClient.prefetchInfiniteQuery({ + queryKey: ['followingData', username], + queryFn: ({ pageParam: last_id }) => getFollowing({ account: username, start: last_id, limit: 50 }), + getNextPageParam: (lastPage) => { + return lastPage.length >= 50 ? lastPage[lastPage.length - 1].following : undefined; + } + }); + + return ( + + + + ); +}; + +export default FollowedUsersPage; diff --git a/apps/blog/app/[param]/(user-profile)/followers/content.tsx b/apps/blog/app/[param]/(user-profile)/followers/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b4c411ebde6ecd7a8b59ba40ab86e296b8db8fb3 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/followers/content.tsx @@ -0,0 +1,99 @@ +'use client'; + +import BasePathLink from '@/blog/components/base-path-link'; +import { useFollowersInfiniteQuery } from '@/blog/features/account-lists/hooks/use-followers-infinitequery'; +import { useFollowingInfiniteQuery } from '@/blog/features/account-lists/hooks/use-following-infinitequery'; +import PrevNextButtons from '@/blog/features/account-lists/prev-next-buttons'; +import ButtonsContainer from '@/blog/features/mute-follow/buttons-container'; +import { useTranslation } from '@/blog/i18n/client'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import { useQuery } from '@tanstack/react-query'; +import { getAccountFull } from '@transaction/lib/hive-api'; +import { useState } from 'react'; + +const LIMIT = 50; + +const FollowersContent = ({ username }: { username: string }) => { + const { t } = useTranslation('common_blog'); + const [page, setPage] = useState(0); + const { user } = useUser(); + + const { data: profileData } = useQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + + const followersData = useFollowersInfiniteQuery(username, LIMIT); + const following = useFollowingInfiniteQuery(user.username, 1000, 'blog', ['blog']); + const mute = useFollowingInfiniteQuery(user.username, 1000, 'ignore', ['ignore']); + + const handleNextPage = () => { + if (!followersData.data) return; + + if (page + 1 < followersData.data.pages.length) { + return setPage((prev) => prev + 1); + } + followersData.fetchNextPage().then(() => setPage((prev) => prev + 1)); + }; + const handlePrevPage = () => { + if (page <= 0) return; + setPage((prev) => prev - 1); + }; + return ( +
+

+ {t('user_profile.lists.followers_pages', { + current: page + 1, + total: profileData?.follow_stats?.follower_count + ? Math.ceil(profileData?.follow_stats?.follower_count / LIMIT) + : 1 + })} +

+ 0} + isLoading={followersData.isFetchingNextPage} + /> +
    + {followersData.data?.pages[page].map((e) => ( +
  • + {e.follower} + {!user.isLoggedIn || user.username === e.follower ? null : ( +
    + +
    + )} +
  • + ))} +
+ 0} + isLoading={followersData.isFetchingNextPage} + /> +

+ {t('user_profile.lists.followers_pages', { + current: page + 1, + total: profileData?.follow_stats?.follower_count + ? Math.ceil(profileData?.follow_stats?.follower_count / LIMIT) + : 1 + })} +

+
+ ); +}; + +export default FollowersContent; diff --git a/apps/blog/app/[param]/(user-profile)/followers/page.tsx b/apps/blog/app/[param]/(user-profile)/followers/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7c7cdedfafdaaf2bbd168b10def3ec76f0b1b5d5 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/followers/page.tsx @@ -0,0 +1,25 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import FollowersContent from './content'; +import { getAccountFull, getFollowers } from '@transaction/lib/hive-api'; + +const FollowersPage = async ({ params }: { params: { param: string } }) => { + const queryClient = getQueryClient(); + const username = params.param.replace('%40', ''); + + await queryClient.prefetchQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + + await queryClient.prefetchInfiniteQuery({ + queryKey: ['followersData', username], + queryFn: ({ pageParam: last_id }) => + getFollowers({ account: username, start: last_id, type: 'blog', limit: 50 }), + getNextPageParam: (lastPage) => { + return lastPage.length >= 50 ? lastPage[lastPage.length - 1].follower : undefined; + } + }); + return ; +}; + +export default FollowersPage; diff --git a/apps/blog/app/[param]/(user-profile)/layout.tsx b/apps/blog/app/[param]/(user-profile)/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9c992a231bb947d71b410b9120c3e588e7f136bc --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/layout.tsx @@ -0,0 +1,37 @@ +import ProfileLayout from '@/blog/features/layouts/user-profile/profile-layout'; +import { ReactNode } from 'react'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import { getQueryClient } from '@/blog/lib/react-query'; +import { getAccountFull, getAccountReputations, getDynamicGlobalProperties } from '@transaction/lib/hive-api'; +import { getTwitterInfo } from '@transaction/lib/custom-api'; + +const Layout = async ({ children, params }: { children: ReactNode; params: { param: string } }) => { + const queryClient = getQueryClient(); + const { param } = params; + const username = param.startsWith('%40') ? param.replace('%40', '') : param; + + console.log('Layout username:', username); + await queryClient.prefetchQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + await queryClient.prefetchQuery({ + queryKey: ['accountReputationData', username], + queryFn: () => getAccountReputations(username, 1) + }); + await queryClient.prefetchQuery({ + queryKey: ['twitterData', username], + queryFn: () => getTwitterInfo(username) + }); + await queryClient.prefetchQuery({ + queryKey: ['dynamicGlobalData'], + queryFn: () => getDynamicGlobalProperties() + }); + return ( + + {children} + + ); +}; + +export default Layout; diff --git a/apps/blog/app/[param]/(user-profile)/lists/blacklisted/content.tsx b/apps/blog/app/[param]/(user-profile)/lists/blacklisted/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d04d64e9826e7b4abcd2dfe05f9b19ea5d2db86c --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/blacklisted/content.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { useFollowListQuery } from '@/blog/components/hooks/use-follow-list'; +import ProfileLists from '@/blog/features/account-lists/profile-lists-component'; + +const type = 'blacklisted'; +const BlacklistedUsersContent = ({ param }: { param: string }) => { + const username = param.replace('%40', ''); + + const { data } = useFollowListQuery(username, type); + + return ; +}; +export default BlacklistedUsersContent; diff --git a/apps/blog/app/[param]/(user-profile)/lists/blacklisted/page.tsx b/apps/blog/app/[param]/(user-profile)/lists/blacklisted/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..83e34e6999c82b4654539051ab118c73ce25a070 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/blacklisted/page.tsx @@ -0,0 +1,20 @@ +import ListsPage from '@/blog/features/account-lists/lists-page'; +import BlacklistedUsersContent from './content'; + +const type = 'blacklisted'; + +interface PageProps { + params: { + param: string; + }; +} +const BlacklistedUsersPage = ({ params }: PageProps) => { + const { param } = params; + + return ( + + + + ); +}; +export default BlacklistedUsersPage; diff --git a/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/content.tsx b/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..962265f7f100aaecf430a7f1537fc0eeec44da60 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/content.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { useFollowListQuery } from '@/blog/components/hooks/use-follow-list'; +import ProfileLists from '@/blog/features/account-lists/profile-lists-component'; + +const type = 'follow_blacklist'; + +const FollowedBlacklistContent = ({ param }: { param: string }) => { + const username = param.replace('%40', ''); + + const { data } = useFollowListQuery(username, type); + + return ; +}; +export default FollowedBlacklistContent; diff --git a/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/page.tsx b/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..067c9924133d4e4cea220b467a383987feee84c9 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/followed_blacklists/page.tsx @@ -0,0 +1,20 @@ +import ListsPage from '@/blog/features/account-lists/lists-page'; +import FollowedBlacklistContent from './content'; + +const type = 'follow_blacklist'; + +interface PageProps { + params: { + param: string; + }; +} +const FollowedBlacklistPage = ({ params }: PageProps) => { + const { param } = params; + + return ( + + + + ); +}; +export default FollowedBlacklistPage; diff --git a/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/content.tsx b/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d64947d453c929c16d6be4c320ea9a29ef5b1cbf --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/content.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { useFollowListQuery } from '@/blog/components/hooks/use-follow-list'; +import ProfileLists from '@/blog/features/account-lists/profile-lists-component'; + +const type = 'followed_muted_lists'; + +const FollowedMutedListsContent = ({ param }: { param: string }) => { + const username = param.replace('%40', ''); + + const { data } = useFollowListQuery(username, type); + + return ; +}; +export default FollowedMutedListsContent; diff --git a/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/page.tsx b/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ba2a7320eb90127ab25d66800da559dddab2a408 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/followed_muted_lists/page.tsx @@ -0,0 +1,20 @@ +import ListsPage from '@/blog/features/account-lists/lists-page'; +import FollowedMutedListsContent from './content'; + +const type = 'followed_muted_lists'; + +interface PageProps { + params: { + param: string; + }; +} +const FollowedMutedListsPage = ({ params }: PageProps) => { + const { param } = params; + + return ( + + + + ); +}; +export default FollowedMutedListsPage; diff --git a/apps/blog/app/[param]/(user-profile)/lists/muted/content.tsx b/apps/blog/app/[param]/(user-profile)/lists/muted/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5119562e8583bfa853f55dc96d14f4da5e5ac44f --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/muted/content.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { useFollowListQuery } from '@/blog/components/hooks/use-follow-list'; +import ProfileLists from '@/blog/features/account-lists/profile-lists-component'; + +const type = 'muted'; + +const MutedContent = ({ param }: { param: string }) => { + const username = param.replace('%40', ''); + + const { data } = useFollowListQuery(username, type); + + return ; +}; +export default MutedContent; diff --git a/apps/blog/app/[param]/(user-profile)/lists/muted/page.tsx b/apps/blog/app/[param]/(user-profile)/lists/muted/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2aa2d1b8650e5bb0187ee351fe8635ac54547221 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/lists/muted/page.tsx @@ -0,0 +1,20 @@ +import ListsPage from '@/blog/features/account-lists/lists-page'; +import MutedContent from './content'; + +const type = 'muted'; + +interface PageProps { + params: { + param: string; + }; +} +const MutedPage = ({ params }: PageProps) => { + const { param } = params; + + return ( + + + + ); +}; +export default MutedPage; diff --git a/apps/blog/app/[param]/(user-profile)/notifications/content.tsx b/apps/blog/app/[param]/(user-profile)/notifications/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cbe95ef4453494d0831d82ce079e305de1f607ce --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/notifications/content.tsx @@ -0,0 +1,33 @@ +'use client'; + +import NotificationActivities from '@/blog/features/activity-log/notification-content'; +import { useTranslation } from '@/blog/i18n/client'; +import { useQuery } from '@tanstack/react-query'; +import { getAccountNotifications } from '@transaction/lib/bridge-api'; + +const NotificationContent = ({ username }: { username: string }) => { + const { t } = useTranslation('common_blog'); + + const { data } = useQuery({ + queryKey: ['AccountNotification', username], + queryFn: () => getAccountNotifications(username) + }); + + return ( +
+ {data && data.length > 0 ? ( + + ) : ( +
+ {t('user_profile.no_notifications_yet', { username: username })} +
+ )} +
+ ); +}; + +export default NotificationContent; diff --git a/apps/blog/app/[param]/(user-profile)/notifications/page.tsx b/apps/blog/app/[param]/(user-profile)/notifications/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2f18287683b813ec2e767b35268bde9578a553c9 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/notifications/page.tsx @@ -0,0 +1,23 @@ +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import NotificationContent from './content'; +import { getAccountNotifications } from '@transaction/lib/bridge-api'; +import { getQueryClient } from '@/blog/lib/react-query'; + +const NotificationsPage = async ({ params }: { params: { param: string } }) => { + const username = params.param.replace('%40', ''); + + const queryClient = getQueryClient(); + + await queryClient.prefetchQuery({ + queryKey: ['AccountNotification', username], + queryFn: () => getAccountNotifications(username) + }); + + return ( + + + + ); +}; + +export default NotificationsPage; diff --git a/apps/blog/app/[param]/(user-profile)/page.tsx b/apps/blog/app/[param]/(user-profile)/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f992111e9d8efcd9ade86a81d3c5ac079c1cf5ae --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/page.tsx @@ -0,0 +1,14 @@ +import Content from './content'; +import PostsPage from '@/blog/features/account-profile/posts-page'; + +const query = 'blog'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; + +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/replies/content.tsx b/apps/blog/app/[param]/(user-profile)/replies/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..767ee07183e56bb3828b8f68077a031fbca02e3e --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/replies/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'replies'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/(user-profile)/replies/page.tsx b/apps/blog/app/[param]/(user-profile)/replies/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..747775b46aa77dee8a2736326faeab1047bfb6be --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/replies/page.tsx @@ -0,0 +1,13 @@ +import PostsPage from '@/blog/features/account-profile/posts-page'; +import Content from './content'; + +const query = 'replies'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/[param]/(user-profile)/settings/content.tsx b/apps/blog/app/[param]/(user-profile)/settings/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..530500dbbb8854b51c895c17356c80dcf5b57431 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/settings/content.tsx @@ -0,0 +1,20 @@ +'use client'; + +import SettingsForm from '@/blog/features/account-settings/form'; +import HealthChecker from '@/blog/features/account-settings/health-checker'; +import MutedList from '@/blog/features/account-settings/muted-list'; +import { useUser } from '@smart-signer/lib/auth/use-user'; + +const SettingsContent = ({ username }: { username: string }) => { + const { user } = useUser(); + + return ( +
+ {user?.isLoggedIn && user?.username === username ? : null} + + +
+ ); +}; + +export default SettingsContent; diff --git a/apps/blog/app/[param]/(user-profile)/settings/page.tsx b/apps/blog/app/[param]/(user-profile)/settings/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6a2880c74c7f3d6bec9857f197fcd61c595fdf48 --- /dev/null +++ b/apps/blog/app/[param]/(user-profile)/settings/page.tsx @@ -0,0 +1,27 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import SettingsContent from './content'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import { getAccountFull } from '@transaction/lib/hive-api'; +import { getFollowList } from '@transaction/lib/bridge-api'; + +const SettingsPage = async ({ params }: { params: { param: string } }) => { + const username = params.param.replace('%40', ''); + const queryClient = getQueryClient(); + + await queryClient.prefetchQuery({ + queryKey: ['muted', username], + queryFn: () => getFollowList(username, 'muted') + }); + await queryClient.prefetchQuery({ + queryKey: ['profileData', username], + queryFn: () => getAccountFull(username) + }); + + return ( + + + + ); +}; + +export default SettingsPage; diff --git a/apps/blog/app/[param]/[p2]/[permlink]/content.tsx b/apps/blog/app/[param]/[p2]/[permlink]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7760f7eb5c822cd887a750fdf6b93a3113a0d762 --- /dev/null +++ b/apps/blog/app/[param]/[p2]/[permlink]/content.tsx @@ -0,0 +1,689 @@ +'use client'; + +import BasePathLink from '@/blog/components/base-path-link'; +import DialogLogin from '@/blog/components/dialog-login'; +import { useFollowListQuery } from '@/blog/components/hooks/use-follow-list'; +import { usePinMutation, useUnpinMutation } from '@/blog/components/hooks/use-pin-mutations'; +import NoDataError from '@/blog/components/no-data-error'; +import ChangeTitleDialog from '@/blog/features/community-profile/change-title-dialog'; +import DetailsCardHover from '@/blog/features/list-of-posts/details-card-hover'; +import ReblogTrigger from '@/blog/features/list-of-posts/reblog-trigger'; +import { useDeletePostMutation } from '@/blog/features/post-editor/hooks/use-post-mutation'; +import { postClassName } from '@/blog/features/post-editor/lib/utils'; +import PostForm from '@/blog/features/post-editor/post-form'; +import PostingLoader from '@/blog/features/post-editor/posting-loader'; +import { ReplyTextbox } from '@/blog/features/post-editor/reply-textbox'; +import { AlertDialogFlag } from '@/blog/features/post-rendering/alert-window-flag'; +import CommentList from '@/blog/features/post-rendering/comment-list'; +import CommentSelectFilter from '@/blog/features/post-rendering/comment-select-filter'; +import ContextLinks from '@/blog/features/post-rendering/context-links'; +import DetailsCardVoters from '@/blog/features/post-rendering/details-card-voters'; +import FlagIcon from '@/blog/features/post-rendering/flag-icon'; +import ImageGallery from '@/blog/features/post-rendering/image-gallery'; +import MutePostDialog from '@/blog/features/post-rendering/mute-post-dialog'; +import { PostDeleteDialog } from '@/blog/features/post-rendering/post-delete-dialog'; +import RendererContainer from '@/blog/features/post-rendering/rendererContainer'; +import { SharePost } from '@/blog/features/post-rendering/share-post-dialog'; +import FacebookShare from '@/blog/features/post-rendering/share-post-facebook'; +import LinkedInShare from '@/blog/features/post-rendering/share-post-linkedin'; +import RedditShare from '@/blog/features/post-rendering/share-post-reddit'; +import TwitterShare from '@/blog/features/post-rendering/share-post-twitter'; +import UserInfo from '@/blog/features/post-rendering/user-info'; +import { UserPopoverCard } from '@/blog/features/post-rendering/user-popover-card'; +import AnimatedList from '@/blog/features/suggestions-posts/animated-tab'; +import SuggestionsList from '@/blog/features/suggestions-posts/list'; +import VotesComponent from '@/blog/features/votes/votes-component'; +import { useTranslation } from '@/blog/i18n/client'; +import sorter, { SortOrder } from '@/blog/lib/sorter'; +import { DEFAULT_OBSERVER } from '@/blog/lib/utils'; +import { getBasePath } from '@/blog/utils/PathUtils'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import { useQuery } from '@tanstack/react-query'; +import { getCommunity, getDiscussion, getListCommunityRoles, getPost } from '@transaction/lib/bridge-api'; +import { Entry } from '@transaction/lib/extended-hive.chain'; +import { getActiveVotes } from '@transaction/lib/hive-api'; +import { getSimilarPostsByPost, isPostStub } from '@transaction/lib/hivesense-api'; +import { Badge } from '@ui/components/badge'; +import { Button } from '@ui/components/button'; +import { Icons } from '@ui/components/icons'; +import Loading from '@ui/components/loading'; +import { Separator } from '@ui/components/separator'; +import TimeAgo from '@ui/components/time-ago'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@ui/components/tooltip'; +import dmcaList from '@ui/config/lists/dmca-list'; +import dmcaUserList from '@ui/config/lists/dmca-user-list'; +import gdprUserList from '@ui/config/lists/gdpr-user-list'; +import userIllegalContent from '@ui/config/lists/user-illegal-content'; +import { handleError } from '@ui/lib/handle-error'; +import parseDate from '@ui/lib/parse-date'; +import clsx from 'clsx'; +import { Clock, Link2 } from 'lucide-react'; +import moment from 'moment'; +import Link from 'next/link'; +import { useParams, usePathname, useRouter, useSearchParams } from 'next/navigation'; +import { useEffect, useMemo, useState } from 'react'; +import { CircleSpinner } from 'react-spinners-kit'; +import { useLocalStorage } from 'usehooks-ts'; + +const PostContent = () => { + const searchParams = useSearchParams(); + const params = useParams<{ param: string; p2: string; permlink: string }>(); + const router = useRouter(); + const pathname = usePathname(); + const commentSort = searchParams?.get('sort') || 'trending'; + const author = params?.p2.replace('%40', '') ?? ''; + const category = params?.param ?? ''; + const permlink = params?.permlink ?? ''; + const { user } = useUser(); + const storageId = `replybox-/${author}/${permlink}-${user.username}`; + + const { t } = useTranslation('common_blog'); + const [storedBox, storeBox, removeBox] = useLocalStorage(storageId, false); + const [storedComment] = useLocalStorage(`replyTo-/${author}/${permlink}-${user.username}`, ''); + + const [reply, setReply] = useState(storedBox !== undefined ? storedBox : false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [edit, setEdit] = useState(false); + const observer = user.isLoggedIn ? user.username : DEFAULT_OBSERVER; + const postInCommunity = category?.startsWith('hive-'); + const { data: postData, isLoading: postIsLoading } = useQuery({ + queryKey: ['postData', author, permlink], + queryFn: () => getPost(author, permlink, observer), + enabled: !!author && !!permlink + }); + const [mutedPost, setMutedPost] = useState(postData?.stats?.gray || false); + const userFromGDPR = gdprUserList.some((e) => e === postData?.author); + + const crossedPost = postData?.json_metadata.tags?.includes('cross-post'); + const legalBlockedUser = userIllegalContent.some((e) => e === postData?.author); + const copyRightCheck = dmcaList.includes(pathname ?? ''); + const { data: crossPostData } = useQuery({ + queryKey: [ + 'postData', + postData?.json_metadata.original_author, + postData?.json_metadata.original_permlink + ], + queryFn: () => + getPost(postData?.json_metadata.original_author, postData?.json_metadata.original_permlink, observer), + enabled: crossedPost + }); + + const { data: suggestionData } = useQuery({ + queryKey: ['suggestions', author, permlink], + queryFn: async () => { + const results = await getSimilarPostsByPost({ + author, + permlink, + observer, + result_limit: 10, // Only get 10 suggestions + full_posts: 10 // Get all as full posts + }); + + if (!results) return null; + + // Filter out null/invalid posts and only include full Entry objects (not stubs) + const fullPosts = results.filter( + (post) => post && !isPostStub(post) && (post as Entry).post_id + ) as Entry[]; + return fullPosts; + } + }); + const { data: communityData } = useQuery({ + queryKey: ['community', category], + queryFn: () => getCommunity(category, observer), + enabled: postInCommunity + }); + + const { data: discussionData, isLoading: discussionIsLoading } = useQuery({ + queryKey: ['discussionData', permlink], + queryFn: () => getDiscussion(author, permlink, observer) + }); + const discussionState = useMemo(() => { + if (!discussionData) return undefined; + const list = [...Object.keys(discussionData).map((key) => discussionData[key])]; + const sortType = commentSort as SortOrder; + sorter(list, sortType); + return list; + }, [discussionData, commentSort]); + const firstPost = discussionState?.find((post) => post.depth === 0); + const post_is_pinned = firstPost?.stats?.is_pinned ?? false; + + const thisPost = discussionState?.find((post) => post.permlink === permlink && postData?.author === author); + const commentSite = thisPost?.depth !== 0 ? true : false; + const userFromDMCA = dmcaUserList.some((e) => e === postData?.author); + + const { data: userCanModerate } = useQuery({ + queryKey: ['rolesList', category], + queryFn: () => getListCommunityRoles(category), + enabled: postInCommunity, + select: (data) => { + const userRole = data?.find((e) => e[0] === user.username); + const userCanModerate = userRole + ? userRole[1] === 'mod' || userRole[1] === 'admin' || userRole[1] === 'owner' + : false; + return userCanModerate; + } + }); + + const { data: activeVotesData } = useQuery({ + queryKey: ['activeVotes'], + queryFn: () => getActiveVotes(author, permlink) + }); + + const { data: mutedList } = useFollowListQuery(user.username, 'muted'); + + const pinMutations = usePinMutation(); + const unpinMutation = useUnpinMutation(); + + const pin = async () => { + try { + await pinMutations.mutateAsync({ community: category, username: author, permlink }); + } catch (error) { + handleError(error, { method: 'pin', params: { community: category, username: author, permlink } }); + } + }; + const unpin = async () => { + try { + await unpinMutation.mutateAsync({ community: category, username: author, permlink }); + } catch (error) { + handleError(error, { method: 'unpin', params: { community: category, username: author, permlink } }); + } + }; + + const deletePostMutation = useDeletePostMutation(); + const basepath = getBasePath(); + const deleteComment = async (permlink: string) => { + try { + await deletePostMutation.mutateAsync({ permlink }); + setIsSubmitting(true); + // Wait 2 seconds before redirecting + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Use window.location for subdirectory deployments to ensure catch-all route works + if (!!basepath) { + window.location.href = `${basepath}/@${author}/posts`; + } else { + // Use client-side navigation for root deployments (faster) + router.push(`/@${author}/posts`); + } + } catch (error) { + setIsSubmitting(false); + handleError(error, { method: 'deleteComment', params: { permlink } }); + } + }; + + useEffect(() => { + setMutedPost(postData?.stats?.gray ?? false); + }, [postData?.stats?.gray]); + + useEffect(() => { + if (reply) { + storeBox(reply); + } + }, [reply, storeBox]); + if (userFromGDPR || (!postData && !postIsLoading)) return ; + + return ( + <> +
+
+ {suggestionData ? : null} +
+
+
+ {crossedPost ? ( +
+ + + + {postData?.author}{' '} + + cross-posted{' '} + + this post{' '} + + in{' '} + + {postData?.community_title ?? postData?.community} + + +
+ ) : null} +
+ {postInCommunity && !user.isLoggedIn ? ( + + {}} /> + + ) : communityData && user.isLoggedIn ? ( + + {}} /> + + ) : null} +
+ + {postData ? ( +
+ {!commentSite ? ( +

+ {postData.title} +

+ ) : ( + e.depth === 1)} + /> + )} + + {postIsLoading ? ( + + ) : edit && commentSite && postData.parent_author && postData.parent_permlink ? ( + + ) : edit ? ( + { + router.replace(pathname || '/'); + }} + setIsSubmitting={setIsSubmitting} + /> + ) : legalBlockedUser ? ( +
{t('global.unavailable_for_legal_reasons')}
+ ) : copyRightCheck || userFromDMCA ? ( +
{t('post_content.body.copyright')}
+ ) : mutedPost ? ( + <> + +
+ {t('post_content.body.content_were_hidden')} + +
+ + ) : ( + + + + )} +
+ {!commentSite ? ( +
    + {postData.json_metadata.tags + ?.filter((e) => e !== postData.category && e !== '' && e !== postData.community) + .map((tag: string) => ( +
  • + + #{tag} + +
  • + ))} +
+ ) : null} +
+
+
+
+ + + + + {t('post_content.footer.in')} + + {postData.community_title ? ( + + {crossPostData?.community ?? postData.community_title} + + ) : ( + + #{postData.category} + + )} + + {t('post_content.footer.by')} +
+ + {postData.author_title ? ( + + {postData.author_title} + + + ) : ( + + )} +
+
+
+ + + + ${postData.payout?.toFixed(2)} + + + {activeVotesData ? ( + + {postData.stats?.total_votes && postData.stats?.total_votes !== 0 ? ( + + {postData.stats?.total_votes > 1 + ? t('post_content.footer.votes', { votes: postData.stats?.total_votes }) + : t('post_content.footer.vote')} + + ) : null} + + ) : null} +
+
+
+
+ + | + {user && user.isLoggedIn ? ( + <> + + {pinMutations.isPending || unpinMutation.isPending ? ( +
+ +
+ ) : userCanModerate && postData.depth === 0 ? ( +
+ {/* */} + {/* TODO swap two button to one when api return stats.is_pinned, + temprary use two button to unpin and pin + */} + + +
+ ) : null} + {userCanModerate ? ( + + ) : null} + + ) : ( + + + + )} + {postData.children === 0 && + user.isLoggedIn && + postData.author === user.username && + moment().format('YYYY-MM-DDTHH:mm:ss') < postData.payout_at ? ( + <> + | + { + deleteComment(permlink); + }} + label="Post" + > + + + + ) : null} + {user && user.isLoggedIn && postData.author === user.username && !edit ? ( + <> + | + + + ) : null} + | + + + + + {postData.children > 1 ? ( + + ) : ( + + )} + + + {postData.children} + + + +

+ {postData.children === 0 + ? t('post_content.footer.no_responses') + : postData.children === 1 + ? t('post_content.footer.response') + : t('post_content.footer.responses', { responses: postData.children })} +

+
+
+
+
+
+ + + + + + + +
+
+
+ {crossedPost ? ( +
+ + + +
+ ) : null} +
+ {!!suggestionData ? ( +
+

+ You Might Also Like +

+ +
+ ) : null} +
+
+ ) : ( + + )} +
+
+
+ {reply && postData && user.isLoggedIn ? ( + + ) : null} +
+ {!!discussionData && !!discussionState && !!postData ? ( +
+
+ {t('select_sort.sort_comments.sort')} + +
+ +
+ ) : ( + + )} +
+
+
+ + + ); +}; +export default PostContent; diff --git a/apps/blog/app/[param]/[p2]/[permlink]/page.tsx b/apps/blog/app/[param]/[p2]/[permlink]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e52c969e5f0b28e867b45fda0e1b1f5e21044240 --- /dev/null +++ b/apps/blog/app/[param]/[p2]/[permlink]/page.tsx @@ -0,0 +1,50 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import PostContent from './content'; +import { getCommunity, getListCommunityRoles, getPost } from '@transaction/lib/bridge-api'; +import { getDiscussion } from '@transaction/lib/bridge-api'; +import { getActiveVotes } from '@transaction/lib/hive-api'; +import { getObserverFromCookies } from '@/blog/lib/auth-utils'; + +const PostPage = async ({ + params: { param, p2, permlink } +}: { + params: { param: string; p2: string; permlink: string }; +}) => { + const queryClient = getQueryClient(); + const username = p2.replace('%40', ''); + const community = param; + const observer = getObserverFromCookies(); + await queryClient.prefetchQuery({ + queryKey: ['postData', username, permlink], + queryFn: () => getPost(username, permlink, observer) + }); + + await queryClient.prefetchQuery({ + queryKey: ['discussionData', permlink], + queryFn: () => getDiscussion(username, permlink, observer) + }); + + await queryClient.prefetchQuery({ + queryKey: ['activeVotes', username, permlink], + queryFn: () => getActiveVotes(username, permlink) + }); + + if (community.startsWith('hive-')) { + await queryClient.prefetchQuery({ + queryKey: ['communityData', community], + queryFn: () => getCommunity(community, observer) + }); + await queryClient.prefetchQuery({ + queryKey: ['rolesList', community], + queryFn: () => getListCommunityRoles(community) + }); + } + + return ( + + + + ); +}; +export default PostPage; diff --git a/apps/blog/app/[param]/[p2]/content.tsx b/apps/blog/app/[param]/[p2]/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a07950d8b3c6bc1a705313a6981ecf5f960a5732 --- /dev/null +++ b/apps/blog/app/[param]/[p2]/content.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { DEFAULT_OBSERVER } from '@/blog/lib/utils'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import { useQuery } from '@tanstack/react-query'; +import { getPost } from '@transaction/lib/bridge-api'; +import { useParams } from 'next/navigation'; +import { useEffect } from 'react'; + +const RedirectContent = () => { + const params = useParams<{ param: string; p2: string }>(); + const username = params?.param.replace('%40', ''); + const { user } = useUser(); + const observer = user.isLoggedIn ? user.username : DEFAULT_OBSERVER; + const { data } = useQuery({ + queryKey: ['postData', username, params?.p2], + queryFn: () => getPost(username, String(params?.p2), observer) + }); + + const url = `/${data?.category ?? data?.community}/@${data?.author}/${data?.permlink}`; + + useEffect(() => { + if (url) { + window.location.href = url; + } + }, [url]); + return ( +
+ Redirecting to post: + + {url} + +
+ ); +}; + +export default RedirectContent; diff --git a/apps/blog/app/[param]/[p2]/page.tsx b/apps/blog/app/[param]/[p2]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bf300cf1bbb715e26f34be640e6b039c1722131b --- /dev/null +++ b/apps/blog/app/[param]/[p2]/page.tsx @@ -0,0 +1,22 @@ +import { getQueryClient } from '@/blog/lib/react-query'; +import RedirectContent from './content'; +import { getPost } from '@transaction/lib/bridge-api'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import { getObserverFromCookies } from '@/blog/lib/auth-utils'; + +const Page = async ({ params: { param, p2 } }: { params: { param: string; p2: string } }) => { + const queryClient = getQueryClient(); + const username = param.replace('%40', ''); + const observer = getObserverFromCookies(); + await queryClient.prefetchQuery({ + queryKey: ['postData', username, p2], + queryFn: () => getPost(username, p2, observer) + }); + + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/[param]/feed/content.tsx b/apps/blog/app/[param]/feed/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2cdf3854b3fb1ff6c37b041877a8b2d0a7d30b70 --- /dev/null +++ b/apps/blog/app/[param]/feed/content.tsx @@ -0,0 +1,9 @@ +'use client'; + +import PostsContent from '@/blog/features/account-profile/posts-content'; + +const query = 'feed'; + +const Content = () => ; + +export default Content; diff --git a/apps/blog/app/[param]/feed/layout.tsx b/apps/blog/app/[param]/feed/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..01c7eff559832db7d8110a24229f0b92b80642b8 --- /dev/null +++ b/apps/blog/app/[param]/feed/layout.tsx @@ -0,0 +1,13 @@ +import PageLayout from '@/blog/features/layouts/main-page-layout'; +import ServerSideLayout from '@/blog/features/layouts/sorts/server-side-layout'; +import { ReactNode } from 'react'; + +const tag = 'feed'; + +const Layout = ({ children }: { children: ReactNode }) => ( + + {children} + +); + +export default Layout; diff --git a/apps/blog/app/[param]/feed/page.tsx b/apps/blog/app/[param]/feed/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3ed8728cfd71a284595311135d587da71e980fe4 --- /dev/null +++ b/apps/blog/app/[param]/feed/page.tsx @@ -0,0 +1,13 @@ +import PostsPage from '@/blog/features/account-profile/posts-page'; +import Content from './content'; + +const query = 'feed'; + +const Page = ({ params }: { params: { param: string } }) => { + return ( + + + + ); +}; +export default Page; diff --git a/apps/blog/app/communities/content.tsx b/apps/blog/app/communities/content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..55a307e89c7de9a22b5d15d574ff09a97c023fd2 --- /dev/null +++ b/apps/blog/app/communities/content.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { useState, KeyboardEvent } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { Input } from '@ui/components/input'; +import { Icons } from '@ui/components/icons'; +import { Separator } from '@ui/components/separator'; +import CommunitiesSelectFilter from '@/blog/features/communities-list/communities-select-filter'; +import CommunitiesList from '@/blog/features/communities-list/communities-list'; +import { useUser } from '@smart-signer/lib/auth/use-user'; +import Link from 'next/link'; +import env from '@beam-australia/react-env'; +import { getCommunities } from '@transaction/lib/bridge-api'; +import { useTranslation } from '@/blog/i18n/client'; +import { DEFAULT_OBSERVER } from '@/blog/lib/utils'; + +const CommunitiesContent = () => { + const walletHost = env('WALLET_ENDPOINT'); + const { t } = useTranslation('common_blog'); + const { user } = useUser(); + const [sort, setSort] = useState('rank'); + const [inputQuery, setInputQuery] = useState(''); + const [query, setQuery] = useState(); + const observer = user.isLoggedIn ? user.username : DEFAULT_OBSERVER; + const { data: communitiesData } = useQuery({ + queryKey: ['communitiesList', sort, query], + queryFn: async () => await getCommunities(sort, query, observer) + }); + + function handleSearchCommunity(e: KeyboardEvent) { + if (e.key === 'Enter') { + inputQuery !== '' ? setQuery(inputQuery) : setQuery(null); + } + } + + function handleChangeFilter(e: string) { + setSort(e); + } + + return ( + <> +
+ + {t('communities.communities')} + + {user.isLoggedIn ? ( + + Create a Community + + ) : null} +
+
+
+
+ +
+ setInputQuery(e.target.value)} + onKeyDown={(e) => handleSearchCommunity(e)} + /> +
+ +
+ + {communitiesData && communitiesData?.length > 0 ? ( + + ) : ( +
+ {t('communities.no_results')} +
+ )} + + ); +}; +export default CommunitiesContent; diff --git a/apps/blog/app/communities/layout.tsx b/apps/blog/app/communities/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..55584cd85c8417b3e5d3cf10ed0f11592300468e --- /dev/null +++ b/apps/blog/app/communities/layout.tsx @@ -0,0 +1,13 @@ +import PageLayout from '@/blog/features/layouts/main-page-layout'; +import ServerSideLayout from '@/blog/features/layouts/sorts/server-side-layout'; +import { ReactNode } from 'react'; + +const Layout = async ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); +}; + +export default Layout; diff --git a/apps/blog/app/communities/page.tsx b/apps/blog/app/communities/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bc706c27e327ef924044d8d34fbef2d5db357d25 --- /dev/null +++ b/apps/blog/app/communities/page.tsx @@ -0,0 +1,26 @@ +import CommunitiesContent from './content'; +import { getQueryClient } from '@/blog/lib/react-query'; +import { dehydrate, Hydrate } from '@tanstack/react-query'; +import { getCommunities } from '@transaction/lib/bridge-api'; +import { getObserverFromCookies } from '@/blog/lib/auth-utils'; + +const sort = 'rank'; +const query = undefined; + +const CommunitiesPage = async () => { + const observer = getObserverFromCookies(); + const queryClient = getQueryClient(); + + await queryClient.prefetchQuery({ + queryKey: ['communitiesList', sort, query], + queryFn: async () => await getCommunities(sort, query, observer) + }); + + return ( + + + + ); +}; + +export default CommunitiesPage; diff --git a/apps/blog/app/faq.html/page.tsx b/apps/blog/app/faq.html/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1ca108cf9f58e6c9b29ac55c8a810fafd7367fc3 --- /dev/null +++ b/apps/blog/app/faq.html/page.tsx @@ -0,0 +1,5 @@ +import StaticContent from '@/blog/features/static-pages/content-component'; + +const FAQPage = () => ; + +export default FAQPage; diff --git a/apps/blog/app/layout.tsx b/apps/blog/app/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a823dccf7436065535394d8f0ca8eb55e588d169 --- /dev/null +++ b/apps/blog/app/layout.tsx @@ -0,0 +1,63 @@ +import '@hive/tailwindcss-config/globals.css'; +import { ReactNode } from 'react'; +import Script from 'next/script'; +import { Metadata } from 'next'; +import { cookies } from 'next/headers'; +import MainBar from '../features/layouts/site-header/main-bar'; +import ClientEffects from '../features/layouts/site-header/client-effects'; +import { Providers } from '../features/layouts/providers'; + +// Get basePath from build-time environment +const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''; + +const SITE_DESC = + 'Communities without borders. A social network owned and operated by its users, powered by Hive.'; + +export const metadata: Metadata = { + title: 'Hive', + description: SITE_DESC, + icons: { + icon: '/favicon.ico' + }, + openGraph: { + type: 'website', + siteName: 'Hive', + title: 'Hive', + description: SITE_DESC, + images: ['https://hive.blog/images/hive-blog-share.png'] + }, + twitter: { + card: 'summary', + site: '@hiveblocks', + title: '#Hive.io', + description: SITE_DESC, + images: ['https://hive.blog/images/hive-blog-twshare.png'] + }, + other: { + 'fb:app_id': 'YOUR_FB_APP_ID' + } +}; + +export default async function RootLayout({ children }: { children: ReactNode }) { + // Server-side locale and language handling + const cookieStore = cookies(); + const locale = cookieStore.get('NEXT_LOCALE')?.value || 'en'; + const isRTL = locale === 'ar'; + + return ( + + +
+ + <> + +
{children}
+ +
+
+