diff --git a/api/resolvers/item.js b/api/resolvers/item.js
index 1f21da31..172650f1 100644
--- a/api/resolvers/item.js
+++ b/api/resolvers/item.js
@@ -146,6 +146,10 @@ function recentClause (type) {
}
}
+const subClause = (sub, num) => {
+ return sub ? ` AND "subName" = $${num} ` : ` AND ("subName" IS NOT NULL OR "subName" = $${num}) `
+}
+
export default {
Query: {
itemRepetition: async (parent, { parentId }, { me, models }) => {
@@ -194,10 +198,6 @@ export default {
const decodedCursor = decodeCursor(cursor)
let items; let user; let pins; let subFull
- const subClause = (num) => {
- return sub ? ` AND "subName" = $${num} ` : ` AND ("subName" IS NULL OR "subName" = $${num}) `
- }
-
const activeOrMine = () => {
return me ? ` AND (status <> 'STOPPED' OR "userId" = ${me.id}) ` : ' AND status <> \'STOPPED\' '
}
@@ -229,7 +229,7 @@ export default {
${SELECT}
FROM "Item"
WHERE "parentId" IS NULL AND created_at <= $1
- ${subClause(3)}
+ ${subClause(sub, 3)}
${activeOrMine()}
${await filterClause(me, models)}
${recentClause(type)}
@@ -264,7 +264,7 @@ export default {
FROM "Item"
WHERE "parentId" IS NULL AND created_at <= $1
AND "pinId" IS NULL
- ${subClause(3)}
+ ${subClause(sub, 3)}
AND status = 'ACTIVE' AND "maxBid" > 0
ORDER BY "maxBid" DESC, created_at ASC)
UNION ALL
@@ -272,7 +272,7 @@ export default {
FROM "Item"
WHERE "parentId" IS NULL AND created_at <= $1
AND "pinId" IS NULL
- ${subClause(3)}
+ ${subClause(sub, 3)}
AND ((status = 'ACTIVE' AND "maxBid" = 0) OR status = 'NOSATS')
ORDER BY created_at DESC)
) a
@@ -292,7 +292,7 @@ export default {
FROM "Item"
WHERE "parentId" IS NULL AND "Item".created_at <= $1 AND "Item".created_at > $3
AND "pinId" IS NULL AND NOT bio AND "deletedAt" IS NULL
- ${subClause(4)}
+ ${subClause(sub, 4)}
${await filterClause(me, models)}
${await newTimedOrderByWeightedSats(me, models, 1)}
OFFSET $2
@@ -305,7 +305,7 @@ export default {
FROM "Item"
WHERE "parentId" IS NULL AND "Item".created_at <= $1
AND "pinId" IS NULL AND NOT bio AND "deletedAt" IS NULL
- ${subClause(3)}
+ ${subClause(sub, 3)}
${await filterClause(me, models)}
${await newTimedOrderByWeightedSats(me, models, 1)}
OFFSET $2
@@ -323,7 +323,8 @@ export default {
)
FROM "Item"
WHERE "pinId" IS NOT NULL
- ) rank_filter WHERE RANK = 1`)
+ ${subClause(sub, 1)}
+ ) rank_filter WHERE RANK = 1`, sub)
}
break
}
@@ -428,7 +429,7 @@ export default {
items
}
},
- moreFlatComments: async (parent, { cursor, name, sort, within }, { me, models }) => {
+ moreFlatComments: async (parent, { sub, cursor, name, sort, within }, { me, models }) => {
const decodedCursor = decodeCursor(cursor)
let comments, user
@@ -437,11 +438,13 @@ export default {
comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
- WHERE "parentId" IS NOT NULL AND created_at <= $1
+ JOIN "Item" root ON "Item"."rootId" = root.id
+ WHERE "Item"."parentId" IS NOT NULL AND "Item".created_at <= $1
+ AND (root."subName" = $3 OR (root."subName" IS NULL AND $3 IS NULL))
${await filterClause(me, models)}
- ORDER BY created_at DESC
+ ORDER BY "Item".created_at DESC
OFFSET $2
- LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub)
break
case 'user':
if (!name) {
@@ -467,13 +470,15 @@ export default {
comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
- WHERE "parentId" IS NOT NULL AND "deletedAt" IS NULL
+ JOIN "Item" root ON "Item"."rootId" = root.id
+ WHERE "Item"."parentId" IS NOT NULL AND"Item"."deletedAt" IS NULL
+ AND (root."subName" = $3 OR (root."subName" IS NULL AND $3 IS NULL))
AND "Item".created_at <= $1
${topClause(within)}
${await filterClause(me, models)}
${await topOrderByWeightedSats(me, models)}
OFFSET $2
- LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub)
break
default:
throw new UserInputError('invalid sort type', { argumentName: 'sort' })
@@ -664,7 +669,7 @@ export default {
}
},
upsertPoll: async (parent, { id, ...data }, { me, models }) => {
- const { forward, boost, title, text, options } = data
+ const { sub, forward, boost, title, text, options } = data
if (!me) {
throw new AuthenticationError('you must be logged in')
}
@@ -693,16 +698,16 @@ export default {
throw new AuthenticationError('item does not belong to you')
}
const [item] = await serialize(models,
- models.$queryRaw(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6) AS "Item"`,
- Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id)))
+ models.$queryRaw(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6, $7) AS "Item"`,
+ sub || 'bitcoin', Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id)))
await createMentions(item, models)
item.comments = []
return item
} else {
const [item] = await serialize(models,
- models.$queryRaw(`${SELECT} FROM create_poll($1, $2, $3, $4, $5, $6, $7, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
- title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id)))
+ models.$queryRaw(`${SELECT} FROM create_poll($1, $2, $3, $4, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
+ sub || 'bitcoin', title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id)))
await createMentions(item, models)
item.comments = []
@@ -1028,7 +1033,7 @@ export const createMentions = async (item, models) => {
}
}
-export const updateItem = async (parent, { id, data: { title, url, text, boost, forward, bounty, parentId } }, { me, models }) => {
+export const updateItem = async (parent, { id, data: { sub, title, url, text, boost, forward, bounty, parentId } }, { me, models }) => {
// update iff this item belongs to me
const old = await models.item.findUnique({ where: { id: Number(id) } })
if (Number(old.userId) !== Number(me?.id)) {
@@ -1059,15 +1064,16 @@ export const updateItem = async (parent, { id, data: { title, url, text, boost,
const [item] = await serialize(models,
models.$queryRaw(
- `${SELECT} FROM update_item($1, $2, $3, $4, $5, $6, $7) AS "Item"`,
- Number(id), title, url, text, Number(boost || 0), bounty ? Number(bounty) : null, Number(fwdUser?.id)))
+ `${SELECT} FROM update_item($1, $2, $3, $4, $5, $6, $7, $8) AS "Item"`,
+ old.parentId ? null : sub || 'bitcoin', Number(id), title, url, text,
+ Number(boost || 0), bounty ? Number(bounty) : null, Number(fwdUser?.id)))
await createMentions(item, models)
return item
}
-const createItem = async (parent, { title, url, text, boost, forward, bounty, parentId }, { me, models }) => {
+const createItem = async (parent, { sub, title, url, text, boost, forward, bounty, parentId }, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
@@ -1091,7 +1097,8 @@ const createItem = async (parent, { title, url, text, boost, forward, bounty, pa
const [item] = await serialize(
models,
models.$queryRaw(
- `${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
+ `${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, $8, $9, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
+ parentId ? null : sub || 'bitcoin',
title,
url,
text,
diff --git a/api/resolvers/search.js b/api/resolvers/search.js
index ec75a84f..fa73c79d 100644
--- a/api/resolvers/search.js
+++ b/api/resolvers/search.js
@@ -79,7 +79,7 @@ export default {
items
}
},
- search: async (parent, { q: query, sub, cursor, sort, what, when }, { me, models, search }) => {
+ search: async (parent, { q: query, cursor, sort, what, when }, { me, models, search }) => {
const decodedCursor = decodeCursor(cursor)
let sitems
@@ -105,7 +105,9 @@ export default {
const queryArr = query.trim().split(/\s+/)
const url = queryArr.find(word => word.startsWith('url:'))
const nym = queryArr.find(word => word.startsWith('nym:'))
- query = queryArr.filter(word => !word.startsWith('url:') && !word.startsWith('nym:')).join(' ')
+ const sub = queryArr.find(word => word.startsWith('~'))
+ const exclude = [url, nym, sub]
+ query = queryArr.filter(word => !exclude.includes(word)).join(' ')
if (url) {
whatArr.push({ wildcard: { url: `*${url.slice(4).toLowerCase()}*` } })
@@ -115,6 +117,10 @@ export default {
whatArr.push({ wildcard: { 'user.name': `*${nym.slice(4).toLowerCase()}*` } })
}
+ if (sub) {
+ whatArr.push({ match: { 'sub.name': sub.slice(1).toLowerCase() } })
+ }
+
const sortArr = []
switch (sort) {
case 'recent':
@@ -204,9 +210,6 @@ export default {
bool: {
must: [
...whatArr,
- sub
- ? { match: { 'sub.name': sub } }
- : { bool: { must_not: { exists: { field: 'sub.name' } } } },
me
? {
bool: {
diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js
index 23ee493a..12fa7141 100644
--- a/api/typeDefs/item.js
+++ b/api/typeDefs/item.js
@@ -3,7 +3,7 @@ import { gql } from 'apollo-server-micro'
export default gql`
extend type Query {
items(sub: String, sort: String, type: String, cursor: String, name: String, within: String): Items
- moreFlatComments(sort: String!, cursor: String, name: String, within: String): Comments
+ moreFlatComments(sub: String, sort: String!, cursor: String, name: String, within: String): Comments
moreBookmarks(cursor: String, name: String!): Items
item(id: ID!): Item
comments(id: ID!, sort: String): [Item!]!
@@ -12,7 +12,7 @@ export default gql`
related(cursor: String, title: String, id: ID, minMatch: String, limit: Int): Items
allItems(cursor: String): Items
getBountiesByUserName(name: String!, cursor: String, , limit: Int): Items
- search(q: String, sub: String, cursor: String, what: String, sort: String, when: String): Items
+ search(q: String, cursor: String, what: String, sort: String, when: String): Items
auctionPosition(sub: String, id: ID, bid: Int!): Int!
itemRepetition(parentId: ID): Int!
outlawedItems(cursor: String): Items
@@ -35,12 +35,12 @@ export default gql`
extend type Mutation {
bookmarkItem(id: ID): Item
deleteItem(id: ID): Item
- upsertLink(id: ID, title: String!, url: String!, boost: Int, forward: String): Item!
- upsertDiscussion(id: ID, title: String!, text: String, boost: Int, forward: String): Item!
- upsertBounty(id: ID, title: String!, text: String, bounty: Int!, boost: Int, forward: String): Item!
- upsertJob(id: ID, sub: ID!, title: String!, company: String!, location: String, remote: Boolean,
+ upsertLink(id: ID, sub: String, title: String!, url: String!, boost: Int, forward: String): Item!
+ upsertDiscussion(id: ID, sub: String, title: String!, text: String, boost: Int, forward: String): Item!
+ upsertBounty(id: ID, sub: String, title: String!, text: String, bounty: Int!, boost: Int, forward: String): Item!
+ upsertJob(id: ID, sub: String!, title: String!, company: String!, location: String, remote: Boolean,
text: String!, url: String!, maxBid: Int!, status: String, logo: Int): Item!
- upsertPoll(id: ID, title: String!, text: String, options: [String!]!, boost: Int, forward: String): Item!
+ upsertPoll(id: ID, sub: String, title: String!, text: String, options: [String!]!, boost: Int, forward: String): Item!
createComment(text: String!, parentId: ID!): Item!
updateComment(id: ID!, text: String!): Item!
dontLikeThis(id: ID!): Boolean!
diff --git a/api/typeDefs/sub.js b/api/typeDefs/sub.js
index 9e1ad04c..4a96855c 100644
--- a/api/typeDefs/sub.js
+++ b/api/typeDefs/sub.js
@@ -2,12 +2,12 @@ import { gql } from 'apollo-server-micro'
export default gql`
extend type Query {
- sub(name: ID!): Sub
- subLatestPost(name: ID!): String
+ sub(name: String!): Sub
+ subLatestPost(name: String!): String
}
type Sub {
- name: ID!
+ name: String!
createdAt: String!
updatedAt: String!
postTypes: [String!]!
diff --git a/components/bounty-form.js b/components/bounty-form.js
index f0a18073..ae3d62c4 100644
--- a/components/bounty-form.js
+++ b/components/bounty-form.js
@@ -10,6 +10,7 @@ import { bountySchema } from '../lib/validate'
export function BountyForm ({
item,
+ sub,
editThreshold,
titleLabel = 'title',
bountyLabel = 'bounty',
@@ -24,6 +25,7 @@ export function BountyForm ({
const [upsertBounty] = useMutation(
gql`
mutation upsertBounty(
+ $sub: String
$id: ID
$title: String!
$bounty: Int!
@@ -32,6 +34,7 @@ export function BountyForm ({
$forward: String
) {
upsertBounty(
+ sub: $sub
id: $id
title: $title
bounty: $bounty
@@ -59,6 +62,7 @@ export function BountyForm ({
(async ({ boost, bounty, ...values }) => {
const { error } = await upsertBounty({
variables: {
+ sub: item?.sub?.name || sub?.name,
id: item?.id,
boost: boost ? Number(boost) : undefined,
bounty: bounty ? Number(bounty) : undefined,
@@ -72,7 +76,8 @@ export function BountyForm ({
if (item) {
await router.push(`/items/${item.id}`)
} else {
- await router.push('/recent')
+ const prefix = sub?.name ? `/~${sub.name}/` : ''
+ await router.push(prefix + '/recent')
}
})
}
diff --git a/components/discussion-form.js b/components/discussion-form.js
index 1738dc96..02e2ebcf 100644
--- a/components/discussion-form.js
+++ b/components/discussion-form.js
@@ -13,7 +13,7 @@ import { Button } from 'react-bootstrap'
import { discussionSchema } from '../lib/validate'
export function DiscussionForm ({
- item, editThreshold, titleLabel = 'title',
+ item, sub, editThreshold, titleLabel = 'title',
textLabel = 'text', buttonText = 'post',
adv, handleSubmit
}) {
@@ -23,8 +23,8 @@ export function DiscussionForm ({
// const me = useMe()
const [upsertDiscussion] = useMutation(
gql`
- mutation upsertDiscussion($id: ID, $title: String!, $text: String, $boost: Int, $forward: String) {
- upsertDiscussion(id: $id, title: $title, text: $text, boost: $boost, forward: $forward) {
+ mutation upsertDiscussion($sub: String, $id: ID, $title: String!, $text: String, $boost: Int, $forward: String) {
+ upsertDiscussion(sub: $sub, id: $id, title: $title, text: $text, boost: $boost, forward: $forward) {
id
}
}`
@@ -56,7 +56,7 @@ export function DiscussionForm ({
schema={schema}
onSubmit={handleSubmit || (async ({ boost, ...values }) => {
const { error } = await upsertDiscussion({
- variables: { id: item?.id, boost: boost ? Number(boost) : undefined, ...values }
+ variables: { sub: item?.sub?.name || sub?.name, id: item?.id, boost: boost ? Number(boost) : undefined, ...values }
})
if (error) {
throw new Error({ message: error.toString() })
@@ -65,7 +65,8 @@ export function DiscussionForm ({
if (item) {
await router.push(`/items/${item.id}`)
} else {
- await router.push('/recent')
+ const prefix = sub?.name ? `/~${sub.name}/` : ''
+ await router.push(prefix + '/recent')
}
})}
storageKeyPrefix={item ? undefined : 'discussion'}
diff --git a/components/error-boundary.js b/components/error-boundary.js
index 83e1b4b7..5ae01f06 100644
--- a/components/error-boundary.js
+++ b/components/error-boundary.js
@@ -1,5 +1,5 @@
import React from 'react'
-import LayoutError from './layout-error'
+import LayoutStatic from './layout-static'
import styles from '../styles/404.module.css'
class ErrorBoundary extends React.Component {
@@ -25,10 +25,10 @@ class ErrorBoundary extends React.Component {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
- something went wrong
-