invite graphql + basic frontend

This commit is contained in:
keyan 2021-10-12 18:49:04 -05:00
parent 09c4883366
commit 4935c7dc1c
8 changed files with 190 additions and 3 deletions

View File

@ -4,5 +4,6 @@ import item from './item'
import wallet from './wallet' import wallet from './wallet'
import lnurl from './lnurl' import lnurl from './lnurl'
import notifications from './notifications' import notifications from './notifications'
import invite from './invite'
export default [user, item, message, wallet, lnurl, notifications] export default [user, item, message, wallet, lnurl, notifications, invite]

37
api/resolvers/invite.js Normal file
View File

@ -0,0 +1,37 @@
import { AuthenticationError, UserInputError } from 'apollo-server-micro'
export default {
Query: {
invites: async (parent, args, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
return await models.invite.findMany({
where: {
userId: me.id
}
})
}
},
Mutation: {
createInvite: async (parent, { gift, limit }, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
if (!gift || (gift && gift < 0)) {
throw new UserInputError('gift must be >= 0', { argumentName: 'gift' })
}
return await models.invite.create({
data: { gift, limit, userId: me.id }
})
}
},
Invite: {
invitees: async (invite, args, { models }) => []
}
}

View File

@ -6,6 +6,7 @@ import item from './item'
import wallet from './wallet' import wallet from './wallet'
import lnurl from './lnurl' import lnurl from './lnurl'
import notifications from './notifications' import notifications from './notifications'
import invite from './invite'
const link = gql` const link = gql`
type Query { type Query {
@ -21,4 +22,4 @@ const link = gql`
} }
` `
export default [link, user, item, message, wallet, lnurl, notifications] export default [link, user, item, message, wallet, lnurl, notifications, invite]

20
api/typeDefs/invite.js Normal file
View File

@ -0,0 +1,20 @@
import { gql } from 'apollo-server-micro'
export default gql`
extend type Query {
invites: [Invite!]!
}
extend type Mutation {
createInvite(gift: Int!, limit: Int): Invite
}
type Invite {
id: ID!
createdAt: String!
invitees: [User!]!
gift: Int!
limit: Int
revoked: Boolean!
}
`

15
fragments/invites.js Normal file
View File

@ -0,0 +1,15 @@
import { gql } from '@apollo/client'
export const INVITE_FIELDS = gql`
fragment InviteFields on Invite {
id
createdAt
invitees {
name
id
}
gift
limit
revoked
}
`

93
pages/invites/index.js Normal file
View File

@ -0,0 +1,93 @@
import Layout from '../../components/layout'
import * as Yup from 'yup'
import { Form, Input, SubmitButton } from '../../components/form'
import { InputGroup } from 'react-bootstrap'
import { gql, useMutation, useQuery } from '@apollo/client'
import { INVITE_FIELDS } from '../../fragments/invites'
export const InviteSchema = Yup.object({
gift: Yup.number().typeError('must be a number')
.min(0, 'must be positive').integer('must be whole').required(),
limit: Yup.number().typeError('must be a number')
.positive('must be positive').integer('must be whole')
})
function InviteForm () {
const [createInvite] = useMutation(
gql`
${INVITE_FIELDS}
mutation createInvite($gift: Int!, $limit: Int) {
createInvite(gift: $gift, limit: $limit) {
...InviteFields
}
}`, {
update (cache, { data: { createInvite } }) {
cache.modify({
fields: {
invites (existingInviteRefs = []) {
const newInviteRef = cache.writeFragment({
data: createInvite,
fragment: INVITE_FIELDS
})
return [newInviteRef, ...existingInviteRefs]
}
}
})
}
}
)
return (
<Form
initial={{
gift: 100,
limit: undefined
}}
schema={InviteSchema}
onSubmit={async ({ limit, ...values }) => {
const { error } = await createInvite({
variables: {
...values, limit: limit ? Number(limit) : limit
}
})
if (error) {
throw new Error({ message: error.String() })
}
}}
>
<Input
label='gift'
name='gift'
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
required
autoFocus
/>
<Input
label={<>invitee limit <small className='text-muted ml-2'>optional</small></>}
name='limit'
/>
<SubmitButton variant='secondary' className='mt-2'>create</SubmitButton>
</Form>
)
}
export default function Invites () {
const { data } = useQuery(
gql`
${INVITE_FIELDS}
{
invites {
...InviteFields
}
}
`)
return (
<Layout>
<InviteForm />
{data && data.invites && data.invites.map(invite => {
return <div key={invite.id}>{invite.id}</div>
})}
</Layout>
)
}

View File

@ -12,7 +12,6 @@ export async function getServerSideProps () {
} }
} }
export function PostForm () { export function PostForm () {
const router = useRouter() const router = useRouter()
const me = useMe() const me = useMe()

View File

@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "inviteId" TEXT;
-- CreateTable
CREATE TABLE "Invite" (
"id" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" INTEGER NOT NULL,
"gift" INTEGER,
"limit" INTEGER,
"revoked" BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "users" ADD FOREIGN KEY ("inviteId") REFERENCES "Invite"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Invite" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;