Skip to content

Commit

Permalink
feat: use data from subid for creators list
Browse files Browse the repository at this point in the history
fix issue where useFetchCreator if fetched multiple times are calling upsert everytime, changing the state, even though the call to api is only once, making it infinite loop
  • Loading branch information
teodorus-nathaniel committed Dec 26, 2023
1 parent 50be25c commit 285ba2b
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 90 deletions.
8 changes: 5 additions & 3 deletions src/components/creators/CreatorDashboardSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SpaceData } from '@subsocial/api/types'
import clsx from 'clsx'
import { ComponentProps } from 'react'
import config from 'src/config'
import { useIsCreatorSpace } from 'src/rtk/features/creators/creatorsListHooks'
import { useFetchStakeData } from 'src/rtk/features/creators/stakesHooks'
import { useMyAddress } from '../auth/MyAccountsContext'
import CreatePostCard from './cards/CreatePostCard'
Expand Down Expand Up @@ -55,8 +55,9 @@ function HomePageSidebar({ variant }: Extract<CreatorDashboardSidebarType, { nam
function SpacePageSidebar({ space }: Extract<CreatorDashboardSidebarType, { name: 'space-page' }>) {
const myAddress = useMyAddress()
const { data } = useFetchStakeData(myAddress ?? '', space.id)
const isCreator = useIsCreatorSpace(space.id)

if (!config.creatorIds?.includes(space.id)) {
if (!isCreator) {
return <SupportCreatorsCard />
}

Expand All @@ -75,8 +76,9 @@ function SpacePageSidebar({ space }: Extract<CreatorDashboardSidebarType, { name
function PostPageSidebar({ space }: Extract<CreatorDashboardSidebarType, { name: 'post-page' }>) {
const myAddress = useMyAddress()
const { data, loading } = useFetchStakeData(myAddress ?? '', space.id)
const isCreator = useIsCreatorSpace(space.id)

if (!config.creatorIds?.includes(space.id)) {
if (!isCreator) {
return (
<>
<CreatePostCard variant='posts' />
Expand Down
7 changes: 1 addition & 6 deletions src/components/main/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import { PostKind } from '../../types/graphql-global-types'

export type PostFilterType = 'latest' | 'suggested' | 'liked' | 'commented'

export type SpaceFilterType =
| 'latest'
| 'suggested'
| 'sortByPosts'
| 'sortByFollowers'
| 'creators'
export type SpaceFilterType = 'latest' | 'suggested' | 'sortByPosts' | 'sortByFollowers'

export type DateFilterType = 'day' | 'week' | 'month' | 'allTime'

Expand Down
2 changes: 1 addition & 1 deletion src/components/main/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const getPostsByFilter: GetEntityFilter<PostFilterType> = {
},
}

const getSpacesByFilter: GetEntityFilter<Exclude<SpaceFilterType, 'creators' | 'suggested'>> = {
const getSpacesByFilter: GetEntityFilter<Exclude<SpaceFilterType, 'suggested'>> = {
latest: {
day: q.GET_LATEST_SPACE_IDS,
week: q.GET_LATEST_SPACE_IDS,
Expand Down
75 changes: 54 additions & 21 deletions src/components/spaces/LatestSpacesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { useSubsocialApi } from 'src/components/substrate/SubstrateContext'
import config from 'src/config'
import { useDfApolloClient } from 'src/graphql/ApolloProvider'
import { GetLatestSpaceIds } from 'src/graphql/__generated__/GetLatestSpaceIds'
import { useAppDispatch } from 'src/rtk/app/store'
import { useFetchCreators } from 'src/rtk/features/creators/creatorsListHooks'
import { fetchCreators } from 'src/rtk/features/creators/creatorsListSlice'
import { fetchMyPermissionsBySpaceIds } from 'src/rtk/features/permissions/mySpacePermissionsSlice'
import { DataSourceTypes, SpaceId } from 'src/types'
import { fetchSpaces } from '../../rtk/features/spaces/spacesSlice'
Expand All @@ -16,39 +19,49 @@ import { isSuggested, loadSpacesByQuery } from '../main/utils'
import { getPageOfIds } from '../utils/getIds'
import { PublicSpacePreviewById } from './SpacePreview'

const { recommendedSpaceIds, creatorIds } = config
const { recommendedSpaceIds } = config

type Props = {
spaceIds?: SpaceId[]
initialSpaceIds?: SpaceId[]
customFetcher?: (config: LoadMoreValues<SpaceFilterType>) => Promise<string[]>
totalSpaceCount: number
filter: SpaceFilterType
dateFilter?: DateFilterType
}

const shuffledCreatorIds = shuffle(creatorIds ?? [])
const loadMoreSpacesFn = async (loadMoreValues: LoadMoreValues<SpaceFilterType>) => {
const { client, size, page, myAddress, subsocial, dispatch, filter } = loadMoreValues
const loadMoreSpacesFn = async (
loadMoreValues: LoadMoreValues<SpaceFilterType> & {
customFetcher?: (config: LoadMoreValues<SpaceFilterType>) => Promise<string[]>
},
) => {
const { client, size, page, myAddress, subsocial, dispatch, filter, customFetcher } =
loadMoreValues

if (filter === undefined) return []

let spaceIds: string[] = []

if (filter.type !== 'suggested' && filter.type !== 'creators' && client) {
const offset = (page - 1) * size
const data = await loadSpacesByQuery({
client,
offset,
filter: { type: filter.type, date: filter.date },
})

const { spaces } = data as GetLatestSpaceIds
spaceIds = spaces.map(value => value.id)
if (customFetcher) {
spaceIds = await customFetcher(loadMoreValues)
} else {
if (filter.type === 'creators') {
spaceIds = getPageOfIds(shuffledCreatorIds, { page, size })
} else spaceIds = getPageOfIds(recommendedSpaceIds, { page, size })
if (filter.type !== 'suggested' && client) {
const offset = (page - 1) * size
const data = await loadSpacesByQuery({
client,
offset,
filter: { type: filter.type, date: filter.date },
})

const { spaces } = data as GetLatestSpaceIds
spaceIds = spaces.map(value => value.id)
} else {
spaceIds = getPageOfIds(recommendedSpaceIds, { page, size })
}
}

console.log(spaceIds)

await Promise.all([
dispatch(fetchMyPermissionsBySpaceIds({ api: subsocial, ids: spaceIds, myAddress })),
dispatch(fetchSpaces({ api: subsocial, ids: spaceIds, dataSource: DataSourceTypes.SQUID })),
Expand All @@ -58,7 +71,7 @@ const loadMoreSpacesFn = async (loadMoreValues: LoadMoreValues<SpaceFilterType>)
}

const InfiniteListOfSpaces = (props: Props) => {
const { totalSpaceCount, initialSpaceIds, filter, dateFilter } = props
const { totalSpaceCount, initialSpaceIds, filter, dateFilter, customFetcher } = props
const client = useDfApolloClient()
const dispatch = useDispatch()
const { subsocial } = useSubsocialApi()
Expand All @@ -76,6 +89,7 @@ const InfiniteListOfSpaces = (props: Props) => {
type: filter,
date: dateFilter,
},
customFetcher,
})
}

Expand Down Expand Up @@ -109,8 +123,27 @@ export const SuggestedSpaces = () => (
<InfiniteListOfSpaces totalSpaceCount={recommendedSpaceIds.length} filter='suggested' />
)

export const CreatorsSpaces = () => (
<InfiniteListOfSpaces totalSpaceCount={creatorIds?.length ?? 0} filter='creators' />
)
let shuffledCreators: string[] | null = null
export const CreatorsSpaces = () => {
const { data: creators } = useFetchCreators()

const dispatch = useAppDispatch()
const loadCreators = async () => {
if (shuffledCreators) return shuffledCreators

const res = await dispatch(fetchCreators({}))
const spaceIds = (res.payload as { spaceId: string }[]).map(({ spaceId }) => spaceId)
shuffledCreators = shuffle(spaceIds)
return shuffledCreators
}
return (
<InfiniteListOfSpaces
totalSpaceCount={creators.length ?? 0}
customFetcher={loadCreators}
// filter is not used if customFetcher is provided, but this is needed to make the type works properly
filter='suggested'
/>
)
}

export default LatestSpacesPage
8 changes: 5 additions & 3 deletions src/components/spaces/ViewSpace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { ButtonLink } from 'src/components/utils/CustomLinks'
import { Segment } from 'src/components/utils/Segment'
import { LARGE_AVATAR_SIZE } from 'src/config/Size.config'
import { useSetChatEntityConfig, useSetChatOpen } from 'src/rtk/app/hooks'
import { useIsCreatorSpace } from 'src/rtk/features/creators/creatorsListHooks'
import { useFetchStakeData } from 'src/rtk/features/creators/stakesHooks'
import { SpaceContent, SpaceData, SpaceId, SpaceStruct, SpaceWithSomeDetails } from 'src/types'
import config from '../../config'
import { useSelectProfileSpace } from '../../rtk/features/profiles/profilesHooks'
import { useSelectSpace } from '../../rtk/features/spaces/spacesHooks'
import { useMyAddress } from '../auth/MyAccountsContext'
Expand Down Expand Up @@ -66,7 +66,8 @@ export const SpaceNameAsLink = React.memo(({ space, ...props }: SpaceNameAsLinkP
})

export const StakeButton = ({ spaceStruct }: { spaceStruct: SpaceStruct }) => {
return config.creatorIds?.includes(spaceStruct.id) ? (
const isCreator = useIsCreatorSpace(spaceStruct.id)
return isCreator ? (
<ButtonLink
type='primary'
target='_blank'
Expand Down Expand Up @@ -131,6 +132,8 @@ export const InnerViewSpace = (props: Props) => {
}
}, [spaceData])

const isCreatorSpace = useIsCreatorSpace(spaceData?.id)

// We do not return 404 page here, because this component could be used to render a space in list.
if (!spaceData) return null

Expand Down Expand Up @@ -295,7 +298,6 @@ export const InnerViewSpace = (props: Props) => {
)
}

const isCreatorSpace = config.creatorIds?.includes(spaceData.id)
const showCreatorCards = isCreatorSpace && isMobile

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/spaces/helpers/SpaceDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { editSpaceUrl } from 'src/components/urls'
import { isHidden, ViewOnIpfs } from 'src/components/utils'
import { BasicDropDownMenuProps, DropdownMenu } from 'src/components/utils/DropDownMenu'
import { showSuccessMessage } from 'src/components/utils/Message'
import config from 'src/config'
import { useHasUserASpacePermission } from 'src/permissions/checkPermission'
import { useSendEvent } from 'src/providers/AnalyticContext'
import { useSetChatOpen } from 'src/rtk/app/hooks'
import { useAppSelector } from 'src/rtk/app/store'
import { useIsCreatorSpace } from 'src/rtk/features/creators/creatorsListHooks'
import { SpaceData } from 'src/types'
import { useSelectProfile } from '../../../rtk/features/profiles/profilesHooks'
import { useIsUsingEmail, useMyAddress } from '../../auth/MyAccountsContext'
Expand Down Expand Up @@ -43,7 +43,7 @@ export const SpaceDropdownMenu = (props: SpaceDropDownProps) => {
const showMakeAsProfileButton = isMySpace && (!profileSpaceId || profileSpaceId !== id)

const sendEvent = useSendEvent()
const isCreatorSpace = config.creatorIds?.includes(struct.id)
const isCreatorSpace = useIsCreatorSpace(struct.id)
const hasChatSetup = useAppSelector(state => !!state.chat.entity)
const setChatOpen = useSetChatOpen()

Expand Down
2 changes: 0 additions & 2 deletions src/components/utils/FollowSpaceButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ export function InnerFollowSpaceButton(props: InnerFollowSpaceButtonProps) {

const label = isFollower ? 'Unfollow' : 'Follow'

console.log(otherProps)

return (
<TxButton
type={isFollower ? 'default' : 'primary'}
Expand Down
4 changes: 2 additions & 2 deletions src/components/utils/OffchainUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export const getChainsInfo = async () => {

export const getCreatorList = async () => {
const res = await axiosRequest(`${subIdApiUrl}/staking/creator/list`)
const creators = (res?.data as { spaceId: string }[]) || []
return creators.map(({ spaceId }) => spaceId)
const creators = (res?.data as { spaceId: string; status: 'Active' | '' }[]) || []
return creators.filter(({ status }) => status === 'Active').map(({ spaceId }) => ({ spaceId }))
}

export const getTotalStake = async ({ address }: { address: string }) => {
Expand Down
16 changes: 0 additions & 16 deletions src/config/app/polkaverse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,6 @@ const index: AppConfig = {
claimedSpaceIds: ['1', '2', '3', '4', '5'],
recommendedSpaceIds: polkaverseSpaces,
suggestedTlds: ['sub', 'polka'],
creatorIds: [
'10124',
'11581',
'7366',
'6953',
'1573',
'11157',
'6283',
// '11414' // inactive
'11566',
'4777',
'1238',
'11844',
'4809',
'10132',
],
}

export default index
1 change: 0 additions & 1 deletion src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export type AppConfig = {
claimedSpaceIds: SpaceId[]
recommendedSpaceIds: SpaceId[]
suggestedTlds?: string[]
creatorIds?: string[]
}

export type CommonSubsocialFeatures = {
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 @@ -5,6 +5,7 @@ import chainsInfo from '../features/chainsInfo/chainsInfoSlice'
import chat from '../features/chat/chatSlice'
import enableConfirmation from '../features/confirmationPopup/enableConfirmationSlice'
import contents from '../features/contents/contentsSlice'
import creatorsList from '../features/creators/creatorsListSlice'
import stakes from '../features/creators/stakesSlice'
import totalStake from '../features/creators/totalStakeSlice'
import ordersById from '../features/domainPendingOrders/pendingOrdersSlice'
Expand Down Expand Up @@ -53,6 +54,7 @@ const rootReducer = combineReducers({
chat,
stakes,
totalStake,
creatorsList,
})

export type RootState = ReturnType<typeof rootReducer>
Expand Down
20 changes: 20 additions & 0 deletions src/rtk/features/creators/creatorsListHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useFetchWithoutApi } from 'src/rtk/app/hooksCommon'
import { useAppSelector } from 'src/rtk/app/store'
import { fetchCreators, selectCreators } from './creatorsListSlice'

export function useFetchCreators(config?: { enabled?: boolean }) {
const { enabled } = config || {}
const data = useAppSelector(state => selectCreators(state))

const props = useFetchWithoutApi(fetchCreators, {}, { enabled })

return {
...props,
data,
}
}

export function useIsCreatorSpace(spaceId?: string) {
const { data } = useFetchCreators({ enabled: !!spaceId })
return data.map(({ spaceId }) => spaceId).includes(spaceId ?? '')
}
47 changes: 47 additions & 0 deletions src/rtk/features/creators/creatorsListSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createAsyncThunk, createEntityAdapter, createSlice } from '@reduxjs/toolkit'
import { getCreatorList } from 'src/components/utils/OffchainUtils'
import { ThunkApiConfig } from 'src/rtk/app/helpers'
import { RootState } from 'src/rtk/app/rootReducer'

export type Creator = { spaceId: string }
const sliceName = 'creatorsList'

const adapter = createEntityAdapter<Creator>({
selectId: data => data.spaceId,
})
const selectors = adapter.getSelectors<RootState>(state => state.creatorsList)

export const selectCreators = selectors.selectAll

let pendingPromise: Promise<Creator[]> | null = null
export const fetchCreators = createAsyncThunk<Creator[], { reload?: boolean }, ThunkApiConfig>(
`${sliceName}/fetchMany`,
async ({ reload }, { getState, dispatch }) => {
if (!reload) {
const fetchedData = selectCreators(getState())
if (fetchedData.length > 0) return fetchedData
}
if (pendingPromise) return pendingPromise

async function fetchData() {
const data = await getCreatorList()
await dispatch(slice.actions.setCreators(data))
return data
}
const promise = fetchData()
pendingPromise = promise
await promise
pendingPromise = null
return promise
},
)

const slice = createSlice({
name: sliceName,
initialState: adapter.getInitialState(),
reducers: {
setCreators: adapter.setAll,
},
})

export default slice.reducer
Loading

0 comments on commit 285ba2b

Please sign in to comment.