Skip to content

Commit

Permalink
Allow adding users via scoutgame admin (#4934)
Browse files Browse the repository at this point in the history
* organize some packages

* add aws package

* organize more code

* ..

* fix many imports

* fix farcaster

* search by display name too

* search by name case insensitive

* add builder icon

* add approve builder in the callback

* .

* .

* fix mocks

* add nft assets

* fix nft assets

* fix package

* fix imports

* .

* fix imports

* .

* fix it

* remove
  • Loading branch information
mattcasey authored Nov 1, 2024
1 parent c563372 commit 7249e64
Show file tree
Hide file tree
Showing 115 changed files with 3,361 additions and 348 deletions.
3 changes: 2 additions & 1 deletion @connect-shared/hooks/useS3UploadInput.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { log } from '@charmverse/core/log';
import { uploadToS3 } from '@root/lib/aws/uploadToS3Browser';
import { DEFAULT_MAX_FILE_SIZE_MB } from '@root/lib/file/constants';
import { encodeFilename } from '@root/lib/utils/encodeFilename';
import { replaceS3Domain } from '@root/lib/utils/url';
import { useState } from 'react';

import { uploadToS3 } from '../../packages/aws/src/uploadToS3Browser';

import { useFilePicker } from './useFilePicker';
import { useGetUploadToken } from './useGetUploadToken';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DataNotFoundError } from '@charmverse/core/errors';
import { log } from '@charmverse/core/log';
import type { GitcoinProjectAttestation } from '@charmverse/core/prisma-client';
import { prisma } from '@charmverse/core/prisma-client';
import { resolveENSName } from '@root/lib/blockchain/getENSName';
import { resolveENSName } from '@packages/blockchain/getENSName';
import { attestOnchain } from '@root/lib/credentials/attestOnchain';
import { gitcoinProjectCredentialSchemaId } from '@root/lib/credentials/schemas/gitcoinProjectSchema';
import { storeProjectInS3 } from '@root/lib/credentials/storeProjectInS3';
Expand Down
1 change: 1 addition & 0 deletions @connect-shared/lib/farcaster/uuidFromFid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { log } from '@charmverse/core/log';
import { v4 as uuid } from 'uuid';

// Function to generate a deterministic UUID v4 based on an integer input
/** @deprecated Use uuidFromNumber from @packages/utils/uuid instead */
export function deterministicV4UUIDFromFid(num: number | string): string {
// Create a hash of the input number to generate more uniformly distributed random bytes
const hash = createHash('sha256').update(num.toString()).digest();
Expand Down
1 change: 1 addition & 0 deletions @connect-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"license": "ISC",
"dependencies": {
"@mui/material-nextjs": "^6.1.0",
"@packages/aws": "file:../packages/aws",
"framer-motion": "^11.3.17",
"next-safe-action": "~7.4.2"
}
Expand Down
4 changes: 2 additions & 2 deletions apps/scoutgame/app/api/aws/upload-token/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { uploadToken } from '@connect-shared/lib/aws/uploadToken';
import { getSession } from '@connect-shared/lib/session/getSession';
import { uploadToken } from '@packages/aws/uploadToken';

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
Expand All @@ -15,7 +15,7 @@ export async function GET(request: Request) {
}

try {
const tokenData = await uploadToken(filename, userId);
const tokenData = await uploadToken({ filename, userId });

return Response.json(tokenData);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getChainById } from '@packages/onchain/chains';
import { getPublicClient } from '@packages/onchain/getPublicClient';
import { getChainById } from '@packages/blockchain/chains';
import { getPublicClient } from '@packages/blockchain/getPublicClient';
import { UsdcErc20ABIClient } from '@packages/scoutgame/builderNfts/usdcContractApiClient';
import useSWR from 'swr';
import type { Address, Chain } from 'viem';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getChainById } from '@packages/onchain/chains';
import { getChainById } from '@packages/blockchain/chains';
import { UsdcErc20ABIClient } from '@packages/scoutgame/builderNfts/usdcContractApiClient';
import useSWRMutation from 'swr/mutation';
import { publicActions, type Address, type Chain } from 'viem';
Expand Down
2 changes: 1 addition & 1 deletion apps/scoutgame/hooks/useS3UploadInput.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { log } from '@charmverse/core/log';
import { uploadToS3 } from '@root/lib/aws/uploadToS3Browser';
import { uploadToS3 } from '@packages/aws/uploadToS3Browser';
import { DEFAULT_MAX_FILE_SIZE_MB } from '@root/lib/file/constants';
import { encodeFilename } from '@root/lib/utils/encodeFilename';
import { replaceS3Domain } from '@root/lib/utils/url';
Expand Down
6 changes: 3 additions & 3 deletions apps/scoutgame/lib/blockchain/findOrCreateWalletUser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { log } from '@charmverse/core/log';
import { getENSDetails, getENSName } from '@packages/blockchain/getENSName';
import { getFarcasterUsersByAddresses } from '@packages/farcaster/getFarcasterUsersByAddresses';
import { getENSDetails, getENSName } from '@root/lib/blockchain/getENSName';
import { findOrCreateUser } from '@packages/scoutgame/users/findOrCreateUser';
import type { FindOrCreateUserResult } from '@packages/scoutgame/users/findOrCreateUser';
import { getAddress } from 'viem';

