Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Post Reward #167

Merged
merged 9 commits into from
Jan 12, 2024
Merged
34 changes: 34 additions & 0 deletions src/components/posts/view-post/PostRewardStat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import clsx from 'clsx'
import { ComponentProps } from 'react'
import { TbCoins } from 'react-icons/tb'
import { FormatBalance } from 'src/components/common/balances'
import { useSelectPostReward } from 'src/rtk/features/activeStaking/hooks'

export type PostRewardStatProps = ComponentProps<'div'> & { postId: string }

export default function PostRewardStat({ postId, ...props }: PostRewardStatProps) {
const postEarn = useSelectPostReward(postId)
if (!postEarn?.isNotZero) return null

return (
<div
{...props}
className={clsx('d-flex align-items-center GapMini FontWeightMedium', props.className)}
style={{ alignSelf: 'end', ...props.style }}
>
<span className='d-flex align-items-center GapMini ColorMuted'>
<TbCoins className='FontNormal' />
<span>Post earned:</span>
</span>
<span className='FontWeightSemibold'>
<FormatBalance
currency='SUB'
decimals={10}
precision={2}
withMutedDecimals={false}
value={postEarn.amount}
/>
</span>
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/posts/view-post/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import ViewTags from '../../utils/ViewTags'
import Embed from '../embed/Embed'
import ViewPostLink from '../ViewPostLink'
import { PostDropDownMenu } from './PostDropDownMenu'
import PostRewardStat from './PostRewardStat'
import TwitterPost from './TwitterPost'

type IsUnlistedPostProps = {
Expand Down Expand Up @@ -297,6 +298,7 @@ export const PostActionsPanel: FC<PostActionsPanelProps> = props => {
return (
<div className={`DfActionsPanel ${withBorder && 'DfActionBorder'} ${className ?? ''}`}>
<ReactionsAction />
<PostRewardStat postId={postDetails.id} />
{/* <ShareDropdown postDetails={postDetails} space={space} className='DfAction' /> */}
</div>
)
Expand Down
35 changes: 35 additions & 0 deletions src/components/utils/datahub/super-likes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
fetchAddressLikeCounts,
} from 'src/rtk/features/activeStaking/addressLikeCountSlice'
import { CanPostSuperLiked } from 'src/rtk/features/activeStaking/canPostSuperLikedSlice'
import { PostRewards } from 'src/rtk/features/activeStaking/postRewardSlice'
import { RewardHistory } from 'src/rtk/features/activeStaking/rewardHistorySlice'
import { fetchRewardReport, RewardReport } from 'src/rtk/features/activeStaking/rewardReportSlice'
import {
Expand Down Expand Up @@ -64,6 +65,40 @@ export async function getSuperLikeCounts(postIds: string[]): Promise<SuperLikeCo
return postIds.map(postId => resultMap.get(postId) ?? { postId, count: 0 })
}

const GET_POST_REWARDS = gql`
query GetPostRewards($postIds: [String!]!) {
activeStakingRewardsByPosts(args: { postPersistentIds: $postIds }) {
persistentPostId
amount
}
}
`
export async function getPostRewards(postIds: string[]): Promise<PostRewards[]> {
const res = await datahubQueryRequest<
{
activeStakingRewardsByPosts: {
persistentPostId: string
amount: string
}[]
},
{ postIds: string[] }
>({
document: GET_POST_REWARDS,
variables: { postIds },
})

const resultMap = new Map<string, PostRewards>()
res.activeStakingRewardsByPosts.forEach(item =>
resultMap.set(item.persistentPostId, {
postId: item.persistentPostId,
amount: item.amount,
isNotZero: BigInt(item.amount) > 0,
}),
)

return postIds.map(postId => resultMap.get(postId) ?? { postId, amount: '0', isNotZero: false })
}

const GET_ADDRESS_LIKE_COUNT_TO_POSTS = gql`
query GetAddressLikeCountToPosts($address: String!, $postIds: [String!]!) {
activeStakingSuperLikeCountsByStaker(args: { postPersistentIds: $postIds, address: $address }) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/voting/SuperLike.module.sass
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
.SuperLike
background: #F8FAFC
color: #7779F3
padding: 0.2em $space_small
padding: $space_mini $space_small
height: auto
display: flex
align-items: center
border: none
Expand Down
2 changes: 2 additions & 0 deletions src/rtk/app/rootReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import myAccount from '../features/accounts/myAccountSlice'
import spaceEditors from '../features/accounts/spaceEditorsSlice'
import addressLikeCount from '../features/activeStaking/addressLikeCountSlice'
import canPostSuperLiked from '../features/activeStaking/canPostSuperLikedSlice'
import postReward from '../features/activeStaking/postRewardSlice'
import rewardHistory from '../features/activeStaking/rewardHistorySlice'
import rewardReport from '../features/activeStaking/rewardReportSlice'
import superLikeCounts from '../features/activeStaking/superLikeCountsSlice'
Expand Down Expand Up @@ -65,6 +66,7 @@ const rootReducer = combineReducers({
rewardReport,
rewardHistory,
canPostSuperLiked,
postReward,
})

export type RootState = ReturnType<typeof rootReducer>
Expand Down
51 changes: 51 additions & 0 deletions src/rtk/app/wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,54 @@ export function createSimpleFetchWrapper<Args, ReturnValue>({
},
)
}

export function createSimpleManyFetchWrapper<Args, ReturnValue>({
sliceName,
fetchData,
saveToCacheAction,
getCachedData,
filterNewArgs,
shouldFetchCondition,
}: {
sliceName: string
saveToCacheAction: (data: ReturnValue[]) => any
getCachedData: (state: RootState, id: string) => ReturnValue | undefined
fetchData: (args: Args, state: RootState) => Promise<ReturnValue[]>
filterNewArgs: (args: Args, isNewId: (id: string) => boolean) => Args
shouldFetchCondition: (filteredArgs: Args) => boolean
}) {
const currentlyFetchingMap = new Map<string, Promise<ReturnValue[]>>()
return createAsyncThunk<ReturnValue[], Args & { reload?: boolean }, ThunkApiConfig>(
`${sliceName}/fetchMany`,
async (allArgs, { getState, dispatch }): Promise<ReturnValue[]> => {
const { reload } = allArgs

let filteredArgs: Args = allArgs
let shouldFetchIds: string[] = []
if (!reload) {
filteredArgs = filterNewArgs(allArgs, id => {
const shouldFetch = !currentlyFetchingMap.get(id) && !getCachedData(getState(), id)
if (!shouldFetch) return false

shouldFetchIds.push(id)
return true
})
}

if (!shouldFetchCondition(filteredArgs)) return []

const promise = fetchData(filteredArgs, getState())
shouldFetchIds.forEach(id => {
currentlyFetchingMap.set(id, promise)
})
const res = await promise

await dispatch(saveToCacheAction(res))
shouldFetchIds.forEach(id => {
currentlyFetchingMap.delete(id)
})

return promise
},
)
}
46 changes: 13 additions & 33 deletions src/rtk/features/activeStaking/addressLikeCountSlice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
import { getAddressLikeCountToPosts } from 'src/components/utils/datahub/super-likes'
import { RootState } from 'src/rtk/app/rootReducer'
import { createSimpleFetchWrapper } from 'src/rtk/app/wrappers'
import { createSimpleManyFetchWrapper } from 'src/rtk/app/wrappers'

export type AddressLikeCount = {
address: string
Expand All @@ -28,48 +28,28 @@ export const selectAllAddressLikeCounts = selectors.selectEntities
function getAllPostIdsFromStore(state: RootState) {
return state.posts.ids as string[]
}
export const fetchAddressLikeCounts = createSimpleFetchWrapper<
export const fetchAddressLikeCounts = createSimpleManyFetchWrapper<
{ postIds: string[] | null; address: string },
AddressLikeCount[]
AddressLikeCount
>({
sliceName,
fetchData: async function ({ postIds, address }, state) {
if (postIds === null) {
postIds = getAllPostIdsFromStore(state)
}
const entities = selectAllAddressLikeCounts(state)
const newIds = postIds.filter(postId => {
const id = getAddressLikeCountId({ address, postId })
return !entities[id]
})
if (!newIds.length) return []
return await getAddressLikeCountToPosts(address, postIds)
},
getCachedData: (state, id) => selectAddressLikeCount(state, id),
saveToCacheAction: data => slice.actions.setAddressLikeCounts(data),
getCachedData: (state, { postIds, address }) => {
if (postIds === null) {
postIds = getAllPostIdsFromStore(state)
}

const entities = selectAllAddressLikeCounts(state)
let isEveryDataCached = true

const postEntities: AddressLikeCount[] = []
for (let i = 0; i < postIds.length; i++) {
const id = getAddressLikeCountId({ address, postId: postIds[i] })
if (!entities[id]) {
isEveryDataCached = false
break
} else {
postEntities.push(entities[id]!)
}
}

if (isEveryDataCached) {
return postEntities
}
return undefined
shouldFetchCondition: ({ address, postIds }) => postIds?.length !== 0 && !!address,
filterNewArgs: ({ address, postIds }, isNewId) => {
// if postIds is null, fetch all postIds
if (postIds === null) return { address, postIds }
const newPostIds = postIds?.filter(postId =>
isNewId(getAddressLikeCountId({ address, postId })),
)
return { address, postIds: newPostIds }
},
sliceName,
})

const slice = createSlice({
Expand Down
32 changes: 9 additions & 23 deletions src/rtk/features/activeStaking/canPostSuperLikedSlice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
import { getCanPostsSuperLiked } from 'src/components/utils/datahub/super-likes'
import { RootState } from 'src/rtk/app/rootReducer'
import { createSimpleFetchWrapper } from 'src/rtk/app/wrappers'
import { createSimpleManyFetchWrapper } from 'src/rtk/app/wrappers'

export type CanPostSuperLiked = {
postId: string
Expand All @@ -18,35 +18,21 @@ const selectors = adapter.getSelectors<RootState>(state => state.canPostSuperLik
export const selectCanPostSuperLiked = selectors.selectById
export const selectAllCanPostSuperLiked = selectors.selectEntities

export const fetchCanPostsSuperLiked = createSimpleFetchWrapper<
export const fetchCanPostsSuperLiked = createSimpleManyFetchWrapper<
{ postIds: string[] },
CanPostSuperLiked[]
CanPostSuperLiked
>({
sliceName,
fetchData: async function ({ postIds }) {
return await getCanPostsSuperLiked(postIds)
},
getCachedData: (state, id) => selectCanPostSuperLiked(state, id),
saveToCacheAction: data => slice.actions.setCanPostsSuperLiked(data),
getCachedData: (state, { postIds }) => {
const entities = selectAllCanPostSuperLiked(state)
let isEveryDataCached = true

const queriedEntities: CanPostSuperLiked[] = []
for (let i = 0; i < postIds.length; i++) {
const postId = postIds[i]
if (!entities[postId]) {
isEveryDataCached = false
break
} else {
queriedEntities.push(entities[postId]!)
}
}

if (isEveryDataCached) {
return queriedEntities
}
return undefined
shouldFetchCondition: ({ postIds }) => postIds?.length !== 0,
filterNewArgs: ({ postIds }, isNewId) => {
const newPostIds = postIds?.filter(postId => isNewId(postId))
return { postIds: newPostIds }
},
sliceName,
})

const slice = createSlice({
Expand Down
5 changes: 5 additions & 0 deletions src/rtk/features/activeStaking/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
selectAddressLikeCount,
} from './addressLikeCountSlice'
import { selectCanPostSuperLiked } from './canPostSuperLikedSlice'
import { selectPostReward } from './postRewardSlice'
import { fetchRewardHistory, selectUserRewardHistory } from './rewardHistorySlice'
import { fetchRewardReport, selectUserRewardReport } from './rewardReportSlice'
import { selectPostSuperLikeCount } from './superLikeCountsSlice'
Expand Down Expand Up @@ -75,3 +76,7 @@ export function useFetchUserRewardHistory(address?: string, config?: { enabled?:
data,
}
}

export function useSelectPostReward(postId: string) {
return useAppSelector(state => selectPostReward(state, postId))
}
43 changes: 43 additions & 0 deletions src/rtk/features/activeStaking/postRewardSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
import { getPostRewards } from 'src/components/utils/datahub/super-likes'
import { RootState } from 'src/rtk/app/rootReducer'
import { createSimpleManyFetchWrapper } from 'src/rtk/app/wrappers'

export type PostRewards = {
postId: string
amount: string
isNotZero: boolean
}

const sliceName = 'postRewards'

const adapter = createEntityAdapter<PostRewards>({
selectId: data => data.postId,
})
const selectors = adapter.getSelectors<RootState>(state => state.postReward)

export const selectPostReward = selectors.selectById

export const fetchPostRewards = createSimpleManyFetchWrapper<{ postIds: string[] }, PostRewards>({
sliceName,
fetchData: async function ({ postIds }) {
return await getPostRewards(postIds)
},
getCachedData: (state, id) => selectPostReward(state, id),
saveToCacheAction: data => slice.actions.setPostRewards(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: {
setPostRewards: adapter.upsertMany,
},
})

export default slice.reducer
5 changes: 4 additions & 1 deletion src/rtk/features/activeStaking/rewardReportSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ const slice = createSlice({
const rewardMultiplier = BigInt(
Math.min(newSuperLike, CREATORS_CONSTANTS.SUPER_LIKES_FOR_MAX_REWARD),
)
const oldSuperLikeMultiplier = BigInt(
Math.min(rewardReport.superLikesCount, CREATORS_CONSTANTS.SUPER_LIKES_FOR_MAX_REWARD),
)

const rewardPerLike = BigInt(rewardReport.currentRewardAmount) / rewardMultiplier
const rewardPerLike = BigInt(rewardReport.currentRewardAmount) / oldSuperLikeMultiplier
const weeklyWithoutCurrentReward =
BigInt(rewardReport.weeklyReward) - BigInt(rewardReport.currentRewardAmount)

Expand Down
Loading