diff --git a/api/resolvers/invite.js b/api/resolvers/invite.js
index c2ad92dd..226debe6 100644
--- a/api/resolvers/invite.js
+++ b/api/resolvers/invite.js
@@ -2,6 +2,7 @@ import { inviteSchema, validateSchema } from '@/lib/validate'
import { msatsToSats } from '@/lib/format'
import assertApiKeyNotPermitted from './apiKey'
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
+import { Prisma } from '@prisma/client'
export default {
Query: {
@@ -9,7 +10,6 @@ export default {
if (!me) {
throw new GqlAuthenticationError()
}
-
return await models.invite.findMany({
where: {
userId: me.id
@@ -29,17 +29,31 @@ export default {
},
Mutation: {
- createInvite: async (parent, { gift, limit }, { me, models }) => {
+ createInvite: async (parent, { id, gift, limit, description }, { me, models }) => {
if (!me) {
throw new GqlAuthenticationError()
}
assertApiKeyNotPermitted({ me })
- await validateSchema(inviteSchema, { gift, limit })
-
- return await models.invite.create({
- data: { gift, limit, userId: me.id }
- })
+ await validateSchema(inviteSchema, { id, gift, limit, description })
+ try {
+ return await models.invite.create({
+ data: {
+ id,
+ gift,
+ limit,
+ userId: me.id,
+ description
+ }
+ })
+ } catch (error) {
+ if (error instanceof Prisma.PrismaClientKnownRequestError) {
+ if (error.code === 'P2002' && error.meta.target.includes('id')) {
+ throw new GqlInputError('an invite with this code already exists')
+ }
+ }
+ throw error
+ }
},
revokeInvite: async (parent, { id }, { me, models }) => {
if (!me) {
@@ -70,6 +84,9 @@ export default {
poor: async (invite, args, { me, models }) => {
const user = await models.user.findUnique({ where: { id: invite.userId } })
return msatsToSats(user.msats) < invite.gift
+ },
+ description: (invite, args, { me }) => {
+ return invite.userId === me?.id ? invite.description : undefined
}
}
}
diff --git a/api/typeDefs/invite.js b/api/typeDefs/invite.js
index 93e8f964..038447d8 100644
--- a/api/typeDefs/invite.js
+++ b/api/typeDefs/invite.js
@@ -7,7 +7,7 @@ export default gql`
}
extend type Mutation {
- createInvite(gift: Int!, limit: Int): Invite
+ createInvite(id: String, gift: Int!, limit: Int, description: String): Invite
revokeInvite(id: ID!): Invite
}
@@ -20,5 +20,6 @@ export default gql`
user: User!
revoked: Boolean!
poor: Boolean!
+ description: String
}
`
diff --git a/components/invite.js b/components/invite.js
index cd2a08ca..9eddbc43 100644
--- a/components/invite.js
+++ b/components/invite.js
@@ -20,6 +20,7 @@ export default function Invite ({ invite, active }) {
+ {invite.description &&
{invite.description}}
your invite has been redeemed by
- {numWithUnits(n.invite.invitees.length, {
+ {' ' + numWithUnits(n.invite.invitees.length, {
abbreviate: false,
unitSingular: 'stacker',
unitPlural: 'stackers'
diff --git a/components/reply.js b/components/reply.js
index eb6c0c41..71427b47 100644
--- a/components/reply.js
+++ b/components/reply.js
@@ -113,7 +113,7 @@ export default forwardRef(function Reply ({
}
},
onSuccessfulSubmit: (data, { resetForm }) => {
- resetForm({ text: '' })
+ resetForm({ values: { text: '' } })
setReply(replyOpen || false)
},
navigateOnSubmit: false
diff --git a/fragments/invites.js b/fragments/invites.js
index 038ba53f..1abc5488 100644
--- a/fragments/invites.js
+++ b/fragments/invites.js
@@ -19,5 +19,6 @@ export const INVITE_FIELDS = gql`
...StreakFields
}
poor
+ description
}
`
diff --git a/lib/validate.js b/lib/validate.js
index bb4de8c5..39de723b 100644
--- a/lib/validate.js
+++ b/lib/validate.js
@@ -478,7 +478,9 @@ export const bioSchema = object({
export const inviteSchema = object({
gift: intValidator.positive('must be greater than 0').required('required'),
- limit: intValidator.positive('must be positive')
+ limit: intValidator.positive('must be positive'),
+ description: string().trim().max(40, 'must be at most 40 characters'),
+ id: string().matches(/^[\w-_]+$/, 'only letters, numbers, underscores, and hyphens').min(4, 'must be at least 4 characters').max(32, 'must be at most 32 characters')
})
export const pushSubscriptionSchema = object({
diff --git a/pages/invites/index.js b/pages/invites/index.js
index 37c71b13..a7ee743d 100644
--- a/pages/invites/index.js
+++ b/pages/invites/index.js
@@ -9,6 +9,8 @@ import Invite from '@/components/invite'
import { inviteSchema } from '@/lib/validate'
import { SSR } from '@/lib/constants'
import { getGetServerSideProps } from '@/api/ssrApollo'
+import Info from '@/components/info'
+import Text from '@/components/text'
// force SSR to include CSP nonces
export const getServerSideProps = getGetServerSideProps({ query: null })
@@ -17,8 +19,8 @@ function InviteForm () {
const [createInvite] = useMutation(
gql`
${INVITE_FIELDS}
- mutation createInvite($gift: Int!, $limit: Int) {
- createInvite(gift: $gift, limit: $limit) {
+ mutation createInvite($id: String, $gift: Int!, $limit: Int, $description: String) {
+ createInvite(id: $id, gift: $gift, limit: $limit, description: $description) {
...InviteFields
}
}`, {
@@ -39,20 +41,28 @@ function InviteForm () {
}
)
+ const initialValues = {
+ id: '',
+ gift: 1000,
+ limit: 1,
+ description: ''
+ }
+
return (
)
}
diff --git a/prisma/migrations/20241125195641_custom_invites/migration.sql b/prisma/migrations/20241125195641_custom_invites/migration.sql
new file mode 100644
index 00000000..aa7e66a5
--- /dev/null
+++ b/prisma/migrations/20241125195641_custom_invites/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Invite" ADD COLUMN "description" TEXT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index cd726fcd..94eac2fb 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -477,6 +477,8 @@ model Invite {
user User @relation("Invites", fields: [userId], references: [id], onDelete: Cascade)
invitees User[]
+ description String?
+
@@index([createdAt], map: "Invite.created_at_index")
@@index([userId], map: "Invite.userId_index")
}