From 4935c7dc1cb2b25fe91afc4723590acdfb8224d1 Mon Sep 17 00:00:00 2001 From: keyan Date: Tue, 12 Oct 2021 18:49:04 -0500 Subject: [PATCH] invite graphql + basic frontend --- api/resolvers/index.js | 3 +- api/resolvers/invite.js | 37 ++++++++ api/typeDefs/index.js | 3 +- api/typeDefs/invite.js | 20 ++++ fragments/invites.js | 15 +++ pages/invites/index.js | 93 +++++++++++++++++++ pages/post.js | 1 - .../migration.sql | 21 +++++ 8 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 api/resolvers/invite.js create mode 100644 api/typeDefs/invite.js create mode 100644 fragments/invites.js create mode 100644 pages/invites/index.js create mode 100644 prisma/migrations/20211012203507_invite_modeld/migration.sql diff --git a/api/resolvers/index.js b/api/resolvers/index.js index 91a04e7b..9461af1c 100644 --- a/api/resolvers/index.js +++ b/api/resolvers/index.js @@ -4,5 +4,6 @@ import item from './item' import wallet from './wallet' import lnurl from './lnurl' 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] diff --git a/api/resolvers/invite.js b/api/resolvers/invite.js new file mode 100644 index 00000000..da640c13 --- /dev/null +++ b/api/resolvers/invite.js @@ -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 }) => [] + } +} diff --git a/api/typeDefs/index.js b/api/typeDefs/index.js index 093272c7..0df0d91b 100644 --- a/api/typeDefs/index.js +++ b/api/typeDefs/index.js @@ -6,6 +6,7 @@ import item from './item' import wallet from './wallet' import lnurl from './lnurl' import notifications from './notifications' +import invite from './invite' const link = gql` 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] diff --git a/api/typeDefs/invite.js b/api/typeDefs/invite.js new file mode 100644 index 00000000..23450db5 --- /dev/null +++ b/api/typeDefs/invite.js @@ -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! + } +` diff --git a/fragments/invites.js b/fragments/invites.js new file mode 100644 index 00000000..4ed25a8b --- /dev/null +++ b/fragments/invites.js @@ -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 + } +` diff --git a/pages/invites/index.js b/pages/invites/index.js new file mode 100644 index 00000000..0d23cf80 --- /dev/null +++ b/pages/invites/index.js @@ -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 ( +
{ + const { error } = await createInvite({ + variables: { + ...values, limit: limit ? Number(limit) : limit + } + }) + if (error) { + throw new Error({ message: error.String() }) + } + }} + > + sats} + required + autoFocus + /> + invitee limit optional} + name='limit' + /> + + create +
+ ) +} + +export default function Invites () { + const { data } = useQuery( + gql` + ${INVITE_FIELDS} + { + invites { + ...InviteFields + } + } + `) + return ( + + + {data && data.invites && data.invites.map(invite => { + return
{invite.id}
+ })} +
+ ) +} diff --git a/pages/post.js b/pages/post.js index 3a8c86fd..df69dbb3 100644 --- a/pages/post.js +++ b/pages/post.js @@ -12,7 +12,6 @@ export async function getServerSideProps () { } } - export function PostForm () { const router = useRouter() const me = useMe() diff --git a/prisma/migrations/20211012203507_invite_modeld/migration.sql b/prisma/migrations/20211012203507_invite_modeld/migration.sql new file mode 100644 index 00000000..4b57b22d --- /dev/null +++ b/prisma/migrations/20211012203507_invite_modeld/migration.sql @@ -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;