Skip to content

Commit

Permalink
refactor: remove useAsyncMemo (#1022)
Browse files Browse the repository at this point in the history
* started useAsyncMemo replacement

* further components migrated

* finished refactor

* added env to .env.example

* use constant for stale time

* requested changes

* removed last useAsyncMemo useage

* fix: Identity modal bug

* small change
  • Loading branch information
ncomerci authored Jun 16, 2023
1 parent e3fb2bc commit 8411b80
Show file tree
Hide file tree
Showing 62 changed files with 719 additions and 499 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ GATSBY_DURATION_TENDER=1200
GATSBY_DURATION_HIRING=1200
DURATION_TENDER=1200
SUBMISSION_WINDOW_DURATION_TENDER=1200
GRANT_PROPOSAL_DURATION_IN_SECONDS=1209600

# Submission threshold for governance proposals
GATSBY_SUBMISSION_THRESHOLD_POLL=100
Expand Down
3 changes: 3 additions & 0 deletions src/clients/Governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ export class Governance extends API {
}

async getCoAuthorsByProposal(id: string, status?: CoauthorStatus) {
if (!id) {
return []
}
const result = await this.fetch<ApiResponse<CoauthorAttributes[]>>(`/coauthors/${id}${status ? `/${status}` : ''}`)
return result.data
}
Expand Down
11 changes: 8 additions & 3 deletions src/components/Common/LinkWithTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react'

import useAsyncMemo from 'decentraland-gatsby/dist/hooks/useAsyncMemo'
import { useQuery } from '@tanstack/react-query'
import useFormatMessage from 'decentraland-gatsby/dist/hooks/useFormatMessage'

import { Governance } from '../../clients/Governance'
import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants'
import Open from '../Icon/Open'

import './LinkWithTitle.css'
Expand All @@ -15,15 +16,19 @@ interface Props {
const fetchTitle = async (url: string) => {
try {
const response = await Governance.get().checkUrlTitle(url)
return response.title
return response.title || ''
} catch (error) {
console.error(error)
}
}

function LinkWithTitle({ url }: Props) {
const t = useFormatMessage()
const [title] = useAsyncMemo(() => fetchTitle(url), [url], { initialValue: undefined })
const { data: title } = useQuery({
queryKey: [`title#${url}`],
queryFn: () => fetchTitle(url),
staleTime: DEFAULT_QUERY_STALE_TIME,
})

return (
<a href={url} target="_blank" rel="noopener noreferrer" className="LinkWithTitle">
Expand Down
2 changes: 1 addition & 1 deletion src/components/Delegation/DelegatorCardProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'

import Link from 'decentraland-gatsby/dist/components/Text/Link'
import useFormatMessage, { useIntl } from 'decentraland-gatsby/dist/hooks/useFormatMessage'
import { Link } from 'decentraland-gatsby/dist/plugins/intl'

import locations from '../../utils/locations'
import ChevronRightCircleOutline from '../Icon/ChevronRightCircleOutline'
Expand Down
6 changes: 3 additions & 3 deletions src/components/Profile/ActivityBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ActivityBox = ({ address }: Props) => {
const [activeTab, setActiveTab] = useState(Tab.MyProposals)

const isLoggedUserProfile = isSameAddress(account, address || '')
const [pendingCoauthorRequests] = useProposalsByCoAuthor(isLoggedUserProfile ? account : null, CoauthorStatus.PENDING)
const { requestsStatus } = useProposalsByCoAuthor(isLoggedUserProfile ? account : null, CoauthorStatus.PENDING)

return (
<Container>
Expand All @@ -52,15 +52,15 @@ const ActivityBox = ({ address }: Props) => {
)}
<BoxTabs.Tab onClick={() => setActiveTab(Tab.CoAuthoring)} active={activeTab === Tab.CoAuthoring}>
{t('page.profile.activity.coauthoring.title')}
{pendingCoauthorRequests.length > 0 && <Dot className="ActivityBox__DotIcon" />}
{requestsStatus.length > 0 && <Dot className="ActivityBox__DotIcon" />}
</BoxTabs.Tab>
</BoxTabs.Left>
</BoxTabs>
<BoxTabsContentContainer>
{activeTab === Tab.MyProposals && <ProposalsCreatedTab address={address} />}
{activeTab === Tab.Watchlist && isLoggedUserProfile && <WatchlistTab />}
{activeTab === Tab.CoAuthoring && (
<CoAuthoringTab address={address} pendingCoauthorRequests={pendingCoauthorRequests} />
<CoAuthoringTab address={address} pendingCoauthorRequests={requestsStatus} />
)}
</BoxTabsContentContainer>
</BoxTabsContainer>
Expand Down
4 changes: 3 additions & 1 deletion src/components/Profile/ProposalsCreatedTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ const ProposalsCreatedTab = ({ address }: Props) => {
{!isLoadingProposals && (
<>
{proposals.length > 0 ? (
proposals.map((proposal) => <ProposalCreatedItem key={proposal.id} proposal={proposal} />)
proposals.map((proposal) => (
<ProposalCreatedItem key={`${proposal.id}#${Math.random()}`} proposal={proposal} />
))
) : (
<Empty className="ActivityBox__Empty" icon={<Watermelon />} description={t(emptyDescriptionKey)} />
)}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Profile/VpDelegationBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import { ProfileBox } from './ProfileBox'
interface Props {
address: string | null
delegation: DelegationResult
isLoadingDelegations: boolean
isLoadingDelegation: boolean
ownVp: number | undefined
isLoadingOwnVp: boolean
}

function VpDelegationBox({ address, delegation, isLoadingDelegations, ownVp, isLoadingOwnVp }: Props) {
function VpDelegationBox({ address, delegation, isLoadingDelegation, ownVp, isLoadingOwnVp }: Props) {
const t = useFormatMessage()
const [userAddress] = useAuthContext()
const isLoggedUserProfile = isSameAddress(userAddress, address)
const isLoading = isLoadingDelegations || isLoadingOwnVp
const isLoading = isLoadingDelegation || isLoadingOwnVp
const { delegatedTo } = delegation
const [openDelegationModal, setOpenDelegationModal] = useState(false)
const { vpDistribution, isLoadingVpDistribution } = useVotingPowerDistribution(address)
Expand Down
7 changes: 3 additions & 4 deletions src/components/Profile/VpDelegatorsBox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'

import { AsyncStateResultState } from 'decentraland-gatsby/dist/hooks/useAsyncState'
import useFormatMessage from 'decentraland-gatsby/dist/hooks/useFormatMessage'
import { Container } from 'decentraland-ui/dist/components/Container/Container'

Expand All @@ -17,7 +16,7 @@ interface Props {
profileAddress: string | null
userAddress: string | null
delegation: DelegationResult
delegationState: AsyncStateResultState<DelegationResult, DelegationResult>
isLoadingDelegation: boolean
scores: DetailedScores
isLoadingScores: boolean
}
Expand All @@ -26,7 +25,7 @@ export default function VpDelegatorsBox({
profileAddress,
userAddress,
delegation,
delegationState,
isLoadingDelegation,
scores,
isLoadingScores,
}: Props) {
Expand Down Expand Up @@ -61,7 +60,7 @@ export default function VpDelegatorsBox({
<DelegationCards
delegation={delegation}
scores={scores}
isLoading={delegationState.loading || isLoadingScores}
isLoading={isLoadingDelegation || isLoadingScores}
isUserProfile={isLoggedUserProfile}
loggedUserVpDistribution={loggedUserVpDistribution}
profileAddress={profileAddress}
Expand Down
21 changes: 16 additions & 5 deletions src/components/Proposal/ProposalHeaderPoi.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react'

import { useQuery } from '@tanstack/react-query'
import ImgFixed from 'decentraland-gatsby/dist/components/Image/ImgFixed'
import Link from 'decentraland-gatsby/dist/components/Text/Link'
import useAsyncMemo from 'decentraland-gatsby/dist/hooks/useAsyncMemo'
import Catalyst from 'decentraland-gatsby/dist/utils/api/Catalyst'
import Land from 'decentraland-gatsby/dist/utils/api/Land'
import { Header } from 'decentraland-ui/dist/components/Header/Header'

import { ProposalAttributes } from '../../entities/Proposal/types'
import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants'
import Pin from '../Icon/Pin'

import './ProposalHeaderPoi.css'
Expand All @@ -19,8 +20,7 @@ interface Props {
export default function ProposalHeaderPoi({ configuration }: Props) {
const { x, y } = configuration

const [tile] = useAsyncMemo(() => Land.get().getTile([x, y]), [x, y])
const [sceneImg] = useAsyncMemo(async () => {
const fetchSceneImg = async (x: number, y: number) => {
const scenes = await Catalyst.get().getEntityScenes([[x, y]])
const scene = scenes[0]
if (!scene) {
Expand All @@ -41,7 +41,18 @@ export default function ProposalHeaderPoi({ configuration }: Props) {
}

return image
}, [x, y])
}

const { data: tile } = useQuery({
queryKey: [`tile#${x},${y}`],
queryFn: () => Land.get().getTile([x, y]),
staleTime: DEFAULT_QUERY_STALE_TIME,
})
const { data: sceneImg } = useQuery({
queryKey: [`sceneImg#${x},${y}`],
queryFn: () => fetchSceneImg(x, y),
staleTime: DEFAULT_QUERY_STALE_TIME,
})

if (!sceneImg) {
return (
Expand Down Expand Up @@ -73,7 +84,7 @@ export default function ProposalHeaderPoi({ configuration }: Props) {
{x},{y}
</span>
</div>
<ImgFixed dimension="standard" size="cover" src={sceneImg!} />
<ImgFixed dimension="standard" size="cover" src={sceneImg} />
</div>
<div className="PoiImageContainer">
<ImgFixed dimension="standard" size="initial" src={Land.get().getParcelImage([x, y])} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Proposal/ProposalSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type Props = {
voteWithSurvey: boolean
updatingStatus: boolean
subscribing: boolean
subscribe: (subscribe?: boolean) => void
subscribe: (subscribe: boolean) => void
subscriptions: SubscriptionAttributes[] | null
subscriptionsLoading: boolean
votes: Record<string, Vote> | null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default function BiddingAndTenderingProcess({ proposal, tenderProposalsTo
const t = useFormatMessage()
const { configuration, start_at, finish_at, type, status } = proposal
const { linked_proposal_id } = configuration
const [pitchProposal] = useProposal(linked_proposal_id)
const { proposal: pitchProposal } = useProposal(linked_proposal_id)

const finishAt = linked_proposal_id ? pitchProposal?.finish_at : finish_at
const pitchConfig = getPitchConfig(type, status)
Expand Down
10 changes: 4 additions & 6 deletions src/components/Proposal/View/ProposalCoAuthorStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import React, { useEffect, useState } from 'react'

import classNames from 'classnames'
import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext'
import useAsyncMemo from 'decentraland-gatsby/dist/hooks/useAsyncMemo'
import useFormatMessage from 'decentraland-gatsby/dist/hooks/useFormatMessage'
import { Button } from 'decentraland-ui/dist/components/Button/Button'
import { Header } from 'decentraland-ui/dist/components/Header/Header'
import Grid from 'semantic-ui-react/dist/commonjs/collections/Grid/Grid'

import { Governance } from '../../../clients/Governance'
import { CoauthorAttributes, CoauthorStatus } from '../../../entities/Coauthor/types'
import { CoauthorStatus } from '../../../entities/Coauthor/types'
import { isCoauthoringUpdatable } from '../../../entities/Coauthor/utils'
import useCoauthors from '../../../hooks/useCoauthors'
import Helper from '../../Helper/Helper'
import Cancel from '../../Icon/Cancel'
import Check from '../../Icon/Check'
Expand Down Expand Up @@ -49,14 +49,12 @@ function ProposalCoAuthorStatus({ proposalId, proposalFinishDate }: Props) {
const [user] = useAuthContext()
const isUpdatable = isCoauthoringUpdatable(proposalFinishDate)

const [coAuthors] = useAsyncMemo(() => Governance.get().getCoAuthorsByProposal(proposalId), [], {
initialValue: [] as CoauthorAttributes[],
})
const coAuthors = useCoauthors(proposalId)

const [status, setStatus] = useState<CoauthorStatus | undefined>(undefined)

useEffect(() => {
if (user) {
if (user && coAuthors) {
setStatus(coAuthors.find((coauthor) => coauthor.address.toLowerCase() === user.toLowerCase())?.status)
}
}, [user, coAuthors])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ const ProposalVotingSection = ({
}: Props) => {
const t = useFormatMessage()
const [account, accountState] = useAuthContext()
const [delegation, delegationState] = useDelegationOnProposal(proposal, account)
const delegate: string | null = delegation?.delegatedTo[0]?.delegate
const { delegationResult, isDelegationResultLoading } = useDelegationOnProposal(proposal, account)
const delegate: string | null = delegationResult.delegatedTo[0]?.delegate
const delegators: string[] = useMemo(
() => delegation?.delegatedFrom.map((delegator) => delegator.delegator),
[delegation?.delegatedFrom]
() => delegationResult.delegatedFrom.map((delegator) => delegator.delegator),
[delegationResult.delegatedFrom]
)

const {
Expand All @@ -70,7 +70,7 @@ const ProposalVotingSection = ({
totalVpOnProposal,
hasEnoughToVote,
isLoadingVp,
} = useVotingPowerOnProposal(account, delegators, delegationState.loading, votes, proposal)
} = useVotingPowerOnProposal(account, delegators, isDelegationResultLoading, votes, proposal)

const { matchResult } = useVotesMatch(account, delegate)
const voteDifference = matchResult.voteDifference
Expand All @@ -94,7 +94,7 @@ const ProposalVotingSection = ({
[delegators, votes, choices]
)

const proposalVotingSectionLoading = loading || accountState.loading || delegationState.loading || isLoadingVp
const proposalVotingSectionLoading = loading || accountState.loading || isDelegationResultLoading || isLoadingVp
const showGetInvolvedQuestion = !!proposal && !proposalVotingSectionLoading && !hasVoted && !finished
const isProposalPending = proposal?.status === ProposalStatus.Pending

Expand Down
9 changes: 5 additions & 4 deletions src/components/User/Badges/Badges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ export default function Badges({ address }: Props) {
const [sidebarOpen, setSidebarOpen] = useState(false)
const [badgeInDetail, setBadgeInDetail] = useState<Badge | null>(null)

const displayedBadges = useMemo(() => badges?.currentBadges.slice(0, MAX_DISPLAYED_BADGES) ?? [], [badges])
const stackedBadges = useMemo(() => {
return badges ? [...badges.currentBadges.slice(MAX_DISPLAYED_BADGES), ...badges.expiredBadges] : []
}, [badges])
const displayedBadges = useMemo(() => badges.currentBadges.slice(0, MAX_DISPLAYED_BADGES) ?? [], [badges])
const stackedBadges = useMemo(
() => [...badges.currentBadges.slice(MAX_DISPLAYED_BADGES), ...badges.expiredBadges],
[badges]
)

const handleSidebarClose = useCallback(() => {
setSidebarOpen(false)
Expand Down
1 change: 1 addition & 0 deletions src/config/env/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"GATSBY_SNAPSHOT_DELEGATE_CONTRACT_ADDRESS": "0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446",
"GATSBY_SNAPSHOT_API": "https://hub.snapshot.org/",
"GATSBY_SNAPSHOT_DURATION": "172800",
"GATSBY_GRANT_PROPOSAL_DURATION_IN_SECONDS": "300",
"GATSBY_SNAPSHOT_SPACE": "lemu.dcl.eth",
"GATSBY_SNAPSHOT_URL": "https://snapshot.org/",
"GATSBY_SNAPSHOT_QUERY_ENDPOINT": "https://api.thegraph.com/subgraphs/name/snapshot-labs/snapshot",
Expand Down
3 changes: 2 additions & 1 deletion src/entities/Grant/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import env from 'decentraland-gatsby/dist/utils/env'

import Time from '../../utils/date/Time'

export const GRANT_PROPOSAL_DURATION_IN_SECONDS = env('GATSBY_GRANT_PROPOSAL_DURATION_IN_SECONDS', '1209600')
export const GRANT_PROPOSAL_DURATION_IN_SECONDS =
process.env.GATSBY_GRANT_PROPOSAL_DURATION_IN_SECONDS || env('GATSBY_GRANT_PROPOSAL_DURATION_IN_SECONDS', '1209600')
export const GATSBY_GRANT_VP_THRESHOLD = process.env.GATSBY_GRANT_VP_THRESHOLD
export const MAX_LOWER_TIER_GRANT_FUNDING = 20000
export const BUDGETING_START_DATE = Time.utc('2023-01-01 00:00:00Z').toDate()
4 changes: 3 additions & 1 deletion src/hooks/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const DEFAULT_QUERY_STALE_TIME = 120 * 1000
export const DEFAULT_QUERY_STALE_TIME = 3.6e6 // 1 hour
export const TWENTY_MINUTES_MS = 1.2e6 // 20 minutes
export const FIVE_MINUTES_MS = 3e5 // 5 minutes
53 changes: 28 additions & 25 deletions src/hooks/useAddressesVotesTotals.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import useAsyncMemo from 'decentraland-gatsby/dist/hooks/useAsyncMemo'
import { useQuery } from '@tanstack/react-query'
import max from 'lodash/max'

import { SnapshotGraphql } from '../clients/SnapshotGraphql'

import { TWENTY_MINUTES_MS } from './constants'

export type VoteHistory = { lastVoted: number; timesVoted: number }

export default function useAddressesVotesTotals(addresses: string[]) {
const [addressesVotesTotals, state] = useAsyncMemo(
async () => {
const addressesVotesByDate = await SnapshotGraphql.get().getAddressesVotes(addresses)
const aggregatedVotes: Record<string, VoteHistory> = {}
addressesVotesByDate.map((voteByDate) => {
const voter = voteByDate.voter.toLowerCase()
if (aggregatedVotes[voter]) {
aggregatedVotes[voter].timesVoted += 1
aggregatedVotes[voter].lastVoted = max([aggregatedVotes[voter].lastVoted, voteByDate.created]) || 0
} else {
aggregatedVotes[voter] = {
lastVoted: voteByDate.created,
timesVoted: 1,
}
}
})
return aggregatedVotes
},
[JSON.stringify(addresses)],
{ initialValue: {} as Record<string, VoteHistory>, callWithTruthyDeps: true }
)
const fetchVotes = async (addresses: string[]) => {
const addressesVotesByDate = await SnapshotGraphql.get().getAddressesVotes(addresses)
const aggregatedVotes: Record<string, VoteHistory> = {}
addressesVotesByDate.map((voteByDate) => {
const voter = voteByDate.voter.toLowerCase()
if (aggregatedVotes[voter]) {
aggregatedVotes[voter].timesVoted += 1
aggregatedVotes[voter].lastVoted = max([aggregatedVotes[voter].lastVoted, voteByDate.created]) || 0
} else {
aggregatedVotes[voter] = {
lastVoted: voteByDate.created,
timesVoted: 1,
}
}
})
return aggregatedVotes
}

export default function useAddressesVotesTotals(addresses: string[]) {
const { data: addressesVotesTotals, isLoading } = useQuery({
queryKey: [`votes#${addresses.join('-')}`],
queryFn: () => fetchVotes(addresses),
staleTime: TWENTY_MINUTES_MS,
})
return {
addressesVotesTotals,
isLoadingAddressesVotesTotals: state.loading,
addressesVotesTotals: addressesVotesTotals ?? {},
isLoadingAddressesVotesTotals: isLoading,
}
}
Loading

0 comments on commit 8411b80

Please sign in to comment.