From fb04dec94b4d60b9f455df79e975314d8bfaf1b5 Mon Sep 17 00:00:00 2001 From: teodorus-nathaniel Date: Wed, 13 Mar 2024 00:33:13 +0700 Subject: [PATCH] Add post view query --- src/components/posts/view-post/PostPage.tsx | 4 +- .../posts/view-post/PostViewCount.tsx | 17 +++++++ .../posts/view-post/helpers.module.sass | 3 ++ src/components/posts/view-post/helpers.tsx | 6 ++- src/components/spaces/ViewSpacePage.tsx | 4 +- src/components/utils/datahub/post-view.ts | 41 ++++++++++++++++- src/rtk/app/rootReducer.ts | 2 + src/rtk/features/posts/postsHooks.ts | 5 +++ src/rtk/features/posts/postsSlice.ts | 2 + src/rtk/features/posts/postsViewCountSlice.ts | 45 +++++++++++++++++++ 10 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/components/posts/view-post/PostViewCount.tsx create mode 100644 src/rtk/features/posts/postsViewCountSlice.ts diff --git a/src/components/posts/view-post/PostPage.tsx b/src/components/posts/view-post/PostPage.tsx index c7609b4bd..4e0da85c3 100644 --- a/src/components/posts/view-post/PostPage.tsx +++ b/src/components/posts/view-post/PostPage.tsx @@ -29,6 +29,7 @@ import { useAppDispatch, useAppSelector } from 'src/rtk/app/store' import { fetchPostRewards } from 'src/rtk/features/activeStaking/postRewardSlice' import { fetchTopUsersWithSpaces } from 'src/rtk/features/leaderboard/topUsersSlice' import { fetchPost, fetchPosts, selectPost } from 'src/rtk/features/posts/postsSlice' +import { fetchPostsViewCount } from 'src/rtk/features/posts/postsViewCountSlice' import { useFetchMyReactionsByPostId } from 'src/rtk/features/reactions/myPostReactionsHooks' import { asCommentStruct, HasStatusCode, idToBn, PostData, PostWithSomeDetails } from 'src/types' import { DfImage } from '../../utils/DfImage' @@ -336,7 +337,8 @@ const PostPage: FC = props => { const { statusCode, postData } = props const dispatch = useAppDispatch() useEffect(() => { - dispatch(fetchPostRewards({ postIds: [postData.id] as string[] })) + dispatch(fetchPostRewards({ postIds: [postData.id] })) + dispatch(fetchPostsViewCount({ postIds: [postData.id] })) }, [dispatch]) if (statusCode === 404) { diff --git a/src/components/posts/view-post/PostViewCount.tsx b/src/components/posts/view-post/PostViewCount.tsx new file mode 100644 index 000000000..ecf732d16 --- /dev/null +++ b/src/components/posts/view-post/PostViewCount.tsx @@ -0,0 +1,17 @@ +import { ComponentProps } from 'react' +import { FaRegEye } from 'react-icons/fa' +import { IconWithLabel } from 'src/components/utils' +import { usePostViewCount } from 'src/rtk/app/hooks' + +export default function PostViewCount({ + postId, + ...props +}: { postId: string } & ComponentProps<'div'>) { + const viewCount = usePostViewCount(postId) + + return ( +
+ } count={viewCount} /> +
+ ) +} diff --git a/src/components/posts/view-post/helpers.module.sass b/src/components/posts/view-post/helpers.module.sass index 8c3ebecc8..6b025c390 100644 --- a/src/components/posts/view-post/helpers.module.sass +++ b/src/components/posts/view-post/helpers.module.sass @@ -2,6 +2,9 @@ .PostActions gap: $space_large + display: grid + width: 100% + grid-template-columns: 1fr 1fr 1fr 1fr @media screen and ( max-width: 768px) gap: $space_normal diff --git a/src/components/posts/view-post/helpers.tsx b/src/components/posts/view-post/helpers.tsx index d90b0001f..b7f5fe6e9 100644 --- a/src/components/posts/view-post/helpers.tsx +++ b/src/components/posts/view-post/helpers.tsx @@ -50,6 +50,7 @@ import ViewPostLink from '../ViewPostLink' import styles from './helpers.module.sass' import { PostDropDownMenu } from './PostDropDownMenu' import PostRewardStat from './PostRewardStat' +import PostViewCount from './PostViewCount' import TwitterPost from './TwitterPost' type IsUnlistedPostProps = { @@ -336,11 +337,12 @@ export const PostActionsPanel: FC = props => { return (
-
+
{preview && } + +
- {/* */}
) diff --git a/src/components/spaces/ViewSpacePage.tsx b/src/components/spaces/ViewSpacePage.tsx index 9fdce6303..bdfb4d411 100644 --- a/src/components/spaces/ViewSpacePage.tsx +++ b/src/components/spaces/ViewSpacePage.tsx @@ -8,6 +8,7 @@ import { useAppDispatch } from 'src/rtk/app/store' import { fetchPostRewards } from 'src/rtk/features/activeStaking/postRewardSlice' import { useFetchMyPermissionsBySpaceId } from 'src/rtk/features/permissions/mySpacePermissionsHooks' import { fetchPosts, selectPosts } from 'src/rtk/features/posts/postsSlice' +import { fetchPostsViewCount } from 'src/rtk/features/posts/postsViewCountSlice' import { fetchProfileSpace, selectProfileSpace } from 'src/rtk/features/profiles/profilesSlice' import { DataSourceTypes, HasStatusCode, idToBn, SpaceContent } from 'src/types' import { descSort, isPolkaProject, isUnclaimedSpace } from 'src/utils' @@ -81,7 +82,8 @@ const ViewSpacePage: FC = props => { const dispatch = useAppDispatch() useEffect(() => { - dispatch(fetchPostRewards({ postIds: prefetchedIds as string[] })) + dispatch(fetchPostRewards({ postIds: prefetchedIds })) + dispatch(fetchPostsViewCount({ postIds: prefetchedIds })) }, [dispatch]) if (statusCode === 404) { diff --git a/src/components/utils/datahub/post-view.ts b/src/components/utils/datahub/post-view.ts index 6a1f2ce0f..01309bc6a 100644 --- a/src/components/utils/datahub/post-view.ts +++ b/src/components/utils/datahub/post-view.ts @@ -1,6 +1,45 @@ import { SocialCallDataArgs, socialCallName } from '@subsocial/data-hub-sdk' import axios from 'axios' -import { createSocialDataEventPayload, DatahubParams } from './utils' +import gql from 'graphql-tag' +import { PostViewCount } from 'src/rtk/features/posts/postsViewCountSlice' +import { createSocialDataEventPayload, DatahubParams, datahubQueryRequest } from './utils' + +// QUERIES +const GET_POSTS_VIEW_COUNT = gql` + query GetPostsViewCount($postIds: [String!]!) { + postsViewsCounts(where: { persistentIds: $postIds }) { + postPersistentId + viewsCountTotal + } + } +` +export async function getPostsViewCount(postIds: string[]): Promise { + const res = await datahubQueryRequest< + { + postsViewsCounts: { postPersistentId: string; viewsCountTotal: number }[] + }, + { postIds: string[] } + >({ + query: GET_POSTS_VIEW_COUNT, + variables: { postIds }, + }) + + const resultMap = new Map() + res.data.postsViewsCounts.forEach(item => + resultMap.set(item.postPersistentId, { + postId: item.postPersistentId, + viewsCount: item.viewsCountTotal, + }), + ) + + return postIds.map( + postId => + resultMap.get(postId) ?? { + postId, + viewsCount: 0, + }, + ) +} // MUTATIONS export async function addPostView( diff --git a/src/rtk/app/rootReducer.ts b/src/rtk/app/rootReducer.ts index d8af038f4..44b563146 100644 --- a/src/rtk/app/rootReducer.ts +++ b/src/rtk/app/rootReducer.ts @@ -29,6 +29,7 @@ import mySpacePermissions from '../features/permissions/mySpacePermissionsSlice' import myFeed from '../features/posts/myFeedSlice' import ownPostIds from '../features/posts/ownPostIdsSlice' import posts from '../features/posts/postsSlice' +import postsViewCount from '../features/posts/postsViewCountSlice' import followedAccountIds from '../features/profiles/followedAccountIdsSlice' import profileSpaces from '../features/profiles/profilesSlice' import myPostReactions from '../features/reactions/myPostReactionsSlice' @@ -79,6 +80,7 @@ const rootReducer = combineReducers({ leaderboard, prevReward, superLikeMessage, + postsViewCount, }) export type RootState = ReturnType diff --git a/src/rtk/features/posts/postsHooks.ts b/src/rtk/features/posts/postsHooks.ts index 4ba3ee7cc..98acea88e 100644 --- a/src/rtk/features/posts/postsHooks.ts +++ b/src/rtk/features/posts/postsHooks.ts @@ -15,6 +15,7 @@ import { selectPostStructById, upsertPost, } from './postsSlice' +import { selectPostViewCount } from './postsViewCountSlice' export function useIsMuted(address: string) { const myAddress = useMyAddress() ?? '' @@ -102,3 +103,7 @@ export function useFilterLowValuePosts(postIds: string[]) { return { filtered, filteredCount } }, [postIds.join(','), myAddress]) } + +export function usePostViewCount(postId: string) { + return useAppSelector(state => selectPostViewCount(state, postId)?.viewsCount ?? 0) +} diff --git a/src/rtk/features/posts/postsSlice.ts b/src/rtk/features/posts/postsSlice.ts index db2299608..21210fb27 100644 --- a/src/rtk/features/posts/postsSlice.ts +++ b/src/rtk/features/posts/postsSlice.ts @@ -48,6 +48,7 @@ import { fetchSuperLikeCounts } from '../activeStaking/superLikeCountsSlice' import { Content, fetchContents, selectPostContentById } from '../contents/contentsSlice' import { fetchProfileSpaces } from '../profiles/profilesSlice' import { fetchSpaces } from '../spaces/spacesSlice' +import { fetchPostsViewCount } from './postsViewCountSlice' export interface PostState extends PostStruct { isOverview?: boolean } @@ -253,6 +254,7 @@ export const fetchPosts = createAsyncThunk[] = [ dispatch(fetchSuperLikeCounts({ postIds: newIds as string[] })), diff --git a/src/rtk/features/posts/postsViewCountSlice.ts b/src/rtk/features/posts/postsViewCountSlice.ts new file mode 100644 index 000000000..78dc5a928 --- /dev/null +++ b/src/rtk/features/posts/postsViewCountSlice.ts @@ -0,0 +1,45 @@ +import { createEntityAdapter, createSlice } from '@reduxjs/toolkit' +import { getPostsViewCount } from 'src/components/utils/datahub/post-view' +import { RootState } from 'src/rtk/app/rootReducer' +import { createSimpleManyFetchWrapper } from 'src/rtk/app/wrappers' + +export type PostViewCount = { + postId: string + viewsCount: number +} + +const sliceName = 'postsViewCount' + +const adapter = createEntityAdapter({ + selectId: data => data.postId, +}) +const selectors = adapter.getSelectors(state => state.postsViewCount) + +export const selectPostViewCount = selectors.selectById + +export const fetchPostsViewCount = createSimpleManyFetchWrapper< + { postIds: string[] }, + PostViewCount +>({ + sliceName, + fetchData: async function ({ postIds }) { + return await getPostsViewCount(postIds) + }, + getCachedData: (state, id) => selectPostViewCount(state, id), + saveToCacheAction: data => slice.actions.setPostsViewCount(data), + shouldFetchCondition: ({ postIds }) => postIds?.length !== 0, + filterNewArgs: ({ postIds }, isNewId) => { + const newPostIds = postIds?.filter(postId => isNewId(postId)) + return { postIds: newPostIds } + }, +}) + +const slice = createSlice({ + name: sliceName, + initialState: adapter.getInitialState(), + reducers: { + setPostsViewCount: adapter.upsertMany, + }, +}) + +export default slice.reducer