import type { FindOrCreateUserResult } from 'lib/users/findOrCreateUser';
import { findOrCreateUser } from 'lib/users/findOrCreateUser';
import { generateUserPath } from 'lib/users/generateUserPath';
import { generateRandomName } from 'lib/utils/generateRandomName';

Expand Down
4 changes: 2 additions & 2 deletions apps/scoutgame/lib/session/getPendingNftTransactions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PendingNftTransaction, TransactionStatus } from '@charmverse/core/prisma-client';
import { prisma } from '@charmverse/core/prisma-client';
import { getPublicClient } from '@packages/onchain/getPublicClient';
import { waitForDecentTransactionSettlement } from '@packages/onchain/waitForDecentTransactionSettlement';
import { getPublicClient } from '@packages/blockchain/getPublicClient';
import { waitForDecentTransactionSettlement } from '@packages/blockchain/waitForDecentTransactionSettlement';

export type TxResponse = Pick<PendingNftTransaction, 'id' | 'sourceChainTxHash' | 'destinationChainTxHash' | 'status'>;

Expand Down
2 changes: 1 addition & 1 deletion apps/scoutgame/lib/session/loginWithFarcasterAction.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use server';

import { log } from '@charmverse/core/log';
import { findOrCreateFarcasterUser } from '@packages/scoutgame/users/findOrCreateFarcasterUser';
import { authSecret } from '@root/config/constants';
import { sealData } from 'iron-session';
import { cookies } from 'next/headers';

import { actionClient } from 'lib/actions/actionClient';
import { findOrCreateFarcasterUser } from 'lib/farcaster/findOrCreateFarcasterUser';
import { verifyFarcasterUser } from 'lib/farcaster/verifyFarcasterUser';

import { authSchema } from '../farcaster/config';
Expand Down
12 changes: 12 additions & 0 deletions apps/scoutgame/lib/session/loginWithWalletAction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use server';

import { trackUserAction } from '@packages/mixpanel/trackUserAction';

import { actionClient } from 'lib/actions/actionClient';
import { findOrCreateWalletUser } from 'lib/blockchain/findOrCreateWalletUser';
import { loginWithWalletSchema } from 'lib/blockchain/schema';
Expand All @@ -20,6 +22,16 @@ export const loginWithWalletAction = actionClient
await saveSession(ctx, { scoutId: user.id });
const sessionUser = (await getUserFromSession()) as SessionUser;

if (user.isNew) {
trackUserAction('sign_up', {
userId: user.id
});
} else {
trackUserAction('sign_in', {
userId: user.id
});
}

