From 45f6618760ca176a5bded4cd5cf1064b29d13bac Mon Sep 17 00:00:00 2001 From: yu-zhen Date: Wed, 17 Jul 2024 12:40:01 +0900 Subject: [PATCH] fix: fetch results cached st not getting latest data --- .../components/AdminButtonsBar.tsx | 11 ++++- .../components/ApplicationsToApprove.tsx | 14 +++++-- .../applications/components/ApproveButton.tsx | 41 ------------------- .../applications/components/ReviewBar.tsx | 4 +- .../hooks/useApprovedApplications.ts | 4 ++ .../projects/components/ProjectDetails.tsx | 6 ++- src/pages/projects/[projectId]/Project.tsx | 4 +- src/server/api/routers/applications.ts | 11 ++++- src/utils/fetchAttestations.ts | 25 +++++++++++ 9 files changed, 67 insertions(+), 53 deletions(-) delete mode 100644 src/features/applications/components/ApproveButton.tsx diff --git a/src/features/applications/components/AdminButtonsBar.tsx b/src/features/applications/components/AdminButtonsBar.tsx index b6eb0c3f..7e003181 100644 --- a/src/features/applications/components/AdminButtonsBar.tsx +++ b/src/features/applications/components/AdminButtonsBar.tsx @@ -1,6 +1,7 @@ import { useCallback } from "react"; import { Button } from "~/components/ui/Button"; +import { Spinner } from "~/components/ui/Spinner"; import { useIsAdmin } from "~/hooks/useIsAdmin"; import { useIsCorrectNetwork } from "~/hooks/useIsCorrectNetwork"; @@ -14,7 +15,11 @@ export const AdminButtonsBar = ({ projectId }: IAdminButtonsBarProps): JSX.Eleme const isAdmin = useIsAdmin(); const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork(); - const approve = useApproveApplication({}); + const approve = useApproveApplication({ + onSuccess: () => { + window.location.reload(); + }, + }); const onClick = useCallback(() => { approve.mutate([projectId]); @@ -29,7 +34,9 @@ export const AdminButtonsBar = ({ projectId }: IAdminButtonsBarProps): JSX.Eleme variant="primary" onClick={onClick} > - {!isCorrectNetwork ? `Connect to ${correctNetwork.name}` : "Approve application"} + {approve.isPending && } + + {!approve.isPending && !isCorrectNetwork ? `Connect to ${correctNetwork.name}` : "Approve application"} ); diff --git a/src/features/applications/components/ApplicationsToApprove.tsx b/src/features/applications/components/ApplicationsToApprove.tsx index be9bdd25..b0b38fbd 100644 --- a/src/features/applications/components/ApplicationsToApprove.tsx +++ b/src/features/applications/components/ApplicationsToApprove.tsx @@ -21,7 +21,7 @@ import { type Attestation } from "~/utils/fetchAttestations"; import { formatDate } from "~/utils/time"; import { useApproveApplication } from "../hooks/useApproveApplication"; -import { useApprovedApplications } from "../hooks/useApprovedApplications"; +import { useLatestApprovedApplications } from "../hooks/useApprovedApplications"; export interface IApplicationItemProps extends Attestation { isApproved?: boolean; @@ -130,7 +130,9 @@ export const ApplicationItem = ({
- {isApproved ? Approved : Pending} + {isApproved && Approved} + + {!isApproved && Pending}
@@ -145,8 +147,12 @@ type TApplicationsToApprove = z.infer; export const ApplicationsToApprove = (): JSX.Element => { const applications = useApplications(); - const approved = useApprovedApplications(); - const approve = useApproveApplication({}); + const approved = useLatestApprovedApplications(); + const approve = useApproveApplication({ + onSuccess: () => { + window.location.reload(); + }, + }); const approvedById = useMemo( () => diff --git a/src/features/applications/components/ApproveButton.tsx b/src/features/applications/components/ApproveButton.tsx deleted file mode 100644 index a8dde55b..00000000 --- a/src/features/applications/components/ApproveButton.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import dynamic from "next/dynamic"; -import { type PropsWithChildren } from "react"; - -import { Badge } from "~/components/ui/Badge"; -import { Button } from "~/components/ui/Button"; -import { useApproveApplication } from "~/features/applications/hooks/useApproveApplication"; -import { useIsAdmin } from "~/hooks/useIsAdmin"; - -import { useApprovedApplications } from "../hooks/useApprovedApplications"; - -const ApproveButton = ({ - children = "Approve project", - projectIds = [], -}: PropsWithChildren<{ projectIds: string[] }>) => { - const isAdmin = useIsAdmin(); - const approvals = useApprovedApplications(projectIds); - - const approve = useApproveApplication(); - if (approvals.data?.length) { - return ( - - Approved - - ); - } - return ( - isAdmin && ( - - ) - ); -}; - -export default dynamic(() => Promise.resolve(ApproveButton)); diff --git a/src/features/applications/components/ReviewBar.tsx b/src/features/applications/components/ReviewBar.tsx index 03a8e43e..d49e4706 100644 --- a/src/features/applications/components/ReviewBar.tsx +++ b/src/features/applications/components/ReviewBar.tsx @@ -4,7 +4,7 @@ import { FiAlertCircle } from "react-icons/fi"; import { StatusBar } from "~/components/StatusBar"; import { useIsAdmin } from "~/hooks/useIsAdmin"; -import { useApprovedApplications } from "../hooks/useApprovedApplications"; +import { useLatestApprovedApplications } from "../hooks/useApprovedApplications"; import { AdminButtonsBar } from "./AdminButtonsBar"; @@ -14,7 +14,7 @@ interface IReviewBarProps { export const ReviewBar = ({ projectId }: IReviewBarProps): JSX.Element => { const isAdmin = useIsAdmin(); - const rawReturn = useApprovedApplications([projectId]); + const rawReturn = useLatestApprovedApplications([projectId]); const approved = useMemo(() => rawReturn.data && rawReturn.data.length > 0, [rawReturn]); diff --git a/src/features/applications/hooks/useApprovedApplications.ts b/src/features/applications/hooks/useApprovedApplications.ts index ece39ea8..5e866147 100644 --- a/src/features/applications/hooks/useApprovedApplications.ts +++ b/src/features/applications/hooks/useApprovedApplications.ts @@ -6,3 +6,7 @@ import type { Attestation } from "~/utils/fetchAttestations"; export function useApprovedApplications(ids?: string[]): UseTRPCQueryResult { return api.applications.approvals.useQuery({ ids }); } + +export function useLatestApprovedApplications(ids?: string[]): UseTRPCQueryResult { + return api.applications.latestApprovals.useQuery({ ids }); +} diff --git a/src/features/projects/components/ProjectDetails.tsx b/src/features/projects/components/ProjectDetails.tsx index a5ddf299..2e4c2d10 100644 --- a/src/features/projects/components/ProjectDetails.tsx +++ b/src/features/projects/components/ProjectDetails.tsx @@ -6,6 +6,8 @@ import { ProjectAvatar } from "~/features/projects/components/ProjectAvatar"; import { ProjectBanner } from "~/features/projects/components/ProjectBanner"; import { VotingWidget } from "~/features/projects/components/VotingWidget"; import { type Attestation } from "~/utils/fetchAttestations"; +import { useAppState } from "~/utils/state"; +import { EAppState } from "~/utils/types"; import { useProjectMetadata } from "../hooks/useProjects"; @@ -30,6 +32,8 @@ const ProjectDetails = ({ const { bio, websiteUrl, payoutAddress, github, twitter, fundingSources, profileImageUrl, bannerImageUrl } = metadata.data ?? {}; + const appState = useAppState(); + return (
@@ -47,7 +51,7 @@ const ProjectDetails = ({

{attestation?.name}

- + {appState === EAppState.VOTING && }
diff --git a/src/pages/projects/[projectId]/Project.tsx b/src/pages/projects/[projectId]/Project.tsx index caf6ef72..b31e2552 100644 --- a/src/pages/projects/[projectId]/Project.tsx +++ b/src/pages/projects/[projectId]/Project.tsx @@ -2,7 +2,7 @@ import { type GetServerSideProps } from "next"; import { useMemo } from "react"; import { ReviewBar } from "~/features/applications/components/ReviewBar"; -import { useApprovedApplications } from "~/features/applications/hooks/useApprovedApplications"; +import { useLatestApprovedApplications } from "~/features/applications/hooks/useApprovedApplications"; import ProjectDetails from "~/features/projects/components/ProjectDetails"; import { useProjectById } from "~/features/projects/hooks/useProjects"; import { LayoutWithSidebar } from "~/layouts/DefaultLayout"; @@ -15,7 +15,7 @@ export interface IProjectDetailsProps { const ProjectDetailsPage = ({ projectId = "" }: IProjectDetailsProps): JSX.Element => { const projects = useProjectById(projectId); - const approved = useApprovedApplications(); + const approved = useLatestApprovedApplications(); const { name } = projects.data?.[0] ?? {}; const appState = useAppState(); diff --git a/src/server/api/routers/applications.ts b/src/server/api/routers/applications.ts index 2d457501..7a3c9b64 100644 --- a/src/server/api/routers/applications.ts +++ b/src/server/api/routers/applications.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { config, eas } from "~/config"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; -import { createDataFilter, fetchAttestations } from "~/utils/fetchAttestations"; +import { createDataFilter, fetchAttestations, fetchAttestationsWithoutCache } from "~/utils/fetchAttestations"; export const FilterSchema = z.object({ limit: z.number().default(3 * 8), @@ -19,6 +19,15 @@ export const applicationsRouter = createTRPCRouter({ }, }), ), + latestApprovals: publicProcedure.input(z.object({ ids: z.array(z.string()).optional() })).query(async ({ input }) => + fetchAttestationsWithoutCache([eas.schemas.approval], { + where: { + attester: { equals: config.admin }, + refUID: input.ids ? { in: input.ids } : undefined, + AND: [createDataFilter("type", "bytes32", "application"), createDataFilter("round", "bytes32", config.roundId)], + }, + }), + ), list: publicProcedure.input(FilterSchema).query(async () => fetchAttestations([eas.schemas.metadata], { orderBy: [{ time: "desc" }], diff --git a/src/utils/fetchAttestations.ts b/src/utils/fetchAttestations.ts index dfc5b860..b7e02632 100644 --- a/src/utils/fetchAttestations.ts +++ b/src/utils/fetchAttestations.ts @@ -7,6 +7,8 @@ import { createCachedFetch } from "./fetch"; const cachedFetch = createCachedFetch({ ttl: 1000 * 60 * 10 }); +const uncachedFetch = createCachedFetch({ ttl: 0 }); + export interface AttestationWithMetadata { id: string; refUID: string; @@ -85,6 +87,29 @@ export async function fetchAttestations(schema: string[], filter?: AttestationsF }).then((r) => r.data.attestations.map(parseAttestation)); } +export async function fetchAttestationsWithoutCache( + schema: string[], + filter?: AttestationsFilter, +): Promise { + const startsAt = config.startsAt && Math.floor(+config.startsAt / 1000); + + return uncachedFetch<{ attestations: AttestationWithMetadata[] }>(eas.url, { + method: "POST", + body: JSON.stringify({ + query: AttestationsQuery, + variables: { + ...filter, + where: { + schemaId: { in: schema }, + revoked: { equals: false }, + time: { gte: startsAt }, + ...filter?.where, + }, + }, + }), + }).then((r) => r.data.attestations.map(parseAttestation)); +} + export async function fetchApprovedVoter(address: string): Promise { if (config.skipApprovedVoterCheck) { return true;