diff --git a/packages/interface/src/features/applications/components/ApplicationForm.tsx b/packages/interface/src/features/applications/components/ApplicationForm.tsx
index 5d1924e7..12483e40 100644
--- a/packages/interface/src/features/applications/components/ApplicationForm.tsx
+++ b/packages/interface/src/features/applications/components/ApplicationForm.tsx
@@ -1,6 +1,5 @@
import { useRouter } from "next/router";
import { useState, useCallback } from "react";
-import { useLocalStorage } from "react-use";
import { toast } from "sonner";
import { useAccount } from "wagmi";
@@ -22,8 +21,6 @@ interface IApplicationFormProps {
}
export const ApplicationForm = ({ pollId }: IApplicationFormProps): JSX.Element => {
- const clearDraft = useLocalStorage("application-draft")[2];
-
const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork();
const { address } = useAccount();
@@ -56,7 +53,6 @@ export const ApplicationForm = ({ pollId }: IApplicationFormProps): JSX.Element
const create = useCreateApplication({
onSuccess: (id: bigint) => {
- clearDraft();
router.push(`/rounds/${pollId}/applications/confirmation?id=${id.toString()}`);
},
onError: (err: { message: string }) =>
diff --git a/packages/interface/src/features/applications/components/ReviewApplicationDetails.tsx b/packages/interface/src/features/applications/components/ReviewApplicationDetails.tsx
index d7977eaa..877e6f14 100644
--- a/packages/interface/src/features/applications/components/ReviewApplicationDetails.tsx
+++ b/packages/interface/src/features/applications/components/ReviewApplicationDetails.tsx
@@ -1,6 +1,7 @@
import clsx from "clsx";
import { useMemo, type ReactNode } from "react";
import { useFormContext } from "react-hook-form";
+import { useAccount } from "wagmi";
import { Heading } from "~/components/ui/Heading";
import { Tag } from "~/components/ui/Tag";
@@ -41,6 +42,8 @@ export const ReviewApplicationDetails = (): JSX.Element => {
const application = useMemo(() => form.getValues(), [form]);
+ const { address } = useAccount();
+
return (
@@ -54,6 +57,8 @@ export const ReviewApplicationDetails = (): JSX.Element => {
+
+
diff --git a/packages/interface/src/features/applications/hooks/useApplicationById.ts b/packages/interface/src/features/applications/hooks/useApplicationById.ts
deleted file mode 100644
index 2cf017c6..00000000
--- a/packages/interface/src/features/applications/hooks/useApplicationById.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { api } from "~/utils/api";
-
-import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { IRequest } from "~/utils/types";
-
-export function useApplicationById(registryAddress: string, id: string): UseTRPCQueryResult
{
- return api.applications.getById.useQuery({ registryAddress, id });
-}
diff --git a/packages/interface/src/features/applications/hooks/useApplications.ts b/packages/interface/src/features/applications/hooks/useApplications.ts
index 0e118bdd..ab03339c 100644
--- a/packages/interface/src/features/applications/hooks/useApplications.ts
+++ b/packages/interface/src/features/applications/hooks/useApplications.ts
@@ -1,7 +1,7 @@
import { api } from "~/utils/api";
import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { IRequest } from "~/utils/types";
+import type { IRequest, IRecipient } from "~/utils/types";
export function useApprovedApplications(registryAddress: string): UseTRPCQueryResult {
return api.applications.approvals.useQuery({ registryAddress });
@@ -17,3 +17,19 @@ export function useApplicationByProjectId(
): UseTRPCQueryResult {
return api.applications.getByProjectId.useQuery({ projectId, registryAddress });
}
+
+export function useAllApplications(registryAddress: string): UseTRPCQueryResult {
+ return api.applications.getByIds.useQuery({ registryAddress, ids: [] });
+}
+
+export function useApplicationById(registryAddress: string, id: string): UseTRPCQueryResult {
+ return api.applications.getById.useQuery({ registryAddress, id });
+}
+
+export function useApplicationsByIds(registryAddress: string, ids: string[]): UseTRPCQueryResult {
+ return api.applications.getByIds.useQuery({ registryAddress, ids });
+}
+
+export function useMyApplications(registryAddress: string, address: string): UseTRPCQueryResult {
+ return api.projects.getMine.useQuery({ registryAddress, address });
+}
diff --git a/packages/interface/src/features/applications/hooks/useCreateApplication.ts b/packages/interface/src/features/applications/hooks/useCreateApplication.ts
index f9e51e67..df2e6c61 100644
--- a/packages/interface/src/features/applications/hooks/useCreateApplication.ts
+++ b/packages/interface/src/features/applications/hooks/useCreateApplication.ts
@@ -34,7 +34,7 @@ export function useCreateApplication(options: {
}): TUseCreateApplicationReturn {
const upload = useUploadMetadata();
- const { chain } = useAccount();
+ const { chain, address } = useAccount();
const { getRoundByPollId } = useRound();
const roundData = getRoundByPollId(options.pollId);
@@ -44,7 +44,7 @@ export function useCreateApplication(options: {
const mutation = useMutation({
mutationFn: async (values: Application) => {
- if (!signer || !chain) {
+ if (!signer || !chain || !address) {
throw new Error("Please connect your wallet first");
}
@@ -71,6 +71,7 @@ export function useCreateApplication(options: {
profileImageUrl: profileImageUrl.url,
bannerImageUrl: bannerImageUrl.url,
submittedAt: Date.now().valueOf(),
+ creator: address,
};
const uploadRes = await upload.mutateAsync(metadataValues);
diff --git a/packages/interface/src/features/applications/types/index.ts b/packages/interface/src/features/applications/types/index.ts
index 683369be..138153c6 100644
--- a/packages/interface/src/features/applications/types/index.ts
+++ b/packages/interface/src/features/applications/types/index.ts
@@ -25,6 +25,7 @@ export const fundingSourceTypes = {
export const ApplicationSchema = z.object({
name: z.string().min(3),
bio: z.string().min(3),
+ creator: z.string().optional(),
profileImageUrl: z.string().optional(),
bannerImageUrl: z.string().optional(),
submittedAt: z.number().optional(),
diff --git a/packages/interface/src/features/projects/components/ProjectItem.tsx b/packages/interface/src/features/projects/components/ProjectItem.tsx
index db4142ca..d78b42fb 100644
--- a/packages/interface/src/features/projects/components/ProjectItem.tsx
+++ b/packages/interface/src/features/projects/components/ProjectItem.tsx
@@ -1,4 +1,5 @@
import Image from "next/image";
+import Link from "next/link";
import { Button } from "~/components/ui/Button";
import { Heading } from "~/components/ui/Heading";
@@ -36,57 +37,59 @@ export const ProjectItem = ({
const roundState = useRoundState({ pollId });
return (
-
-
-
-
-
- {metadata.data?.name}
-
-
-
-
- {metadata.data?.bio}
-
+
+
+
-
-
-
-
- {!isLoading && state !== undefined && action && roundState === ERoundState.VOTING && (
-
-
- {state === EProjectState.DEFAULT && (
-
- )}
-
- {state === EProjectState.ADDED && (
-
- )}
-
- {state === EProjectState.SUBMITTED && (
-
- )}
+
+
+ {metadata.data?.name}
+
+
+
+
+ {metadata.data?.bio}
- )}
-
-
+
+
+
+
+
+ {!isLoading && state !== undefined && action && roundState === ERoundState.VOTING && (
+
+
+ {state === EProjectState.DEFAULT && (
+
+ )}
+
+ {state === EProjectState.ADDED && (
+
+ )}
+
+ {state === EProjectState.SUBMITTED && (
+
+ )}
+
+
+ )}
+
+
+
);
};
diff --git a/packages/interface/src/features/projects/components/ProjectsResults.tsx b/packages/interface/src/features/projects/components/ProjectsResults.tsx
index 5b112b4d..4ef3c166 100644
--- a/packages/interface/src/features/projects/components/ProjectsResults.tsx
+++ b/packages/interface/src/features/projects/components/ProjectsResults.tsx
@@ -1,5 +1,4 @@
import clsx from "clsx";
-import Link from "next/link";
import { useRouter } from "next/router";
import { useCallback, useMemo } from "react";
import { type Hex, zeroAddress } from "viem";
@@ -43,11 +42,7 @@ export const ProjectsResults = ({ pollId }: IProjectsResultsProps): JSX.Element
(
-
+
{!results.isLoading && roundState === ERoundState.RESULTS ? (
) : null}
@@ -59,7 +54,7 @@ export const ProjectsResults = ({ pollId }: IProjectsResultsProps): JSX.Element
recipient={item}
state={EProjectState.SUBMITTED}
/>
-
+
)}
/>
);
diff --git a/packages/interface/src/features/rounds/components/Projects.tsx b/packages/interface/src/features/rounds/components/Projects.tsx
index 606e902f..0a06886b 100644
--- a/packages/interface/src/features/rounds/components/Projects.tsx
+++ b/packages/interface/src/features/rounds/components/Projects.tsx
@@ -3,6 +3,7 @@ import Link from "next/link";
import { useCallback, useMemo } from "react";
import { FiAlertCircle } from "react-icons/fi";
import { Hex, zeroAddress } from "viem";
+import { useAccount } from "wagmi";
import { InfiniteLoading } from "~/components/InfiniteLoading";
import { SortFilter } from "~/components/SortFilter";
@@ -12,6 +13,7 @@ import { Heading } from "~/components/ui/Heading";
import { useBallot } from "~/contexts/Ballot";
import { useMaci } from "~/contexts/Maci";
import { useRound } from "~/contexts/Round";
+import { useMyApplications } from "~/features/applications/hooks/useApplications";
import { useResults } from "~/hooks/useResults";
import { useRoundState } from "~/utils/state";
import { ERoundState } from "~/utils/types";
@@ -27,6 +29,8 @@ export interface IProjectsProps {
export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
const roundState = useRoundState({ pollId });
+ const { address } = useAccount();
+
const { getRoundByPollId } = useRound();
const round = useMemo(() => getRoundByPollId(pollId), [pollId, getRoundByPollId]);
@@ -43,6 +47,13 @@ export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
const ballot = useMemo(() => getBallot(pollId), [pollId, getBallot]);
+ /**
+ * Find my applications: "I" am either the "creator" or the "payout address"
+ */
+ const applications = useMyApplications(round?.registryAddress ?? zeroAddress, address ?? zeroAddress);
+
+ const myApplications = useMemo(() => applications.data, [applications]);
+
const handleAction = useCallback(
(projectIndex: number, projectId: string) => (e: Event) => {
e.preventDefault();
@@ -118,24 +129,36 @@ export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
- {roundState === ERoundState.APPLICATION && (
-
-
-
-
+ {roundState === ERoundState.APPLICATION && address && (
+
+
+ My Projects
+
+
+
+
+
+
+
+ {myApplications &&
+ myApplications.length > 0 &&
+ myApplications.map((project) => (
+
+ ))}
+
+ {(!myApplications || myApplications.length === 0) && (
+
Create your application by clicking the button
+ )}
+
)}
(
-
+
{!results.isLoading && roundState === ERoundState.RESULTS ? (
) : null}
@@ -147,7 +170,7 @@ export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
recipient={item}
state={defineState(Number.parseInt(item.index, 10))}
/>
-
+
)}
/>
diff --git a/packages/interface/src/hooks/useRegistry.ts b/packages/interface/src/hooks/useRegistry.ts
index 7d2efdbb..b9151e08 100644
--- a/packages/interface/src/hooks/useRegistry.ts
+++ b/packages/interface/src/hooks/useRegistry.ts
@@ -22,7 +22,7 @@ interface SubmitApplicationArgs {
*/
registryAddress: Hex;
/**
- * The recipient of the attestation
+ * The recipient of the application
*/
recipient: Hex;
}
diff --git a/packages/interface/src/pages/rounds/[pollId]/applications/confirmation.tsx b/packages/interface/src/pages/rounds/[pollId]/applications/confirmation.tsx
index 49b932a7..6bacfc79 100644
--- a/packages/interface/src/pages/rounds/[pollId]/applications/confirmation.tsx
+++ b/packages/interface/src/pages/rounds/[pollId]/applications/confirmation.tsx
@@ -1,4 +1,3 @@
-import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { useMemo } from "react";
import { FiAlertCircle } from "react-icons/fi";
@@ -8,7 +7,7 @@ import { EmptyState } from "~/components/EmptyState";
import { Alert } from "~/components/ui/Alert";
import { Heading } from "~/components/ui/Heading";
import { useRound } from "~/contexts/Round";
-import { useApplicationById } from "~/features/applications/hooks/useApplicationById";
+import { useApplicationById } from "~/features/applications/hooks/useApplications";
import { ProjectItem } from "~/features/projects/components/ProjectItem";
import { Layout } from "~/layouts/DefaultLayout";
import { useRoundState } from "~/utils/state";
@@ -83,9 +82,7 @@ const ConfirmProjectPage = ({ pollId }: { pollId: string }): JSX.Element => {
)}
-
-
-
+
diff --git a/packages/interface/src/server/api/routers/applications.ts b/packages/interface/src/server/api/routers/applications.ts
index dfe3d57c..14f9e657 100644
--- a/packages/interface/src/server/api/routers/applications.ts
+++ b/packages/interface/src/server/api/routers/applications.ts
@@ -6,18 +6,31 @@ import {
fetchApplicationByProjectId,
fetchApprovedApplications,
fetchPendingApplications,
+ fetchApplications,
} from "~/utils/fetchApplications";
export const applicationsRouter = createTRPCRouter({
approvals: publicProcedure
.input(z.object({ registryAddress: z.string() }))
.query(async ({ input }) => fetchApprovedApplications(input.registryAddress)),
+
pending: publicProcedure
.input(z.object({ registryAddress: z.string() }))
.query(async ({ input }) => fetchPendingApplications(input.registryAddress)),
+
getById: publicProcedure
.input(z.object({ registryAddress: z.string(), id: z.string() }))
.query(async ({ input }) => fetchApplicationById(input.registryAddress, input.id)),
+
+ getByIds: publicProcedure
+ .input(z.object({ registryAddress: z.string(), ids: z.array(z.string()) }))
+ .query(async ({ input }) => {
+ if (input.ids.length > 0) {
+ return Promise.all(input.ids.map((id) => fetchApplicationById(input.registryAddress, id)));
+ }
+ return fetchApplications(input.registryAddress);
+ }),
+
getByProjectId: publicProcedure
.input(z.object({ registryAddress: z.string(), projectId: z.string() }))
.query(async ({ input }) => fetchApplicationByProjectId(input.projectId, input.registryAddress)),
diff --git a/packages/interface/src/server/api/routers/projects.ts b/packages/interface/src/server/api/routers/projects.ts
index a1dfad3f..cf33ee66 100644
--- a/packages/interface/src/server/api/routers/projects.ts
+++ b/packages/interface/src/server/api/routers/projects.ts
@@ -3,7 +3,7 @@ import { z } from "zod";
import { FilterSchema } from "~/features/filter/types";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { fetchApprovedProjects, fetchProjects } from "~/utils/fetchProjects";
+import { fetchApprovedProjects, fetchProjects, fetchProjectsByAddress } from "~/utils/fetchProjects";
import { getProjectCount } from "~/utils/registry";
import type { Chain, Hex } from "viem";
@@ -39,6 +39,10 @@ export const projectsRouter = createTRPCRouter({
.input(FilterSchema.extend({ registryAddress: z.string() }))
.query(async ({ input }) => fetchProjects(input.registryAddress)),
+ getMine: publicProcedure
+ .input(z.object({ registryAddress: z.string(), address: z.string() }))
+ .query(async ({ input }) => fetchProjectsByAddress(input.registryAddress, input.address)),
+
// Used for distribution to get the projects' payoutAddress
// To get this data we need to fetch all projects and their metadata
// payoutAddresses: publicProcedure.input(z.object({ ids: z.array(z.string()) })).query(async ({ input }) =>
diff --git a/packages/interface/src/utils/fetchApplications.ts b/packages/interface/src/utils/fetchApplications.ts
index 63280740..1ea284a8 100644
--- a/packages/interface/src/utils/fetchApplications.ts
+++ b/packages/interface/src/utils/fetchApplications.ts
@@ -65,6 +65,25 @@ const ApprovedRequests = `
}
`;
+// Fetch all add requests
+const AllRequests = `
+ query AllRequests($registryAddress: String!) {
+ requests(where: { requestType: "Add", recipient_: { registry: $registryAddress } }) {
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
const IndividualRequest = `
query PendingAddRequests($registryAddress: String!, $recipientId: String!) {
requests(where: { requestType: "Add", status: "Pending", recipient_: {
@@ -226,7 +245,7 @@ export async function fetchApplications(registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: ProjectsByAddress,
+ variables: { registryAddress, address },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.recipients;
+}