diff --git a/migration/1694295208252-AddEligibleNetworksToQfRoundEntity.ts b/migration/1694295208252-AddEligibleNetworksToQfRoundEntity.ts index 65a9610d3..e7c2e85f4 100644 --- a/migration/1694295208252-AddEligibleNetworksToQfRoundEntity.ts +++ b/migration/1694295208252-AddEligibleNetworksToQfRoundEntity.ts @@ -5,8 +5,8 @@ export class AddEligibleNetworksToQfRoundEntity1694295208252 { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` - ALTER TABLE "qf_round" - ADD COLUMN IF NOT EXIST "eligibleNetworks" integer array DEFAULT ARRAY[]::integer[] + ALTER TABLE public.qf_round + ADD COLUMN IF NOT EXISTS "eligibleNetworks" integer array DEFAULT ARRAY[]::integer[] `); } diff --git a/migration/1694635872128-AddEligibleNetworksToPreviousQfRounds.ts b/migration/1694635872128-AddEligibleNetworksToPreviousQfRounds.ts index 680bdef1d..d8d6df46f 100644 --- a/migration/1694635872128-AddEligibleNetworksToPreviousQfRounds.ts +++ b/migration/1694635872128-AddEligibleNetworksToPreviousQfRounds.ts @@ -13,20 +13,20 @@ export class AddEligibleNetworksToPreviousQfRounds1694635872128 ? [1, 3, 5, 100, 137, 10, 420, 56, 42220, 44787] // Include testnets for staging : [1, 137, 56, 42220, 100, 10]; // Exclude testnets for non-staging - // Convert the eligibleNetworks array to a comma-separated string - const eligibleNetworksString = eligibleNetworks.join(', '); - // Update the "qf_round" table with the new eligibleNetworks values - await queryRunner.query(` - UPDATE "qf_round" - SET eligibleNetworks = '{${eligibleNetworksString}}' - `); + await queryRunner.query( + ` + UPDATE public.qf_round + SET "eligibleNetworks" = $1 + `, + [eligibleNetworks], + ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(` - UPDATE "qf_round" - SET eligibleNetworks = '{}' + UPDATE public.qf_round + SET "eligibleNetworks" = '{}' `); } } diff --git a/src/entities/user.ts b/src/entities/user.ts index 6c6817f37..d9f2b17c0 100644 --- a/src/entities/user.ts +++ b/src/entities/user.ts @@ -183,13 +183,7 @@ export class User extends BaseEntity { createdAt: Date; @Field(type => Int, { nullable: true }) - async projectsCount() { - const projectsCount = await Project.createQueryBuilder('project') - .where('project."admin" = :id', { id: String(this.id) }) - .getCount(); - - return projectsCount; - } + projectsCount?: number; @Field(type => Int, { nullable: true }) async donationsCount() { @@ -215,6 +209,7 @@ export class User extends BaseEntity { return likedProjectsCount; } + @Field(type => Int, { nullable: true }) async boostedProjectsCount() { return findPowerBoostingsCountByUserId(this.id); diff --git a/src/repositories/donationRepository.ts b/src/repositories/donationRepository.ts index caa7b3ae8..9bf2825fc 100644 --- a/src/repositories/donationRepository.ts +++ b/src/repositories/donationRepository.ts @@ -26,6 +26,19 @@ export const fillQfRoundDonationsUserScores = async (): Promise => { `); }; +export const addressHasDonated = async (address: string) => { + const projectAddress = await Donation.query( + ` + SELECT "id" + FROM donation + where lower("fromWalletAddress") = $1 + limit 1 + `, + [address.toLowerCase()], + ); + return projectAddress.length > 0; +}; + export const createDonation = async (data: { amount: number; project: Project; diff --git a/src/repositories/userRepository.ts b/src/repositories/userRepository.ts index 7e074598e..31f2233d6 100644 --- a/src/repositories/userRepository.ts +++ b/src/repositories/userRepository.ts @@ -3,6 +3,7 @@ import { SegmentAnalyticsSingleton } from '../services/segment/segmentAnalyticsS import { Donation } from '../entities/donation'; import { Reaction } from '../entities/reaction'; import { PowerBoosting } from '../entities/powerBoosting'; +import { Project, ProjStatus, ReviewStatus } from '../entities/project'; export const findAdminUserByEmail = async ( email: string, @@ -45,8 +46,34 @@ export const findUserByWalletAddress = async ( if (!includeSensitiveFields) { query.select(publicSelectionFields); } + const user = await query.getOne(); + if (!user) return null; - return query.getOne(); + user.projectsCount = await fetchUserProjectsCount( + user!.id, + includeSensitiveFields, + ); + + return user; +}; + +export const fetchUserProjectsCount = async ( + userId: number, + includeSensitiveFields: boolean, +) => { + const projectsCount = Project.createQueryBuilder('project').where( + 'project."adminUserId" = :id', + { id: userId }, + ); + + if (!includeSensitiveFields) { + projectsCount.andWhere( + `project.statusId = ${ProjStatus.active} AND project.reviewStatus = :reviewStatus`, + { reviewStatus: ReviewStatus.Listed }, + ); + } + + return projectsCount.getCount(); }; export const findUserById = (userId: number): Promise => { diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index a03f3906a..109655e75 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -1,4 +1,12 @@ -import { Arg, Ctx, Mutation, Query, Resolver } from 'type-graphql'; +import { + Arg, + Ctx, + Field, + Mutation, + ObjectType, + Query, + Resolver, +} from 'type-graphql'; import { Repository } from 'typeorm'; import { User } from '../entities/user'; @@ -18,6 +26,17 @@ import { SegmentAnalyticsSingleton } from '../services/segment/segmentAnalyticsS import { AppDataSource } from '../orm'; import { getGitcoinAdapter } from '../adapters/adaptersFactory'; import { logger } from '../utils/logger'; +import { isWalletAddressInPurpleList } from '../repositories/projectAddressRepository'; +import { addressHasDonated } from '../repositories/donationRepository'; + +@ObjectType() +class UserRelatedAddressResponse { + @Field(type => Boolean, { nullable: false }) + hasRelatedProject: boolean; + + @Field(type => Boolean, { nullable: false }) + hasDonated: boolean; +} @Resolver(of => User) export class UserResolver { @@ -29,6 +48,14 @@ export class UserResolver { // return User.create(data).save(); } + @Query(returns => UserRelatedAddressResponse) + async walletAddressUsed(@Arg('address') address: string) { + return { + hasRelatedProject: await isWalletAddressInPurpleList(address), + hasDonated: await addressHasDonated(address), + }; + } + @Query(returns => UserByAddressResponse, { nullable: true }) async userByAddress( @Arg('address', type => String) address: string,