return {
user: sessionUser,
success: true,
Expand Down
31 changes: 0 additions & 31 deletions apps/scoutgame/scripts/onboardScouts.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/scoutgame/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@
"@charmverse/core/errors": ["../../node_modules/@charmverse/core/dist/cjs/errors"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "../../packages/scoutgame/src/users/generateRandomAvatar.ts", "../../packages/scoutgame/src/users/findOrCreateUser.ts"],
"exclude": ["../../node_modules", ".next/", "public/sw.js", "sw.js"]
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getTransactionStatusFromDecent } from '@packages/onchain/waitForDecentTransactionSettlement';
import { getTransactionStatusFromDecent } from '@packages/blockchain/waitForDecentTransactionSettlement';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

Expand Down
12 changes: 12 additions & 0 deletions apps/scoutgameadmin/app/api/users/create-builder/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { log } from '@charmverse/core/log';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

import { createBuilder } from 'lib/users/createBuilder';

export async function POST(request: NextRequest) {
const params = await request.json();
const newUser = await createBuilder(params);
log.info('Approved new builder', { newUser });
return NextResponse.json({ success: true });
}
9 changes: 6 additions & 3 deletions apps/scoutgameadmin/app/api/users/get-user/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { getUser } from 'lib/users/getUser';

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const searchString = searchParams.get('searchString');
const repos = await getUser({ searchString: searchString || '' });
return NextResponse.json(repos);
const userId = searchParams.get('userId');
if (!userId) {
return NextResponse.json({ error: 'userId is required' }, { status: 400 });
}
const user = await getUser(userId);
return NextResponse.json(user);
}
14 changes: 9 additions & 5 deletions apps/scoutgameadmin/app/api/users/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { log } from '@charmverse/core/log';
import { importReposByUser } from '@packages/scoutgame/importReposByUser';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

import { createUser } from 'lib/users/createUser';
import type { SortOrder, SortField } from 'lib/users/getUsers';
import { getUsers } from 'lib/users/getUsers';
import { searchForUser } from 'lib/users/searchForUser';

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
Expand All @@ -16,9 +17,12 @@ export async function GET(request: NextRequest) {
}

export async function POST(request: NextRequest) {
const { owner } = await request.json();

await importReposByUser(owner);

const params = await request.json();
const user = await searchForUser(params);
if (!user) {
throw new Error(`User not found: ${params.searchString}`);
}
const newUser = await createUser(user);
log.info('Created new user', { newUser });
return NextResponse.json({ success: true });
}
11 changes: 11 additions & 0 deletions apps/scoutgameadmin/app/api/users/search-for-user/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

