WIP bios
This commit is contained in:
parent
7e62fad69c
commit
e7787e3e67
|
@ -4,6 +4,6 @@ const prisma = global.prisma || new PrismaClient({
|
|||
log: ['warn', 'error']
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV === 'development') global.prisma = prisma
|
||||
global.prisma = prisma
|
||||
|
||||
export default prisma
|
||||
|
|
|
@ -21,6 +21,18 @@ async function comments (models, id) {
|
|||
return nestComments(flat, id)[0]
|
||||
}
|
||||
|
||||
export async function getItem (parent, { id }, { models }) {
|
||||
console.log(id)
|
||||
const [item] = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE id = $1`, Number(id))
|
||||
if (item) {
|
||||
item.comments = comments(models, id)
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
moreItems: async (parent, { sort, cursor, userId }, { me, models }) => {
|
||||
|
@ -82,16 +94,7 @@ export default {
|
|||
comments
|
||||
}
|
||||
},
|
||||
item: async (parent, { id }, { models }) => {
|
||||
const [item] = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE id = $1`, Number(id))
|
||||
if (item) {
|
||||
item.comments = comments(models, id)
|
||||
}
|
||||
return item
|
||||
},
|
||||
item: getItem,
|
||||
userComments: async (parent, { userId }, { models }) => {
|
||||
return await models.$queryRaw(`
|
||||
${SELECT}
|
||||
|
@ -363,7 +366,7 @@ export default {
|
|||
|
||||
const namePattern = /\B@[\w_]+/gi
|
||||
|
||||
const createMentions = async (item, models) => {
|
||||
export const createMentions = async (item, models) => {
|
||||
// if we miss a mention, in the rare circumstance there's some kind of
|
||||
// failure, it's not a big deal so we don't do it transactionally
|
||||
// ideally, we probably would
|
||||
|
@ -456,7 +459,7 @@ function nestComments (flat, parentId) {
|
|||
}
|
||||
|
||||
// we have to do our own query because ltree is unsupported
|
||||
const SELECT =
|
||||
export const SELECT =
|
||||
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
||||
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path"`
|
||||
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
import { AuthenticationError, UserInputError } from 'apollo-server-errors'
|
||||
import { createMentions, getItem, SELECT } from './item'
|
||||
import serialize from './serial'
|
||||
|
||||
export const createBio = async (parent, { title, text }, { me, models }) => {
|
||||
if (!me) {
|
||||
throw new AuthenticationError('you must be logged in')
|
||||
}
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`,
|
||||
title, text, Number(me.id)))
|
||||
|
||||
await createMentions(item, models)
|
||||
|
||||
item.comments = []
|
||||
return item
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
|
@ -32,7 +49,8 @@ export default {
|
|||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
},
|
||||
createBio: createBio
|
||||
},
|
||||
|
||||
User: {
|
||||
|
@ -54,6 +72,10 @@ export default {
|
|||
sats: async (user, args, { models }) => {
|
||||
return Math.floor(user.msats / 1000)
|
||||
},
|
||||
bio: async (user, args, { models }) => {
|
||||
console.log(user)
|
||||
return getItem(user, { id: user.bioId }, { models })
|
||||
},
|
||||
hasNewNotes: async (user, args, { models }) => {
|
||||
// check if any votes have been cast for them since checkedNotesAt
|
||||
const votes = await models.$queryRaw(`
|
||||
|
|
|
@ -10,6 +10,7 @@ export default gql`
|
|||
|
||||
extend type Mutation {
|
||||
setName(name: String!): Boolean
|
||||
createBio(title: String!, text: String): Item!
|
||||
}
|
||||
|
||||
type User {
|
||||
|
@ -22,6 +23,7 @@ export default gql`
|
|||
freeComments: Int!
|
||||
hasNewNotes: Boolean!
|
||||
tipDefault: Int!
|
||||
bio: Item
|
||||
sats: Int!
|
||||
msats: Int!
|
||||
}
|
||||
|
|
|
@ -39,7 +39,10 @@ function Parent ({ item, rootText }) {
|
|||
)
|
||||
}
|
||||
|
||||
export default function Comment ({ item, children, replyOpen, includeParent, rootText, noComments, noReply }) {
|
||||
export default function Comment ({
|
||||
item, children, replyOpen, includeParent,
|
||||
rootText, noComments, noReply
|
||||
}) {
|
||||
const [reply, setReply] = useState(replyOpen)
|
||||
const [edit, setEdit] = useState()
|
||||
const [collapse, setCollapse] = useState(false)
|
||||
|
|
|
@ -12,7 +12,11 @@ export const DiscussionSchema = Yup.object({
|
|||
...AdvPostSchema
|
||||
})
|
||||
|
||||
export function DiscussionForm ({ item, editThreshold }) {
|
||||
export function DiscussionForm ({
|
||||
item, editThreshold, titleLabel = 'title',
|
||||
textLabel = 'text', buttonText = 'post',
|
||||
adv, handleSubmit
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [createDiscussion] = useMutation(
|
||||
gql`
|
||||
|
@ -53,7 +57,7 @@ export function DiscussionForm ({ item, editThreshold }) {
|
|||
...AdvPostInitial
|
||||
}}
|
||||
schema={DiscussionSchema}
|
||||
onSubmit={async ({ boost, ...values }) => {
|
||||
onSubmit={handleSubmit || (async ({ boost, ...values }) => {
|
||||
let id, error
|
||||
if (item) {
|
||||
({ data: { updateDiscussion: { id } }, error } = await updateDiscussion({ variables: { ...values, id: item.id } }))
|
||||
|
@ -63,17 +67,18 @@ export function DiscussionForm ({ item, editThreshold }) {
|
|||
if (error) {
|
||||
throw new Error({ message: error.toString() })
|
||||
}
|
||||
|
||||
router.push(`/items/${id}`)
|
||||
}}
|
||||
})}
|
||||
>
|
||||
<Input
|
||||
label='title'
|
||||
label={titleLabel}
|
||||
name='title'
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
<MarkdownInput
|
||||
label={<>text <small className='text-muted ml-2'>optional</small></>}
|
||||
label={<>{textLabel} <small className='text-muted ml-2'>optional</small></>}
|
||||
name='text'
|
||||
as={TextareaAutosize}
|
||||
minRows={4}
|
||||
|
@ -81,9 +86,9 @@ export function DiscussionForm ({ item, editThreshold }) {
|
|||
? <Countdown date={editThreshold} />
|
||||
: null}
|
||||
/>
|
||||
{!item && <AdvPostForm />}
|
||||
{!item && adv && <AdvPostForm />}
|
||||
<ActionTooltip>
|
||||
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : buttonText}</SubmitButton>
|
||||
</ActionTooltip>
|
||||
</Form>
|
||||
)
|
||||
|
|
|
@ -84,12 +84,12 @@ export default function Footer ({ noLinks }) {
|
|||
/>
|
||||
</div>}
|
||||
<small>
|
||||
<a className='text-dark d-inline-flex' href='https://github.com/stackernews/stacker.news'>
|
||||
This is free open source software <Github width={20} height={20} className='mx-1' />
|
||||
<a className='text-dark d-inline-block' href='https://github.com/stackernews/stacker.news'>
|
||||
This is free open source software<Github width={20} height={20} className='mx-1' />
|
||||
</a>
|
||||
<span className='d-inline-flex text-muted'>
|
||||
made with sound love in Austin <Texas className='mx-1' width={20} height={20} />
|
||||
by <a href='https://twitter.com/k00bideh' className='text-twitter d-inline-flex'><Twitter width={20} height={20} className='ml-1' />@k00bideh</a>
|
||||
<span className='d-inline-block text-muted'>
|
||||
made with sound love in Austin<Texas className='mx-1' width={20} height={20} />
|
||||
by<a href='https://twitter.com/k00bideh' className='text-twitter d-inline-block'><Twitter width={20} height={20} className='ml-1' />@k00bideh</a>
|
||||
</span>
|
||||
</small>
|
||||
</Container>
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import Item, { ItemSkeleton } from './item'
|
||||
import Reply, { ReplySkeleton } from './reply'
|
||||
import Comment from './comment'
|
||||
import Text from './text'
|
||||
import Comments, { CommentsSkeleton } from './comments'
|
||||
import { COMMENTS } from '../fragments/comments'
|
||||
import { ITEM_FIELDS } from '../fragments/items'
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
import styles from '../styles/item.module.css'
|
||||
import { NOFOLLOW_LIMIT } from '../lib/constants'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export default function ItemFull ({ item: qItem, minimal }) {
|
||||
const query = gql`
|
||||
${ITEM_FIELDS}
|
||||
${COMMENTS}
|
||||
{
|
||||
item(id: ${qItem.id}) {
|
||||
...ItemFields
|
||||
text
|
||||
comments {
|
||||
...CommentsRecursive
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const router = useRouter()
|
||||
const { error, data } = useQuery(query, {
|
||||
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||
})
|
||||
if (error) return <div>Failed to load!</div>
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div>
|
||||
<ItemSkeleton>
|
||||
<ReplySkeleton />
|
||||
</ItemSkeleton>
|
||||
<div className={styles.comments}>
|
||||
<CommentsSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const { item } = data
|
||||
|
||||
return (
|
||||
<>
|
||||
{item.parentId
|
||||
? <Comment item={item} replyOpen includeParent noComments />
|
||||
: (minimal
|
||||
? (
|
||||
<>
|
||||
{item.text &&
|
||||
<div className='mb-3'>
|
||||
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
|
||||
</div>}
|
||||
</>)
|
||||
: (
|
||||
<>
|
||||
<Item item={item}>
|
||||
{item.text &&
|
||||
<div className='mb-3'>
|
||||
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
|
||||
</div>}
|
||||
<Reply parentId={item.id} />
|
||||
</Item>
|
||||
</>)
|
||||
)}
|
||||
<div className={styles.comments}>
|
||||
<Comments comments={item.comments} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -5,7 +5,7 @@ import { LightningProvider } from './lightning'
|
|||
import Footer from './footer'
|
||||
import Seo from './seo'
|
||||
|
||||
export default function Layout ({ noContain, noFooter, noFooterLinks, noSeo, children }) {
|
||||
export default function Layout ({ noContain, noFooter, noFooterLinks, containClassName, noSeo, children }) {
|
||||
return (
|
||||
<>
|
||||
{!noSeo && <Seo />}
|
||||
|
@ -17,7 +17,7 @@ export default function Layout ({ noContain, noFooter, noFooterLinks, noSeo, chi
|
|||
{noContain
|
||||
? children
|
||||
: (
|
||||
<Container className='px-sm-0'>
|
||||
<Container className={`px-sm-0 ${containClassName || ''}`}>
|
||||
{children}
|
||||
</Container>
|
||||
)}
|
||||
|
|
|
@ -31,7 +31,7 @@ export default function UserHeader ({ user }) {
|
|||
const client = useApolloClient()
|
||||
const [setName] = useMutation(NAME_MUTATION)
|
||||
|
||||
const Satistics = () => <h1 className='ml-2'><small className='text-success'>{user.sats} sats \ {user.stacked} stacked</small></h1>
|
||||
const Satistics = () => <h1 className='mb-0'><small className='text-success'>{user.sats} sats \ {user.stacked} stacked</small></h1>
|
||||
|
||||
const UserSchema = Yup.object({
|
||||
name: Yup.string()
|
||||
|
@ -51,14 +51,15 @@ export default function UserHeader ({ user }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{editting
|
||||
? (
|
||||
<Form
|
||||
className='d-flex align-items-center flex-wrap'
|
||||
schema={UserSchema}
|
||||
initial={{
|
||||
name: user.name
|
||||
}}
|
||||
className='d-flex align-items-center'
|
||||
validateImmediately
|
||||
onSubmit={async ({ name }) => {
|
||||
if (name === user.name) {
|
||||
|
@ -94,23 +95,29 @@ export default function UserHeader ({ user }) {
|
|||
groupClassName={`mb-0 ${styles.username}`}
|
||||
showValid
|
||||
/>
|
||||
<Satistics user={user} />
|
||||
<SubmitButton className='ml-2' variant='info' size='sm' onClick={() => setEditting(true)}>save</SubmitButton>
|
||||
<SubmitButton variant='link' onClick={() => setEditting(true)}>save</SubmitButton>
|
||||
</Form>
|
||||
)
|
||||
: (
|
||||
<div className='d-flex align-items-center flex-wrap'>
|
||||
<h1>@{user.name}</h1>
|
||||
<Satistics user={user} />
|
||||
{session && session.user && session.user.name === user.name &&
|
||||
<Button className='ml-2' variant='boost' size='sm' onClick={() => setEditting(true)}>edit</Button>}
|
||||
<div className='d-flex align-items-center'>
|
||||
<h2 className='mb-0'>@{user.name}</h2>
|
||||
{session?.user?.name === user.name &&
|
||||
<Button variant='link' onClick={() => setEditting(true)}>edit nym</Button>}
|
||||
</div>
|
||||
)}
|
||||
<Satistics user={user} />
|
||||
</div>
|
||||
<Nav
|
||||
className={styles.nav}
|
||||
activeKey={router.asPath}
|
||||
>
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name} passHref>
|
||||
<Nav.Link>bio</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name + '/posts'} passHref>
|
||||
<Nav.Link>{user.nitems} posts</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
|
@ -121,7 +128,7 @@ export default function UserHeader ({ user }) {
|
|||
</Nav.Item>
|
||||
{/* <Nav.Item>
|
||||
<Link href={'/' + user.name + '/sativity'} passHref>
|
||||
<Nav.Link>sativity</Nav.Link>
|
||||
<Nav.Link>satistics</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item> */}
|
||||
</Nav>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
.username {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
margin-top: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav div:first-child a {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.nav div:last-child a {
|
||||
padding-right: 0;
|
||||
}
|
|
@ -1,14 +1,22 @@
|
|||
import Layout from '../components/layout'
|
||||
import Items from '../components/items'
|
||||
import { gql } from '@apollo/client'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import ApolloClient from '../api/client'
|
||||
import UserHeader from '../components/user-header'
|
||||
import Seo from '../components/seo'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import styles from '../styles/user.module.css'
|
||||
import { useState } from 'react'
|
||||
import { DiscussionForm } from '../components/discussion-form'
|
||||
import { useSession } from 'next-auth/client'
|
||||
import { ITEM_FIELDS } from '../fragments/items'
|
||||
import ItemFull from '../components/item-full'
|
||||
|
||||
export async function getServerSideProps ({ req, params }) {
|
||||
const { error, data: { user } } = await (await ApolloClient(req)).query({
|
||||
query:
|
||||
gql`{
|
||||
gql`
|
||||
${ITEM_FIELDS}
|
||||
{
|
||||
user(name: "${params.username}") {
|
||||
id
|
||||
createdAt
|
||||
|
@ -17,6 +25,9 @@ export async function getServerSideProps ({ req, params }) {
|
|||
ncomments
|
||||
stacked
|
||||
sats
|
||||
bio {
|
||||
...ItemFields
|
||||
}
|
||||
}
|
||||
}`
|
||||
})
|
||||
|
@ -34,12 +45,61 @@ export async function getServerSideProps ({ req, params }) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function User ({ user }) {
|
||||
function BioForm () {
|
||||
const [createBio] = useMutation(
|
||||
gql`
|
||||
mutation createBio($title: String!, $text: String) {
|
||||
createBio(title: $title, text: $text) {
|
||||
id
|
||||
}
|
||||
}`, {
|
||||
update (cache, { data: { createBio } }) {
|
||||
cache.modify({
|
||||
id: `User:${createBio.userId}`,
|
||||
fields: {
|
||||
bio () {
|
||||
return createBio
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<Layout noSeo>
|
||||
<div className={styles.createFormContainer}>
|
||||
<DiscussionForm
|
||||
titleLabel='one line bio' textLabel='full bio' buttonText='create'
|
||||
handleSubmit={async values => {
|
||||
const { error } = await createBio({ variables: values })
|
||||
if (error) {
|
||||
throw new Error({ message: error.toString() })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function User ({ user }) {
|
||||
const [create, setCreate] = useState(false)
|
||||
const [session] = useSession()
|
||||
|
||||
return (
|
||||
<Layout noSeo containClassName={styles.contain}>
|
||||
<Seo user={user} />
|
||||
<UserHeader user={user} />
|
||||
<Items variables={{ sort: 'user', userId: user.id }} />
|
||||
{user.bio
|
||||
? <ItemFull item={user.bio} minimal />
|
||||
: (
|
||||
<div className={styles.create}>
|
||||
{create
|
||||
? <BioForm />
|
||||
: (
|
||||
session?.user?.name === user.name &&
|
||||
<Button onClick={setCreate} size='md' variant='secondary'>create bio</Button>
|
||||
)}
|
||||
</div>)}
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import Layout from '../../components/layout'
|
||||
import { gql } from '@apollo/client'
|
||||
import ApolloClient from '../../api/client'
|
||||
import UserHeader from '../../components/user-header'
|
||||
import Seo from '../../components/seo'
|
||||
import Items from '../../components/items'
|
||||
|
||||
export async function getServerSideProps ({ req, params }) {
|
||||
const { error, data: { user } } = await (await ApolloClient(req)).query({
|
||||
query:
|
||||
gql`{
|
||||
user(name: "${params.username}") {
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
nitems
|
||||
ncomments
|
||||
stacked
|
||||
sats
|
||||
}
|
||||
}`
|
||||
})
|
||||
|
||||
if (!user || error) {
|
||||
return {
|
||||
notFound: true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function UserPosts ({ user }) {
|
||||
return (
|
||||
<Layout noSeo>
|
||||
<Seo user={user} />
|
||||
<UserHeader user={user} />
|
||||
<Items variables={{ sort: 'user', userId: user.id }} />
|
||||
</Layout>
|
||||
)
|
||||
}
|
|
@ -1,17 +1,9 @@
|
|||
import Item, { ItemSkeleton } from '../../components/item'
|
||||
import Layout from '../../components/layout'
|
||||
import Reply, { ReplySkeleton } from '../../components/reply'
|
||||
import Comment from '../../components/comment'
|
||||
import Text from '../../components/text'
|
||||
import Comments, { CommentsSkeleton } from '../../components/comments'
|
||||
import { COMMENTS } from '../../fragments/comments'
|
||||
import { ITEM_FIELDS } from '../../fragments/items'
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
import styles from '../../styles/item.module.css'
|
||||
import { gql } from '@apollo/client'
|
||||
import Seo from '../../components/seo'
|
||||
import ApolloClient from '../../api/client'
|
||||
import { NOFOLLOW_LIMIT } from '../../lib/constants'
|
||||
import { useRouter } from 'next/router'
|
||||
import ItemFull from '../../components/item-full'
|
||||
|
||||
// ssr the item without comments so that we can populate metatags
|
||||
export async function getServerSideProps ({ req, params: { id } }) {
|
||||
|
@ -46,65 +38,11 @@ export async function getServerSideProps ({ req, params: { id } }) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function FullItem ({ item }) {
|
||||
const query = gql`
|
||||
${ITEM_FIELDS}
|
||||
${COMMENTS}
|
||||
{
|
||||
item(id: ${item.id}) {
|
||||
...ItemFields
|
||||
text
|
||||
comments {
|
||||
...CommentsRecursive
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
export default function AnItem ({ item }) {
|
||||
return (
|
||||
<Layout noSeo>
|
||||
<Seo item={item} />
|
||||
<LoadItem query={query} />
|
||||
<ItemFull item={item} />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
function LoadItem ({ query }) {
|
||||
const router = useRouter()
|
||||
const { error, data } = useQuery(query, {
|
||||
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||
})
|
||||
if (error) return <div>Failed to load!</div>
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div>
|
||||
<ItemSkeleton>
|
||||
<ReplySkeleton />
|
||||
</ItemSkeleton>
|
||||
<div className={styles.comments}>
|
||||
<CommentsSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const { item } = data
|
||||
|
||||
return (
|
||||
<>
|
||||
{item.parentId
|
||||
? <Comment item={item} replyOpen includeParent noComments />
|
||||
: (
|
||||
<>
|
||||
<Item item={item}>
|
||||
{item.text && <div className='mb-3'><Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text></div>}
|
||||
<Reply parentId={item.id} />
|
||||
</Item>
|
||||
</>
|
||||
)}
|
||||
<div className={styles.comments}>
|
||||
<Comments comments={item.comments} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "bioId" INTEGER;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "users" ADD FOREIGN KEY ("bioId") REFERENCES "Item"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
@ -0,0 +1,16 @@
|
|||
CREATE OR REPLACE FUNCTION create_bio(title TEXT, text TEXT, user_id INTEGER)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
item "Item";
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
SELECT * INTO item FROM create_item(title, NULL, text, 0, NULL, user_id);
|
||||
|
||||
UPDATE users SET "bioId" = item.id WHERE id = user_id;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
|
@ -24,6 +24,8 @@ model User {
|
|||
actions ItemAct[]
|
||||
invoices Invoice[]
|
||||
withdrawls Withdrawl[]
|
||||
bio Item? @relation(name: "Item", fields: [bioId], references: [id])
|
||||
bioId Int?
|
||||
msats Int @default(0)
|
||||
freeComments Int @default(5)
|
||||
freePosts Int @default(2)
|
||||
|
@ -65,6 +67,7 @@ model Item {
|
|||
mentions Mention[]
|
||||
path Unsupported("LTREE")?
|
||||
|
||||
User User[] @relation("Item")
|
||||
@@index([userId])
|
||||
@@index([parentId])
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
.contain {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.create {
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.createFormContainer {
|
||||
width: 100%;
|
||||
max-width: 740px;
|
||||
}
|
Loading…
Reference in New Issue