From afed19430cfc907940da9ad2d095d5f50276d026 Mon Sep 17 00:00:00 2001 From: keyan Date: Thu, 27 Jan 2022 13:18:48 -0600 Subject: [PATCH] working search --- api/resolvers/item.js | 16 ++++++------ api/typeDefs/item.js | 3 ++- components/comment.js | 21 ++++++++++++++++ components/comments-flat.js | 25 +++---------------- components/item-full.js | 4 +-- components/item.js | 10 +++++--- components/items.js | 5 +++- components/items.module.css | 2 +- components/search-items.js | 50 +++++++++++++++++++++++++++++++++++++ components/search.js | 35 ++++++++++++++++++++------ fragments/items.js | 13 ++++++++++ lib/apollo.js | 13 ++++++++++ pages/search.js | 18 +++++++++++++ worker/search.js | 1 + 14 files changed, 170 insertions(+), 46 deletions(-) create mode 100644 components/search-items.js create mode 100644 pages/search.js diff --git a/api/resolvers/item.js b/api/resolvers/item.js index 5d788735..56c50b15 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -275,7 +275,7 @@ export default { comments: async (parent, { id, sort }, { models }) => { return comments(models, id, sort) }, - search: async (parent, { query, cursor }, { models, search }) => { + search: async (parent, { q: query, cursor }, { models, search }) => { const decodedCursor = decodeCursor(cursor) const sitems = await search.search({ @@ -503,7 +503,7 @@ export default { const [{ count }] = await models.$queryRaw` SELECT count(*) FROM "Item" - WHERE path <@ text2ltree(${item.path}) AND id != ${item.id}` + WHERE path <@ text2ltree(${item.path}) AND id != ${Number(item.id)}` return count || 0 }, sats: async (item, args, { models }) => { @@ -512,9 +512,9 @@ export default { sats: true }, where: { - itemId: item.id, + itemId: Number(item.id), userId: { - not: item.userId + not: Number(item.userId) }, act: { not: 'BOOST' @@ -530,9 +530,9 @@ export default { sats: true }, where: { - itemId: item.id, + itemId: Number(item.id), userId: { - not: item.userId + not: Number(item.userId) }, act: 'VOTE' } @@ -546,7 +546,7 @@ export default { sats: true }, where: { - itemId: item.id, + itemId: Number(item.id), act: 'BOOST' } }) @@ -561,7 +561,7 @@ export default { sats: true }, where: { - itemId: item.id, + itemId: Number(item.id), userId: me.id, OR: [ { diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index 945fe50c..166f696a 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -9,7 +9,7 @@ export default gql` pageTitle(url: String!): String dupes(url: String!): [Item!] allItems(cursor: String): Items - search(query: String, cursor: String): Items + search(q: String, cursor: String): Items } type ItemActResult { @@ -49,6 +49,7 @@ export default gql` parent: Item root: Item user: User! + userId: Int! depth: Int! mine: Boolean! boost: Int! diff --git a/components/comment.js b/components/comment.js index 17af2616..210af276 100644 --- a/components/comment.js +++ b/components/comment.js @@ -12,6 +12,7 @@ import { useRouter } from 'next/router' import CommentEdit from './comment-edit' import Countdown from './countdown' import { NOFOLLOW_LIMIT } from '../lib/constants' +import { ignoreClick } from '../lib/clicks' function Parent ({ item, rootText }) { const ParentFrag = () => ( @@ -43,6 +44,26 @@ const truncateString = (string = '', maxLength = 140) => ? `${string.substring(0, maxLength)} […]` : string +export function CommentFlat ({ item, ...props }) { + const router = useRouter() + return ( +
{ + if (ignoreClick(e)) { + return + } + router.push({ + pathname: '/items/[id]', + query: { id: item.root.id, commentId: item.id } + }, `/items/${item.root.id}`) + }} + > + +
+ ) +} + export default function Comment ({ item, children, replyOpen, includeParent, rootText, noComments, noReply, truncate diff --git a/components/comments-flat.js b/components/comments-flat.js index d797f948..f26f70ab 100644 --- a/components/comments-flat.js +++ b/components/comments-flat.js @@ -1,12 +1,9 @@ import { useQuery } from '@apollo/client' import { MORE_FLAT_COMMENTS } from '../fragments/comments' -import Comment, { CommentSkeleton } from './comment' -import { useRouter } from 'next/router' +import { CommentFlat, CommentSkeleton } from './comment' import MoreFooter from './more-footer' -import { ignoreClick } from '../lib/clicks' export default function CommentsFlat ({ variables, comments, cursor, ...props }) { - const router = useRouter() const { data, fetchMore } = useQuery(MORE_FLAT_COMMENTS, { variables }) @@ -21,23 +18,9 @@ export default function CommentsFlat ({ variables, comments, cursor, ...props }) return ( <> - {comments.map(item => ( -
{ - if (ignoreClick(e)) { - return - } - router.push({ - pathname: '/items/[id]', - query: { id: item.root.id, commentId: item.id } - }, `/items/${item.root.id}`) - }} - > - -
- ))} + {comments.map(item => + + )} ) diff --git a/components/item-full.js b/components/item-full.js index 2282603a..519158a9 100644 --- a/components/item-full.js +++ b/components/item-full.js @@ -42,12 +42,12 @@ function ItemEmbed ({ item }) { return null } -function TopLevelItem ({ item }) { +function TopLevelItem ({ item, noReply }) { return ( {item.text && } {item.url && } - + {!noReply && } ) } diff --git a/components/item.js b/components/item.js index 1af04e32..9de712be 100644 --- a/components/item.js +++ b/components/item.js @@ -109,10 +109,12 @@ export default function Item ({ item, rank, children }) { export function ItemSkeleton ({ rank, children }) { return ( <> - {rank && -
- {rank} -
} + {rank + ? ( +
+ {rank} +
) + :
}
diff --git a/components/items.js b/components/items.js index 9c8f9c92..1cfdda6b 100644 --- a/components/items.js +++ b/components/items.js @@ -4,6 +4,7 @@ import styles from './items.module.css' import { MORE_ITEMS } from '../fragments/items' import MoreFooter from './more-footer' import React from 'react' +import Comment from './comment' export default function Items ({ variables, rank, items, pins, cursor }) { const { data, fetchMore } = useQuery(MORE_ITEMS, { variables }) @@ -24,7 +25,9 @@ export default function Items ({ variables, rank, items, pins, cursor }) { {items.map((item, i) => ( {pinMap && pinMap[i + 1] && } - + {item.parentId + ? <>
+ : } ))}
diff --git a/components/items.module.css b/components/items.module.css index 51216627..bf34a2e9 100644 --- a/components/items.module.css +++ b/components/items.module.css @@ -1,4 +1,4 @@ .grid { display: grid; - grid-template-columns: auto 1fr; + grid-template-columns: auto minmax(0, 1fr); } \ No newline at end of file diff --git a/components/search-items.js b/components/search-items.js new file mode 100644 index 00000000..0ff804c3 --- /dev/null +++ b/components/search-items.js @@ -0,0 +1,50 @@ +import { useQuery } from '@apollo/client' +import { ItemSkeleton } from './item' +import styles from './items.module.css' +import { ITEM_SEARCH } from '../fragments/items' +import MoreFooter from './more-footer' +import React from 'react' +import Comment from './comment' +import ItemFull from './item-full' + +export default function SearchItems ({ variables, items, pins, cursor }) { + const { data, fetchMore } = useQuery(ITEM_SEARCH, { variables }) + + if (!data && !items) { + return + } + + if (data) { + ({ search: { items, cursor } } = data) + } + + return ( + <> +
+ {items.map((item, i) => ( + + {item.parentId + ? <>
+ : <>
} + + ))} +
+ } + /> + + ) +} + +function ItemsSkeleton () { + const items = new Array(21).fill(null) + + return ( +
+ {items.map((_, i) => ( + + ))} +
+ ) +} diff --git a/components/search.js b/components/search.js index b5545f8e..a0c27d2a 100644 --- a/components/search.js +++ b/components/search.js @@ -2,24 +2,42 @@ import { Button, Container } from 'react-bootstrap' import styles from './search.module.css' import SearchIcon from '../svgs/search-fill.svg' import CloseIcon from '../svgs/close-line.svg' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { Form, Input, SubmitButton } from './form' +import { useRouter } from 'next/router' export default function Search () { - const [searching, setSearching] = useState() - const [q, setQ] = useState() + const router = useRouter() + const [searching, setSearching] = useState(router.query.q) + const [q, setQ] = useState(router.query.q) + const [atBottom, setAtBottom] = useState() + + useEffect(() => { + setAtBottom((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) + window.onscroll = function (ev) { + if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) { + setAtBottom(true) + } else { + setAtBottom(false) + } + } + }, []) + + const showSearch = atBottom || searching || router.query.q return ( <> -
+
- {searching + {showSearch ? (
{ + router.push(`/search?q=${q}`) + }} > { + setSearching(true) setQ(e.target.value?.trim()) }} /> - {q + {q || atBottom || router.query.q ? ( diff --git a/fragments/items.js b/fragments/items.js index 943ecb16..4a8b552d 100644 --- a/fragments/items.js +++ b/fragments/items.js @@ -89,3 +89,16 @@ export const ITEM_WITH_COMMENTS = gql` ...CommentsRecursive } }` + +export const ITEM_SEARCH = gql` + ${ITEM_FIELDS} + query Search($q: String!, $cursor: String) { + search(q: $q, cursor: $cursor) { + cursor + items { + ...ItemFields + text + } + } + } +` diff --git a/lib/apollo.js b/lib/apollo.js index 52b321a9..c661baf0 100644 --- a/lib/apollo.js +++ b/lib/apollo.js @@ -52,6 +52,19 @@ export default function getApolloClient () { } } }, + search: { + keyArgs: ['q'], + merge (existing, incoming) { + if (isFirstPage(incoming.cursor, existing?.items)) { + return incoming + } + + return { + cursor: incoming.cursor, + items: [...(existing?.items || []), ...incoming.items] + } + } + }, moreFlatComments: { keyArgs: ['name', 'sort', 'within'], merge (existing, incoming) { diff --git a/pages/search.js b/pages/search.js new file mode 100644 index 00000000..7e40022c --- /dev/null +++ b/pages/search.js @@ -0,0 +1,18 @@ +import Layout from '../components/layout' +import { getGetServerSideProps } from '../api/ssrApollo' +import { ITEM_SEARCH } from '../fragments/items' +import SearchItems from '../components/search-items' +import { useRouter } from 'next/router' + +export const getServerSideProps = getGetServerSideProps(ITEM_SEARCH) + +export default function Index ({ data: { search: { items, cursor } } }) { + const router = useRouter() + return ( + + + + ) +} diff --git a/worker/search.js b/worker/search.js index 3a1d0447..8d94ece1 100644 --- a/worker/search.js +++ b/worker/search.js @@ -12,6 +12,7 @@ const ITEM_SEARCH_FIELDS = gql` title text url + userId user { name }