import { searchForUser } from 'lib/users/searchForUser';

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const searchString = searchParams.get('searchString');
const user = await searchForUser({ searchString: searchString || '' });
return NextResponse.json(user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import React, { useState } from 'react';
import { mutate } from 'swr';

import { useCreateRepo, useSearchReposByOwnerFromGithub } from 'hooks/api/repos';
import { useCreateRepos, useSearchReposByOwnerFromGithub } from 'hooks/api/repos';
import { useDebouncedValue } from 'hooks/useDebouncedValue';

type Props = {
Expand All @@ -26,7 +26,7 @@ type Props = {

export function AddRepoModal({ open, onClose, onAdd }: Props) {
const [repoInput, setRepoInput] = useState('');
const { trigger: addGithubRepo, isMutating: isImporting } = useCreateRepo();
const { trigger: createRepos, isMutating: isImporting } = useCreateRepos();
const debouncedFilterString = useDebouncedValue(repoInput);
const {
data: reposFromGithub,
Expand All @@ -39,7 +39,7 @@ export function AddRepoModal({ open, onClose, onAdd }: Props) {

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await addGithubRepo({ owner: repoInput });
await createRepos({ owner: repoInput });
onAdd();
onClose();
setRepoInput('');
Expand All @@ -55,7 +55,7 @@ export function AddRepoModal({ open, onClose, onAdd }: Props) {
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { maxWidth: 400 } }} fullWidth>
<DialogTitle>Add new repos by owner</DialogTitle>
<form onSubmit={handleSubmit}>
<DialogContent sx={{ pt: 0 }}>
<DialogContent>
<TextField
autoFocus
margin='dense'
Expand Down
34 changes: 20 additions & 14 deletions apps/scoutgameadmin/components/users/AddUserButton/AddUserModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import React, { useState } from 'react';
import { mutate } from 'swr';

import { useCreateUser, useGetUser } from 'hooks/api/users';
import { useCreateUser, useSearchForUser } from 'hooks/api/users';
import { useDebouncedValue } from 'hooks/useDebouncedValue';

type Props = {
Expand All @@ -26,16 +26,16 @@ type Props = {
};

export function AddUserModal({ open, onClose, onAdd }: Props) {
const [repoInput, setRepoInput] = useState('');
const [repoInput, setTextInput] = useState('');
const { trigger: createUser, isMutating: isCreating } = useCreateUser();
const debouncedFilterString = useDebouncedValue(repoInput);
const { data: user, error, isValidating, isLoading } = useGetUser(debouncedFilterString);
const { data: user, error, isValidating, isLoading } = useSearchForUser(debouncedFilterString);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await createUser({ searchString: repoInput });
onAdd();
onClose();
setRepoInput('');
setTextInput('');
// clear SWR cache
mutate(
(key) => true, // which cache keys are updated
Expand All @@ -46,17 +46,17 @@ export function AddUserModal({ open, onClose, onAdd }: Props) {

return (
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { maxWidth: 400 } }} fullWidth>
<DialogTitle>Add or manage user</DialogTitle>
<DialogTitle>Add scout</DialogTitle>
<form onSubmit={handleSubmit}>
<DialogContent sx={{ pt: 0 }}>
<DialogContent>
<TextField
autoFocus
margin='dense'
label='User identifier'
type='text'
placeholder='Farcaster id, username or Github username'
fullWidth
value={repoInput}
onChange={(e) => setRepoInput(e.target.value)}
onChange={(e) => setTextInput(e.target.value)}
required
slotProps={{
input: {
Expand Down Expand Up @@ -98,11 +98,11 @@ export function AddUserModal({ open, onClose, onAdd }: Props) {
<Typography>
<span style={{ fontWeight: 'bold' }}>Points Balance:</span> {user.scout.currentBalance}
</Typography>
{user.scout.farcasterId && (
{user.scout.farcasterName && (
<Typography>
<span style={{ fontWeight: 'bold' }}>Farcaster:</span>{' '}
<Link href={`https://warpcast.com/${user.scout.path}`} target='_blank'>
{user.scout.path}
<Link href={`https://warpcast.com/${user.scout.farcasterName}`} target='_blank'>
{user.scout.farcasterName}
</Link>
</Typography>
)}
Expand All @@ -126,7 +126,9 @@ export function AddUserModal({ open, onClose, onAdd }: Props) {
</Typography>
</Box>
<Stack direction='row' spacing={2} justifyContent='flex-end'>
<Button onClick={onClose}>Cancel</Button>
<Button variant='outlined' color='secondary' onClick={onClose}>
Cancel
</Button>
{user.scout.builderStatus === 'applied' && (
<LoadingButton disabled loading={isCreating} type='submit' color='primary' variant='contained'>
Approve builder
Expand Down Expand Up @@ -164,7 +166,9 @@ export function AddUserModal({ open, onClose, onAdd }: Props) {
</Typography>
</Box>
<Stack direction='row' spacing={2} justifyContent='flex-end'>
<Button onClick={onClose}>Cancel</Button>
<Button variant='outlined' color='secondary' onClick={onClose}>
Cancel
</Button>
<LoadingButton loading={isCreating} type='submit' color='primary' variant='contained'>
Add {user.waitlistUser.githubLogin ? 'builder' : 'scout'}
</LoadingButton>
Expand All @@ -186,7 +190,9 @@ export function AddUserModal({ open, onClose, onAdd }: Props) {
<Typography>Following: {user.farcasterUser.following_count}</Typography>
</Box>
<Stack direction='row' spacing={2} justifyContent='flex-end'>
<Button onClick={onClose}>Cancel</Button>
<Button variant='outlined' color='secondary' onClick={onClose}>
Cancel
</Button>
<LoadingButton loading={isCreating} type='submit' color='primary' variant='contained'>
Add scout
</LoadingButton>
Expand Down
Loading

0 comments on commit 7249e64

Please sign in to comment.