Improve useCommunities (#113)

This commit is contained in:
Szymon Szlachtowicz 2021-07-14 10:46:25 +02:00 committed by GitHub
parent f67c63830d
commit 22121d9952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 134 additions and 164 deletions

View File

@ -101,9 +101,13 @@ export function DirectoryCards() {
</PageBar>
<WeeklyFeature endDate={new Date('07/26/2021')} />
<Voting>
{communities.map((community) => (
<DirectoryCard key={community.publicKey} community={community} />
))}
{communities.map((community, idx) => {
if (community) {
return <DirectoryCard key={community.publicKey} community={community} />
} else {
return <DirectoryCardSkeleton key={idx} />
}
})}
{communities.length === 0 && <DirectoryCardSkeleton />}
</Voting>
</>

View File

@ -45,9 +45,13 @@ export function VotingCards() {
</VoteFilter>
<FilterList value={sortedBy} setValue={setSortedBy} options={VotingSortingOptions} />
</PageBar>
{roomsToShow.map((room: any) => (
<VotingCard key={room.roomNumber.toString()} room={room} />
))}
{roomsToShow.map((room: any, idx) => {
if (room?.details) {
return <VotingCard key={room.roomNumber.toString()} room={room} />
} else {
return <VotingCardSkeleton key={idx} />
}
})}
{roomsToShow.length === 0 && <VotingCardSkeleton />}
</div>
)

View File

@ -26,60 +26,62 @@ export function isTypeInRoom(voteType: string, room: any) {
}
export function sortVotingFunction(sortedBy: VotingSortingEnum) {
return (a: DetailedVotingRoom | undefined, b: DetailedVotingRoom | undefined) => {
if (!a) {
return 1
}
if (!b) {
return -1
}
let aSum
let bSum
switch (sortedBy) {
case VotingSortingEnum.AtoZ:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => (a.details.name < b.details.name ? -1 : 1)
return a.details.name < b.details.name ? -1 : 1
case VotingSortingEnum.ZtoA:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => (a.details.name < b.details.name ? 1 : -1)
return a.details.name < b.details.name ? 1 : -1
case VotingSortingEnum.EndingLatest:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => {
return a.endAt < b.endAt ? -1 : 1
}
case VotingSortingEnum.EndingSoonest:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => {
return a.endAt < b.endAt ? 1 : -1
}
case VotingSortingEnum.MostVotes:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => {
const aSum = a.totalVotesAgainst.add(a.totalVotesFor)
const bSum = b.totalVotesAgainst.add(b.totalVotesFor)
aSum = a.totalVotesAgainst.add(a.totalVotesFor)
bSum = b.totalVotesAgainst.add(b.totalVotesFor)
return aSum < bSum ? 1 : -1
}
case VotingSortingEnum.LeastVotes:
return (a: DetailedVotingRoom, b: DetailedVotingRoom) => {
const aSum = a.totalVotesAgainst.add(a.totalVotesFor)
const bSum = b.totalVotesAgainst.add(b.totalVotesFor)
aSum = a.totalVotesAgainst.add(a.totalVotesFor)
bSum = b.totalVotesAgainst.add(b.totalVotesFor)
return aSum < bSum ? -1 : 1
}
}
}
export function sortDirectoryFunction(sortedBy: DirectorySortingEnum) {
return (a: CommunityDetail | undefined, b: CommunityDetail | undefined) => {
if (!a) {
return 1
}
if (!b) {
return -1
}
switch (sortedBy) {
case DirectorySortingEnum.AtoZ:
return (a: CommunityDetail, b: CommunityDetail) => (a.name < b.name ? -1 : 1)
return a.name < b.name ? -1 : 1
case DirectorySortingEnum.ZtoA:
return (a: CommunityDetail, b: CommunityDetail) => (a.name < b.name ? 1 : -1)
return a.name < b.name ? 1 : -1
case DirectorySortingEnum.IncludedLongAgo:
return (a: CommunityDetail, b: CommunityDetail) => {
if (!a.directoryInfo) return 1
if (!b.directoryInfo) return -1
return a?.directoryInfo?.additionDate < b?.directoryInfo?.additionDate ? -1 : 1
}
case DirectorySortingEnum.IncludedRecently:
return (a: CommunityDetail, b: CommunityDetail) => {
if (!a.directoryInfo) return -1
if (!b.directoryInfo) return 1
return a?.directoryInfo?.additionDate < b?.directoryInfo?.additionDate ? 1 : -1
}
case DirectorySortingEnum.MostVotes:
return (a: CommunityDetail, b: CommunityDetail) => {
if (!a.directoryInfo?.featureVotes) return 1
if (!b.directoryInfo?.featureVotes) return -1
return a?.directoryInfo?.featureVotes < b?.directoryInfo?.featureVotes ? 1 : -1
}
case DirectorySortingEnum.LeastVotes:
return (a: CommunityDetail, b: CommunityDetail) => {
if (!a.directoryInfo?.featureVotes) return 1
if (!b.directoryInfo?.featureVotes) return -1
return a?.directoryInfo?.featureVotes < b?.directoryInfo?.featureVotes ? -1 : 1

View File

@ -1,72 +1,24 @@
import { useState, useEffect } from 'react'
import { CommunityDetail } from '../models/community'
import { APIOptions, APIFunction } from './../models/api'
import { getCommunityDetails } from '../helpers/apiMock'
import { useCommunitiesProvider } from '../providers/communities/provider'
export function useCommunities(API: APIFunction, options: APIOptions) {
const [communities, setCommunities] = useState<CommunityDetail[]>([])
const [page, setPage] = useState(0)
const [increment, setIncrement] = useState(false)
const [loading, setLoading] = useState(true)
export function useCommunities(publicKeys: string[]) {
const { communitiesDetails, dispatch } = useCommunitiesProvider()
const handleScroll = (e: Event) => {
if (e.target) {
const scrollingElement = (e.target as HTMLDocument).scrollingElement
if (!scrollingElement?.scrollHeight || !scrollingElement?.scrollTop || !scrollingElement?.clientHeight) {
return
}
if (scrollingElement.scrollHeight - scrollingElement.scrollTop <= scrollingElement.clientHeight + 60) {
setPage((prevPage) => prevPage + 1)
window.removeEventListener('scroll', handleScroll)
}
}
}
useEffect(() => {
if (increment) {
if (document.scrollingElement?.clientHeight || document.scrollingElement?.scrollHeight) {
if (document.scrollingElement?.clientHeight >= document.scrollingElement?.scrollHeight) {
setPage((prevPage) => prevPage + 1)
return publicKeys.map((publicKey) => {
const detail = communitiesDetails[publicKey]
if (detail) {
return { ...detail }
} else {
window.addEventListener('scroll', handleScroll)
if (publicKey) {
const setCommunity = async () => {
const communityDetail = await getCommunityDetails(publicKey)
if (communityDetail) {
dispatch(communityDetail)
}
}
setCommunity()
}
return () => {
window.removeEventListener('scroll', handleScroll)
return undefined
}
}, [increment])
useEffect(() => {
const getCommunities = async () => {
setLoading(true)
const communities = (await API(0, options)).communities
setCommunities(communities)
setLoading(false)
setIncrement(true)
}
setCommunities([])
setPage(0)
setIncrement(false)
getCommunities()
}, [options.filterKeyword, options.sortedBy, options.voteType])
useEffect(() => {
const updateCommunities = async () => {
setLoading(true)
const newCommunities = (await API(page, options)).communities
setCommunities((oldCommunities) => [...oldCommunities, ...newCommunities])
if (newCommunities.length === options.numberPerPage) {
setIncrement(true)
}
setLoading(false)
}
if (page === 0) return
setIncrement(false)
updateCommunities()
}, [page])
return { communities, loading }
})
}

View File

@ -1,27 +1,14 @@
import { useEffect, useRef, useState } from 'react'
import { getCommunityDetails } from '../helpers/apiMock'
import { useEffect } from 'react'
import { CommunityDetail } from '../models/community'
import { useCommunities } from './useCommunities'
export function useCommunityDetails(publicKey: string, setCommunityDetail: (val: CommunityDetail | undefined) => void) {
const [loading, setLoading] = useState(false)
const loadingIdx = useRef(0)
const [CommunityDetail] = useCommunities([publicKey])
useEffect(() => {
const getDetails = async (key: string) => {
const idx = ++loadingIdx.current
setLoading(true)
setCommunityDetail(await getCommunityDetails(key))
if (idx === loadingIdx.current) {
setLoading(false)
}
}
setCommunityDetail(undefined)
if (publicKey) {
getDetails(publicKey)
}
}, [publicKey])
setCommunityDetail(CommunityDetail)
return () => setCommunityDetail(undefined)
}, [JSON.stringify(CommunityDetail)])
useEffect(() => setCommunityDetail(undefined), [])
return loading
return !CommunityDetail
}

View File

@ -1,8 +1,8 @@
import { useContractCall } from '@usedapp/core'
import { useEffect, useState } from 'react'
import { getCommunityDetails } from '../helpers/apiMock'
import { isTextInDetails, sortDirectoryFunction } from '../helpers/communityFiltering'
import { CommunityDetail, DirectorySortingEnum } from '../models/community'
import { useCommunities } from './useCommunities'
import { useContracts } from './useContracts'
export function useDirectoryCommunities(filterKeyword: string, sortedBy: DirectorySortingEnum) {
@ -14,28 +14,18 @@ export function useDirectoryCommunities(filterKeyword: string, sortedBy: Directo
args: [],
}) ?? [[]]
const [unfilteredComm, setUnfilteredComm] = useState(communities)
const [filteredCommunities, setFilteredCommunities] = useState<CommunityDetail[]>([])
const unfilteredComm = useCommunities(communities)
const [filteredCommunities, setFilteredCommunities] = useState<(CommunityDetail | undefined)[]>([])
useEffect(() => {
const getDetails = async () => {
if (communities.length > 0) {
const detailedComm = await Promise.all(communities.map(async (key: string) => await getCommunityDetails(key)))
setUnfilteredComm(detailedComm)
}
}
getDetails()
}, [JSON.stringify(communities)])
useEffect(() => {
const filterCommunities = unfilteredComm.filter((comm: CommunityDetail) => {
const filterCommunities = unfilteredComm.filter((comm: CommunityDetail | undefined) => {
if (comm) {
return isTextInDetails(filterKeyword, comm)
}
return false
return true
})
setFilteredCommunities(filterCommunities.sort(sortDirectoryFunction(sortedBy)))
}, [unfilteredComm, sortedBy, filterKeyword])
}, [JSON.stringify(unfilteredComm), sortedBy, filterKeyword])
return filteredCommunities
}

View File

@ -1,10 +1,10 @@
import { useContractCall, useContractCalls } from '@usedapp/core'
import { useEffect, useState } from 'react'
import { getCommunityDetails } from '../helpers/apiMock'
import { VotingSortingEnum } from '../models/community'
import { DetailedVotingRoom } from '../models/smartContract'
import { useContracts } from './useContracts'
import { isTextInDetails, isTypeInRoom, sortVotingFunction } from '../helpers/communityFiltering'
import { useCommunities } from './useCommunities'
export function useVotingCommunities(
filterKeyword: string,
@ -32,31 +32,28 @@ export function useVotingCommunities(
})
const votingRooms = useContractCalls(contractCalls)
const publicKeys = votingRooms.map((votingRoom: any) => votingRoom?.community)
const communitiesDetails = useCommunities(publicKeys)
useEffect(() => {
if (votingRooms.length > 0) {
const getPromises = async () => {
const rooms = await Promise.all(
votingRooms.map(async (el: any) => {
const rooms = votingRooms.map((el: any) => {
if (el) {
return { ...el, details: await getCommunityDetails(el.community) }
return { ...el, details: communitiesDetails.find((comm) => comm?.publicKey === el.community) }
}
return undefined
})
)
setRoomsWithCommunity(rooms)
}
getPromises()
}
}, [JSON.stringify(votingRooms)])
}, [JSON.stringify(votingRooms), JSON.stringify(communitiesDetails)])
useEffect(() => {
const filteredRooms = roomsWithCommunity.filter((room: any) => {
if (room) {
if (room && room.details) {
return isTextInDetails(filterKeyword, room.details) && isTypeInRoom(voteType, room)
}
return false
})
return true
}) as (any | undefined)[]
setFilteredRooms(filteredRooms.sort(sortVotingFunction(sortedBy)))
}, [roomsWithCommunity, filterKeyword, voteType, sortedBy])

View File

@ -5,6 +5,7 @@ import { DAppProvider, ChainId } from '@usedapp/core'
import { DEFAULT_CONFIG } from '@usedapp/core/dist/cjs/src/model/config/default'
import { ConfigProvider } from './providers/config/provider'
import { WakuProvider } from './providers/waku/provider'
import { CommunitiesProvider } from './providers/communities/provider'
const config = {
readOnlyChainId: ChainId.Ropsten,
@ -23,7 +24,9 @@ render(
<WakuProvider>
<DAppProvider config={config}>
<ConfigProvider>
<CommunitiesProvider>
<App />
</CommunitiesProvider>
</ConfigProvider>
</DAppProvider>
</WakuProvider>

View File

@ -0,0 +1,31 @@
import React, { ReactNode, useReducer, createContext, useContext } from 'react'
import { CommunityDetail } from '../../models/community'
type CommunitiesDetails = {
[publicKey: string]: CommunityDetail
}
const CommunitiesContext = createContext<{
communitiesDetails: CommunitiesDetails
dispatch: (details: CommunityDetail) => void
}>({
communitiesDetails: {},
dispatch: () => undefined,
})
export function useCommunitiesProvider() {
return useContext(CommunitiesContext)
}
function communitiesReducer(state: CommunitiesDetails, action: CommunityDetail) {
return { ...state, [action.publicKey]: action }
}
interface CommunitiesProviderProps {
children: ReactNode
}
export function CommunitiesProvider({ children }: CommunitiesProviderProps) {
const [communitiesDetails, dispatch] = useReducer(communitiesReducer, {})
return <CommunitiesContext.Provider value={{ communitiesDetails, dispatch }} children={children} />
}