invoiced ... WIP transactions
This commit is contained in:
parent
764a683500
commit
bc0389e622
|
@ -107,7 +107,7 @@ export default {
|
|||
throw new UserInputError('Sats must be positive', { argumentName: 'sats' })
|
||||
}
|
||||
|
||||
// check if we've already voted for the item
|
||||
// TODO: check if we've already voted for the item ... XXX this isn't transactional
|
||||
const boosted = await models.vote.findFirst({
|
||||
where: {
|
||||
itemId: parseInt(id),
|
||||
|
@ -143,7 +143,7 @@ export default {
|
|||
SELECT count(*)
|
||||
FROM "Item"
|
||||
WHERE path <@ text2ltree(${item.path}) AND id != ${item.id}`
|
||||
return count
|
||||
return count || 0
|
||||
},
|
||||
sats: async (item, args, { models }) => {
|
||||
const { sum: { sats } } = await models.vote.aggregate({
|
||||
|
@ -156,7 +156,7 @@ export default {
|
|||
}
|
||||
})
|
||||
|
||||
return sats
|
||||
return sats || 0
|
||||
},
|
||||
boost: async (item, args, { models }) => {
|
||||
const { sum: { sats } } = await models.vote.aggregate({
|
||||
|
@ -169,7 +169,7 @@ export default {
|
|||
}
|
||||
})
|
||||
|
||||
return sats
|
||||
return sats || 0
|
||||
},
|
||||
meSats: async (item, args, { me, models }) => {
|
||||
if (!me) return 0
|
||||
|
@ -184,7 +184,7 @@ export default {
|
|||
}
|
||||
})
|
||||
|
||||
return sats
|
||||
return sats || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
Query: {
|
||||
me: async (parent, args, { models, me }) =>
|
||||
me ? await models.user.findUnique({ where: { id: me.id } }) : null,
|
||||
me ? await models.user.findUnique({ where: { name: me.name } }) : null,
|
||||
user: async (parent, { name }, { models }) => {
|
||||
return await models.user.findUnique({ where: { name } })
|
||||
},
|
||||
|
@ -22,8 +22,10 @@ export default {
|
|||
FROM "Item"
|
||||
LEFT JOIN "Vote" on "Vote"."itemId" = "Item".id
|
||||
WHERE "Item"."userId" = ${user.id}`
|
||||
return sum
|
||||
return sum || 0
|
||||
},
|
||||
sats: () => 0
|
||||
sats: async (user, args, { models }) => {
|
||||
return Math.floor(user.msats / 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,52 @@
|
|||
import { createInvoice } from 'ln-service'
|
||||
import { UserInputError, AuthenticationError } from 'apollo-server-micro'
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
invoice: async (parent, { id }, { me, models, lnd }) => {
|
||||
return 'lnbc1500n1psfxyaypp5tmlgpudspqed4qf32xxmc7dhlqrd4glc09x794exz4t2pw8ms38sdpa2fjkzep6yptks7fqt9hh2gzwv4jkggz5dus9gatjdcsyzmrvypvk7atjypzx7cqzpgxqr23ssp529tup4vaxlxnst0lwh9kljpl9n6zg6n6vma5hw78lmnws32x278s9qyyssqxe73jclrlz3u7v7ruwee3n7h70ktsdsfmvpfjkccqxq5wg5h6njhqxar0a9fef5hd09ethwhvsj0dha2qy4tjjdxu08nkqymfs8wghqp6d7kth'
|
||||
return await models.invoice.findUnique({ where: { id: Number(id) } })
|
||||
}
|
||||
},
|
||||
|
||||
Mutation: {
|
||||
createInvoice: async (parent, { amount }, { me, models, lnd }) => {
|
||||
return 'lnbc1500n1psfxyaypp5tmlgpudspqed4qf32xxmc7dhlqrd4glc09x794exz4t2pw8ms38sdpa2fjkzep6yptks7fqt9hh2gzwv4jkggz5dus9gatjdcsyzmrvypvk7atjypzx7cqzpgxqr23ssp529tup4vaxlxnst0lwh9kljpl9n6zg6n6vma5hw78lmnws32x278s9qyyssqxe73jclrlz3u7v7ruwee3n7h70ktsdsfmvpfjkccqxq5wg5h6njhqxar0a9fef5hd09ethwhvsj0dha2qy4tjjdxu08nkqymfs8wghqp6d7kth'
|
||||
if (!me) {
|
||||
throw new AuthenticationError('You must be logged in')
|
||||
}
|
||||
|
||||
if (!amount || amount <= 0) {
|
||||
throw new UserInputError('Amount must be positive', { argumentName: 'amount' })
|
||||
}
|
||||
|
||||
/*
|
||||
chain_address: undefined,
|
||||
created_at: '2021-05-06T22:16:28.000Z',
|
||||
description: 'hi there',
|
||||
id: '30946d6ff432933e30f6c180cce982c92b509a80bf6c2e896e6579cbda4c1677',
|
||||
mtokens: '1000',
|
||||
payment: 'e3deb7a0471bf050aa5dd0ef9b546887ab1fdf0306a7cb67d9dda8473f9542f2',
|
||||
request: 'lnbcrt10n1psfg64upp5xz2x6ml5x2fnuv8kcxqve6vzey44px5qhakzaztwv4uuhkjvzemsdqddp5jqargv4ex2cqzpgxqr23ssp5u00t0gz8r0c9p2ja6rhek4rgs743lhcrq6nuke7emk5yw0u4gteq9q8zqqyssq92epsvsap3pyfcj4kex5vysew4tqg6c8vxux5nfmc7yqx36l6dk49pafs62dlr92lm5ekzftl7nq6r4wvjhwydtekg6lpj0xgjm5auqpwflxyk',
|
||||
secret: '82abf620f82dc9a61cf3921f77432e31d4a11e1dc066ccc177d31937c473eb30',
|
||||
tokens: 1
|
||||
*/
|
||||
// set expires at to 3 hours into future
|
||||
const expiresAt = new Date(new Date().setHours(new Date().getHours() + 3))
|
||||
const description = `${amount} sats for @${me.name} on stacker.news`
|
||||
const invoice = await createInvoice({ description, lnd, tokens: amount, expires_at: expiresAt })
|
||||
|
||||
const data = {
|
||||
hash: invoice.id,
|
||||
bolt11: invoice.request,
|
||||
expiresAt: expiresAt,
|
||||
msatsRequested: amount * 1000,
|
||||
user: {
|
||||
connect: {
|
||||
name: me.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await models.invoice.create({ data })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,20 @@ import { gql } from 'apollo-server-micro'
|
|||
|
||||
export default gql`
|
||||
extend type Query {
|
||||
invoice(id: ID!): String!
|
||||
invoice(id: ID!): Invoice!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createInvoice(amount: Int!): String!
|
||||
createInvoice(amount: Int!): Invoice!
|
||||
}
|
||||
|
||||
type Invoice {
|
||||
id: ID!
|
||||
createdAt: String!
|
||||
bolt11: String!
|
||||
expiresAt: String!
|
||||
cancelled: Boolean!
|
||||
confirmedAt: String
|
||||
msatsReceived: Int
|
||||
}
|
||||
`
|
||||
|
|
|
@ -6,6 +6,21 @@ import styles from './header.module.css'
|
|||
import { useRouter } from 'next/router'
|
||||
import { Button, Container, NavDropdown } from 'react-bootstrap'
|
||||
import Price from './price'
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
|
||||
function WalletSummary () {
|
||||
const query = gql`
|
||||
{
|
||||
me {
|
||||
sats
|
||||
stacked
|
||||
}
|
||||
}`
|
||||
const { data } = useQuery(query, { pollInterval: 1000 })
|
||||
if (!data) return null
|
||||
|
||||
return `[${data.me.stacked},${data.me.sats}]`
|
||||
}
|
||||
|
||||
export default function Header () {
|
||||
const [session, loading] = useSession()
|
||||
|
@ -42,7 +57,7 @@ export default function Header () {
|
|||
</NavDropdown>
|
||||
<Nav.Item>
|
||||
<Link href='/wallet' passHref>
|
||||
<Nav.Link className='text-success px-0'>[0,0]</Nav.Link>
|
||||
<Nav.Link className='text-success px-0'><WalletSummary /></Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
</div>
|
||||
|
|
|
@ -6,51 +6,84 @@ import Thumb from '../svgs/thumb-up-fill.svg'
|
|||
import { useState } from 'react'
|
||||
import BootstrapForm from 'react-bootstrap/Form'
|
||||
import Button from 'react-bootstrap/Button'
|
||||
import Check from '../svgs/check-double-line.svg'
|
||||
import Fail from '../svgs/close-line.svg'
|
||||
|
||||
export function Invoice ({ invoice }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const qrValue = 'lightning:' + invoice.toUpperCase()
|
||||
const qrValue = 'lightning:' + invoice.bolt11.toUpperCase()
|
||||
|
||||
let InvoiceStatus = InvoiceDefaultStatus
|
||||
let status = 'waiting for you'
|
||||
if (invoice.confirmedAt) {
|
||||
InvoiceStatus = InvoiceConfirmedStatus
|
||||
status = `${invoice.msatsReceived / 1000} sats deposited`
|
||||
} else if (invoice.cancelled) {
|
||||
InvoiceStatus = InvoiceFailedStatus
|
||||
status = 'cancelled'
|
||||
} else if (invoice.expiresAt <= new Date()) {
|
||||
InvoiceStatus = InvoiceFailedStatus
|
||||
status = 'expired'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<QRCode className='h-auto mw-100' value={qrValue} size={300} />
|
||||
<QRCode className='h-auto mw-100' value={qrValue} renderAs='svg' size={300} />
|
||||
</div>
|
||||
<div className='mt-3 w-100'>
|
||||
<InputGroup onClick={() => {
|
||||
copy(invoice)
|
||||
copy(invoice.bolt11)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
}}
|
||||
>
|
||||
<BootstrapForm.Control type='text' placeholder={invoice} readOnly />
|
||||
<BootstrapForm.Control type='text' placeholder={invoice.bolt11} readOnly />
|
||||
<InputGroup.Append>
|
||||
<Button>{copied ? <Thumb width={20} height={20} /> : 'copy'}</Button>
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<InvoiceStatus />
|
||||
<InvoiceStatus status={status} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function InvoiceStatus ({ skeleton }) {
|
||||
export function InvoiceDefaultStatus ({ status }) {
|
||||
return (
|
||||
<div className='d-flex mt-4'>
|
||||
<Moon className='spin fill-grey' />
|
||||
<div className='ml-3 text-muted' style={{ fontWeight: '600' }}>{skeleton ? 'generating' : 'waiting for you'}</div>
|
||||
<div className='ml-3 text-muted' style={{ fontWeight: '600' }}>{status}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function InvoiceSkeleton () {
|
||||
export function InvoiceConfirmedStatus ({ status }) {
|
||||
return (
|
||||
<div className='d-flex mt-4'>
|
||||
<Check className='fill-success' />
|
||||
<div className='ml-3 text-success' style={{ fontWeight: '600' }}>{status}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function InvoiceFailedStatus ({ status }) {
|
||||
return (
|
||||
<div className='d-flex mt-4'>
|
||||
<Fail className='fill-danger' />
|
||||
<div className='ml-3 text-danger' style={{ fontWeight: '600' }}>{status}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function InvoiceSkeleton ({ status }) {
|
||||
return (
|
||||
<>
|
||||
<div className='h-auto w-100 clouds' style={{ paddingTop: 'min(300px, 100%)', maxWidth: '300px' }} />
|
||||
<div className='mt-3 w-100'>
|
||||
<div className='w-100 clouds form-control' />
|
||||
</div>
|
||||
<InvoiceStatus skeleton />
|
||||
<InvoiceDefaultStatus status={status} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ export default function UserHeader ({ user }) {
|
|||
<Nav.Link>{user.ncomments} comments</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
{/* <Nav.Item>
|
||||
<Link href={'/' + user.name + '/sativity'} passHref>
|
||||
<Nav.Link>sativity</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
</Nav.Item> */}
|
||||
</Nav>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
const { PrismaClient } = require('@prisma/client')
|
||||
const { authenticatedLndGrpc, subscribeToInvoices, getInvoice } = require('ln-service')
|
||||
const dotenv = require('dotenv')
|
||||
|
||||
dotenv.config({ path: '..' })
|
||||
|
||||
const { lnd } = authenticatedLndGrpc({
|
||||
cert: process.env.LND_CERT,
|
||||
macaroon: process.env.LND_MACAROON,
|
||||
socket: process.env.LND_SOCKET
|
||||
})
|
||||
|
||||
const models = new PrismaClient()
|
||||
|
||||
async function recordStatus (inv) {
|
||||
console.log(inv)
|
||||
if (inv.is_confirmed) {
|
||||
const received = Number(inv.received_mtokens)
|
||||
|
||||
// only increment iff this invoice has not yet confirmed
|
||||
const updateUser = models.user.updateMany({
|
||||
where: {
|
||||
invoices: {
|
||||
some: {
|
||||
hash: inv.id,
|
||||
confirmedAt: {
|
||||
equals: null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: {
|
||||
msats: {
|
||||
increment: received
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ATOMICALLY (with above) mark the invoice as confirmed
|
||||
const updateInvoice = models.invoice.updateMany({
|
||||
where: {
|
||||
hash: inv.id,
|
||||
AND: [
|
||||
{
|
||||
confirmedAt: {
|
||||
equals: null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
data: {
|
||||
confirmedAt: inv.confirmed_at,
|
||||
msatsReceived: received
|
||||
}
|
||||
})
|
||||
|
||||
models.$transaction([updateUser, updateInvoice])
|
||||
} else if (inv.is_canceled) {
|
||||
// mark as cancelled
|
||||
models.invoice.update({
|
||||
where: {
|
||||
hash: inv.id
|
||||
},
|
||||
data: {
|
||||
cancelled: true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1. subscribe to all invoices async
|
||||
const sub = subscribeToInvoices({ lnd })
|
||||
sub.on('invoice_updated', recordStatus)
|
||||
|
||||
// 2. check all pending invoices from db in lnd
|
||||
async function checkPending () {
|
||||
const now = new Date()
|
||||
const active = await models.invoice.findMany({
|
||||
where: {
|
||||
expiresAt: {
|
||||
gt: now
|
||||
},
|
||||
cancelled: false,
|
||||
confirmedAt: {
|
||||
equals: null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
active.forEach(async invoice => {
|
||||
try {
|
||||
const inv = await getInvoice({ id: invoice.hash, lnd })
|
||||
recordStatus(inv)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
checkPending()
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.3.13",
|
||||
"@prisma/client": "^2.19.0",
|
||||
"@prisma/client": "^2.22.1",
|
||||
"apollo-server-micro": "^2.21.2",
|
||||
"bootstrap": "^4.6.0",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"babel-plugin-inline-react-svg": "^2.0.1",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-plugin-compat": "^3.9.0",
|
||||
"prisma": "2.19.0",
|
||||
"prisma": "^2.22.1",
|
||||
"standard": "^16.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,14 @@ export async function getServerSideProps ({ params: { id } }) {
|
|||
export default function FullInvoice ({ id }) {
|
||||
const query = gql`
|
||||
{
|
||||
invoice(id: ${id})
|
||||
invoice(id: ${id}) {
|
||||
id
|
||||
bolt11
|
||||
msatsReceived
|
||||
cancelled
|
||||
confirmedAt
|
||||
expiresAt
|
||||
}
|
||||
}`
|
||||
return (
|
||||
<LayoutCenter>
|
||||
|
@ -24,10 +31,10 @@ export default function FullInvoice ({ id }) {
|
|||
}
|
||||
|
||||
function LoadInvoice ({ query }) {
|
||||
const { loading, error, data } = useQuery(query)
|
||||
const { loading, error, data } = useQuery(query, { pollInterval: 1000 })
|
||||
if (error) return <div>error</div>
|
||||
if (!data || loading) {
|
||||
return <InvoiceSkeleton />
|
||||
return <InvoiceSkeleton status='loading' />
|
||||
}
|
||||
|
||||
return <Invoice invoice={data.invoice} />
|
||||
|
|
|
@ -47,11 +47,13 @@ export function FundForm () {
|
|||
const router = useRouter()
|
||||
const [createInvoice, { called }] = useMutation(gql`
|
||||
mutation createInvoice($amount: Int!) {
|
||||
createInvoice(amount: $amount)
|
||||
createInvoice(amount: $amount) {
|
||||
id
|
||||
}
|
||||
}`)
|
||||
|
||||
if (called) {
|
||||
return <InvoiceSkeleton />
|
||||
return <InvoiceSkeleton status='generating' />
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -61,8 +63,8 @@ export function FundForm () {
|
|||
}}
|
||||
schema={FundSchema}
|
||||
onSubmit={async ({ amount }) => {
|
||||
await createInvoice({ variables: { amount } })
|
||||
router.push('/invoices/1')
|
||||
const { data } = await createInvoice({ variables: { amount: Number(amount) } })
|
||||
router.push(`/invoices/${data.createInvoice.id}`)
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
-- DropIndex
|
||||
DROP INDEX "item_gist_path_index";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Invoice" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"hash" TEXT NOT NULL,
|
||||
"bolt11" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"confirmedAt" TIMESTAMP(3),
|
||||
"requested" INTEGER NOT NULL,
|
||||
"received" INTEGER,
|
||||
"cancelled" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Invoice.userId_index" ON "Invoice"("userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Invoice" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `requested` on the `Invoice` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `received` on the `Invoice` table. All the data in the column will be lost.
|
||||
- Added the required column `msatsRequested` to the `Invoice` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Invoice" DROP COLUMN "requested",
|
||||
DROP COLUMN "received",
|
||||
ADD COLUMN "msatsRequested" INTEGER NOT NULL,
|
||||
ADD COLUMN "msatsReceived" INTEGER;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "msats" INTEGER NOT NULL DEFAULT 0;
|
|
@ -21,6 +21,8 @@ model User {
|
|||
items Item[]
|
||||
messages Message[]
|
||||
votes Vote[]
|
||||
invoices Invoice[]
|
||||
msats Int @default(0)
|
||||
|
||||
@@map(name: "users")
|
||||
}
|
||||
|
@ -66,6 +68,24 @@ model Vote {
|
|||
@@index([userId])
|
||||
}
|
||||
|
||||
model Invoice {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
|
||||
hash String @unique
|
||||
bolt11 String
|
||||
expiresAt DateTime
|
||||
confirmedAt DateTime?
|
||||
msatsRequested Int
|
||||
msatsReceived Int?
|
||||
cancelled Boolean @default(false)
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model Account {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
|
|
|
@ -117,6 +117,14 @@ body {
|
|||
fill: grey;
|
||||
}
|
||||
|
||||
.fill-success {
|
||||
fill: #5c8001;
|
||||
}
|
||||
|
||||
.fill-danger {
|
||||
fill: #c03221;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
from { filter: brightness(1);}
|
||||
2% { filter: brightness(2.3); }
|
||||
|
@ -154,4 +162,30 @@ body {
|
|||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
opacity: .1;
|
||||
}
|
||||
|
||||
@keyframes flipX{
|
||||
from {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
to {
|
||||
transform: rotateX(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.flipX {
|
||||
animation: flipX 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes flipY{
|
||||
from {
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.flipY {
|
||||
animation: flipY 4s linear infinite;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.602 13.76l1.412 1.412 8.466-8.466 1.414 1.414-9.88 9.88-6.364-6.364 1.414-1.414 2.125 2.125 1.413 1.412zm.002-2.828l4.952-4.953 1.41 1.41-4.952 4.953-1.41-1.41zm-2.827 5.655L7.364 18 1 11.636l1.414-1.414 1.413 1.413-.001.001 4.951 4.951z"/></svg>
|
After Width: | Height: | Size: 379 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
|
After Width: | Height: | Size: 266 B |
36
yarn.lock
36
yarn.lock
|
@ -340,22 +340,22 @@
|
|||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
|
||||
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
|
||||
|
||||
"@prisma/client@^2.19.0":
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.19.0.tgz#a45f17a59fd109e95b61bf4b56d4a7642169ec0e"
|
||||
integrity sha512-QK4M8TjJh1QesyO9aLM7DeAQUi5+UnNHpEAm5kwqBO1cq/4Ag5yU9ladctJFJleEE5BLewXHwV2t9A+VfCZslg==
|
||||
"@prisma/client@^2.22.1":
|
||||
version "2.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.22.1.tgz#10fdcd1532a6baf46dd1c464cad9a54af0532bc8"
|
||||
integrity sha512-JQjbsY6QSfFiovXHEp5WeJHa5p2CuR1ZFPAeYXmUsOAQOaMCrhgQmKAL6w2Q3SRA7ALqPjrKywN9/QfBc4Kp1A==
|
||||
dependencies:
|
||||
"@prisma/engines-version" "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d"
|
||||
"@prisma/engines-version" "2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c"
|
||||
|
||||
"@prisma/engines-version@2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d":
|
||||
version "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d.tgz#a7f80d481ec6cb8e2975ab530664d4ca5fc9eba6"
|
||||
integrity sha512-NzhbwC4iMbRQwJxdhNQX6eaVcOuNGtHRk6aesWE4KMf/YmlW5kfi3HDy7WZ/C4P0Iyn9oURDuk+xZV6QDUVjTw==
|
||||
"@prisma/engines-version@2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c":
|
||||
version "2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c.tgz#e98ee17217a0ebb54f2f9314fbbfd610b05e6e31"
|
||||
integrity sha512-OkkVwk6iTzTbwwl8JIKAENyxmh4TFORal55QMKQzrHEY8UzbD0M90mQnmziz3PAopQUZgTFFMlaPAq1WNrLMtA==
|
||||
|
||||
"@prisma/engines@2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d":
|
||||
version "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d.tgz#db2809a6f7f18584e3ca89b1f5bad884155629ec"
|
||||
integrity sha512-rEWpaG7wZvPuWJC5SwkBB/Iwue//oC5yv58Mse7r+ibtgkA7vGdWc1bFDQ32DT9tDL5WSC6bBwqEASGV/1Gm1Q==
|
||||
"@prisma/engines@2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c":
|
||||
version "2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c.tgz#4ccd255e0823605db3d8387a5195b6fdabe3b0c0"
|
||||
integrity sha512-KmWdogrsfsSLYvfqY3cS3QcDGzaEFklE+T6dNJf+k/KPQum4A29IwDalafMwh5cMN8ivZobUbowNSwWJrMT08Q==
|
||||
|
||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||
version "1.1.2"
|
||||
|
@ -4234,12 +4234,12 @@ pretty-format@^3.8.0:
|
|||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385"
|
||||
integrity sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U=
|
||||
|
||||
prisma@2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.19.0.tgz#2c14f9cbbfb0ab69c8a9473e16736759713d29ad"
|
||||
integrity sha512-iartCNVrtR4XT20ABN3zrSi3R/pCBe75Y0ZH8681QIGm8qjRQzf3DnbscPZgZ9iY4KFuVxL8ZrBQVDmRhpN0EQ==
|
||||
prisma@^2.22.1:
|
||||
version "2.22.1"
|
||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.22.1.tgz#884687a90c7b797b34c6110ea413049078c8da6e"
|
||||
integrity sha512-hwvCM3zyxgSda/+/p+GW7nz93jRebtMU01wAG7YVVnl0OKU+dpw1wPvPFmQRldkZHk8fTCleYmjc24WaSdVPZQ==
|
||||
dependencies:
|
||||
"@prisma/engines" "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d"
|
||||
"@prisma/engines" "2.22.0-21.60cc71d884972ab4e897f0277c4b84383dddaf6c"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
|
|
Loading…
Reference in New Issue