From 22121d9952e1268b8197baa55eb6969ac0b52dcd Mon Sep 17 00:00:00 2001 From: Szymon Szlachtowicz <38212223+Szymx95@users.noreply.github.com> Date: Wed, 14 Jul 2021 10:46:25 +0200 Subject: [PATCH] Improve useCommunities (#113) --- .../components/directory/DirectoryCards.tsx | 10 ++- .../DApp/src/components/votes/VotingCards.tsx | 10 ++- .../DApp/src/helpers/communityFiltering.ts | 78 ++++++++--------- packages/DApp/src/hooks/useCommunities.ts | 84 ++++--------------- .../DApp/src/hooks/useCommunityDetails.ts | 27 ++---- .../DApp/src/hooks/useDirectoryCommunities.ts | 22 ++--- .../DApp/src/hooks/useVotingCommunities.ts | 31 ++++--- packages/DApp/src/index.tsx | 5 +- .../src/providers/communities/provider.tsx | 31 +++++++ 9 files changed, 134 insertions(+), 164 deletions(-) create mode 100644 packages/DApp/src/providers/communities/provider.tsx diff --git a/packages/DApp/src/components/directory/DirectoryCards.tsx b/packages/DApp/src/components/directory/DirectoryCards.tsx index 4bb62b2..5bbbad7 100644 --- a/packages/DApp/src/components/directory/DirectoryCards.tsx +++ b/packages/DApp/src/components/directory/DirectoryCards.tsx @@ -101,9 +101,13 @@ export function DirectoryCards() { - {communities.map((community) => ( - - ))} + {communities.map((community, idx) => { + if (community) { + return + } else { + return + } + })} {communities.length === 0 && } diff --git a/packages/DApp/src/components/votes/VotingCards.tsx b/packages/DApp/src/components/votes/VotingCards.tsx index 593dfb9..116702b 100644 --- a/packages/DApp/src/components/votes/VotingCards.tsx +++ b/packages/DApp/src/components/votes/VotingCards.tsx @@ -45,9 +45,13 @@ export function VotingCards() { - {roomsToShow.map((room: any) => ( - - ))} + {roomsToShow.map((room: any, idx) => { + if (room?.details) { + return + } else { + return + } + })} {roomsToShow.length === 0 && } ) diff --git a/packages/DApp/src/helpers/communityFiltering.ts b/packages/DApp/src/helpers/communityFiltering.ts index 9438cbd..43ca732 100644 --- a/packages/DApp/src/helpers/communityFiltering.ts +++ b/packages/DApp/src/helpers/communityFiltering.ts @@ -26,63 +26,65 @@ export function isTypeInRoom(voteType: string, room: any) { } export function sortVotingFunction(sortedBy: VotingSortingEnum) { - switch (sortedBy) { - case VotingSortingEnum.AtoZ: - return (a: DetailedVotingRoom, b: DetailedVotingRoom) => (a.details.name < b.details.name ? -1 : 1) - case VotingSortingEnum.ZtoA: - return (a: DetailedVotingRoom, b: DetailedVotingRoom) => (a.details.name < b.details.name ? 1 : -1) - case VotingSortingEnum.EndingLatest: - return (a: DetailedVotingRoom, b: DetailedVotingRoom) => { + 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.details.name < b.details.name ? -1 : 1 + case VotingSortingEnum.ZtoA: + return a.details.name < b.details.name ? 1 : -1 + case VotingSortingEnum.EndingLatest: return a.endAt < b.endAt ? -1 : 1 - } - case VotingSortingEnum.EndingSoonest: - return (a: DetailedVotingRoom, b: DetailedVotingRoom) => { + case VotingSortingEnum.EndingSoonest: 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) + case VotingSortingEnum.MostVotes: + 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) + case VotingSortingEnum.LeastVotes: + aSum = a.totalVotesAgainst.add(a.totalVotesFor) + bSum = b.totalVotesAgainst.add(b.totalVotesFor) return aSum < bSum ? -1 : 1 - } + } } } export function sortDirectoryFunction(sortedBy: DirectorySortingEnum) { - switch (sortedBy) { - case DirectorySortingEnum.AtoZ: - return (a: CommunityDetail, b: CommunityDetail) => (a.name < b.name ? -1 : 1) - case DirectorySortingEnum.ZtoA: - return (a: CommunityDetail, b: CommunityDetail) => (a.name < b.name ? 1 : -1) - case DirectorySortingEnum.IncludedLongAgo: - return (a: CommunityDetail, b: CommunityDetail) => { + return (a: CommunityDetail | undefined, b: CommunityDetail | undefined) => { + if (!a) { + return 1 + } + if (!b) { + return -1 + } + switch (sortedBy) { + case DirectorySortingEnum.AtoZ: + return a.name < b.name ? -1 : 1 + case DirectorySortingEnum.ZtoA: + return a.name < b.name ? 1 : -1 + case DirectorySortingEnum.IncludedLongAgo: 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) => { + case DirectorySortingEnum.IncludedRecently: 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) => { + case DirectorySortingEnum.MostVotes: 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) => { + case DirectorySortingEnum.LeastVotes: if (!a.directoryInfo?.featureVotes) return 1 if (!b.directoryInfo?.featureVotes) return -1 return a?.directoryInfo?.featureVotes < b?.directoryInfo?.featureVotes ? -1 : 1 - } + } } } diff --git a/packages/DApp/src/hooks/useCommunities.ts b/packages/DApp/src/hooks/useCommunities.ts index 2ce89ec..286e6a7 100644 --- a/packages/DApp/src/hooks/useCommunities.ts +++ b/packages/DApp/src/hooks/useCommunities.ts @@ -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([]) - 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) - } else { - window.addEventListener('scroll', handleScroll) + return publicKeys.map((publicKey) => { + const detail = communitiesDetails[publicKey] + if (detail) { + return { ...detail } + } else { + if (publicKey) { + const setCommunity = async () => { + const communityDetail = await getCommunityDetails(publicKey) + if (communityDetail) { + dispatch(communityDetail) + } } + setCommunity() } + return undefined } - return () => { - window.removeEventListener('scroll', handleScroll) - } - }, [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 } + }) } diff --git a/packages/DApp/src/hooks/useCommunityDetails.ts b/packages/DApp/src/hooks/useCommunityDetails.ts index 13eaa8b..1e4cca2 100644 --- a/packages/DApp/src/hooks/useCommunityDetails.ts +++ b/packages/DApp/src/hooks/useCommunityDetails.ts @@ -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 } diff --git a/packages/DApp/src/hooks/useDirectoryCommunities.ts b/packages/DApp/src/hooks/useDirectoryCommunities.ts index a1ffe38..6c8d998 100644 --- a/packages/DApp/src/hooks/useDirectoryCommunities.ts +++ b/packages/DApp/src/hooks/useDirectoryCommunities.ts @@ -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([]) + 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 } diff --git a/packages/DApp/src/hooks/useVotingCommunities.ts b/packages/DApp/src/hooks/useVotingCommunities.ts index 5989379..fac2502 100644 --- a/packages/DApp/src/hooks/useVotingCommunities.ts +++ b/packages/DApp/src/hooks/useVotingCommunities.ts @@ -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) => { - if (el) { - return { ...el, details: await getCommunityDetails(el.community) } - } - return undefined - }) - ) - setRoomsWithCommunity(rooms) - } - getPromises() + const rooms = votingRooms.map((el: any) => { + if (el) { + return { ...el, details: communitiesDetails.find((comm) => comm?.publicKey === el.community) } + } + return undefined + }) + setRoomsWithCommunity(rooms) } - }, [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]) diff --git a/packages/DApp/src/index.tsx b/packages/DApp/src/index.tsx index e5945cc..94afbca 100644 --- a/packages/DApp/src/index.tsx +++ b/packages/DApp/src/index.tsx @@ -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( - + + + diff --git a/packages/DApp/src/providers/communities/provider.tsx b/packages/DApp/src/providers/communities/provider.tsx new file mode 100644 index 0000000..111c092 --- /dev/null +++ b/packages/DApp/src/providers/communities/provider.tsx @@ -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 +}