1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

make the search work

This commit is contained in:
Matthias Kretschmann 2020-06-30 15:24:30 +02:00
parent 829ec502a2
commit 4a35fd73d2
Signed by: m
GPG Key ID: 606EEEF3C479A91F
10 changed files with 183 additions and 157 deletions

View File

@ -65,6 +65,11 @@ module.exports = ({ config }) => {
config.resolve.extensions.push('.ts', '.tsx') config.resolve.extensions.push('.ts', '.tsx')
// 'fs' fix for squid.js
config.node = {
fs: 'empty'
}
// Handle SVGs // Handle SVGs
// Don't use Storybook's default SVG Configuration // Don't use Storybook's default SVG Configuration
config.module.rules = config.module.rules.map((rule) => { config.module.rules = config.module.rules.map((rule) => {

53
package-lock.json generated
View File

@ -14100,6 +14100,18 @@
"prepend-http": "^2.0.0", "prepend-http": "^2.0.0",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"sort-keys": "^2.0.0" "sort-keys": "^2.0.0"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
} }
}, },
"p-cancelable": { "p-cancelable": {
@ -23978,6 +23990,18 @@
"prepend-http": "^2.0.0", "prepend-http": "^2.0.0",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"sort-keys": "^2.0.0" "sort-keys": "^2.0.0"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
} }
}, },
"p-cancelable": { "p-cancelable": {
@ -36070,13 +36094,20 @@
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
}, },
"query-string": { "query-string": {
"version": "5.1.1", "version": "6.13.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
"requires": { "requires": {
"decode-uri-component": "^0.2.0", "decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0", "split-on-first": "^1.0.0",
"strict-uri-encode": "^1.0.0" "strict-uri-encode": "^2.0.0"
},
"dependencies": {
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
}
} }
}, },
"querystring": { "querystring": {
@ -44017,6 +44048,18 @@
"timed-out": "^4.0.1", "timed-out": "^4.0.1",
"url-set-query": "^1.0.0", "url-set-query": "^1.0.0",
"xhr": "^2.0.4" "xhr": "^2.0.4"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
} }
}, },
"xhr-request-promise": { "xhr-request-promise": {

View File

@ -49,6 +49,7 @@
"intersection-observer": "^0.11.0", "intersection-observer": "^0.11.0",
"is-url-superb": "^4.0.0", "is-url-superb": "^4.0.0",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"query-string": "^6.13.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-data-table-component": "^6.9.3", "react-data-table-component": "^6.9.3",
"react-datepicker": "^3.0.0", "react-datepicker": "^3.0.0",

View File

@ -2,7 +2,15 @@
margin-bottom: var(--spacer); margin-bottom: var(--spacer);
} }
.inputGroup .input { .inputGroup > div {
margin: 0;
}
.inputGroup label {
display: none;
}
.inputGroup input {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
@ -12,44 +20,23 @@
border-top-right-radius: 0; border-top-right-radius: 0;
margin-top: -1px; margin-top: -1px;
width: 100%; width: 100%;
border-width: 1px;
text-transform: uppercase;
color: var(--brand-grey-lighter);
background: var(--brand-gradient);
box-shadow: none;
} }
.inputGroup button:hover, .inputGroup button:hover,
.inputGroup button:focus, .inputGroup button:focus,
.inputGroup .input:focus + button:hover, .inputGroup input:focus + button:hover,
.inputGroup .input:focus + button:focus { .inputGroup input:focus + button:focus {
background: var(--brand-gradient); background: var(--brand-gradient);
transform: none; transform: none;
box-shadow: none; box-shadow: none;
} }
.inputGroup .input:focus + button {
color: var(--brand-white);
}
/* .inputGroup button:hover,
.inputGroup button:focus,
.inputGroup .input:focus + button {
color: var(--brand-white);
background-color: var(--brand-pink);
}
.inputGroup .input:focus + button {
border-color: var(--brand-pink);
background-color: var(--brand-pink);
} */
@media screen and (min-width: 30rem) { @media screen and (min-width: 30rem) {
.inputGroup { .inputGroup {
display: flex; display: flex;
} }
.inputGroup .input { .inputGroup input {
border-bottom-left-radius: var(--border-radius); border-bottom-left-radius: var(--border-radius);
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
@ -64,14 +51,3 @@
width: auto; width: auto;
} }
} }
.input {
composes: input from './Form/FieldTemplate.module.css';
}
.large {
composes: large from './Form/FieldTemplate.module.css';
}
.filters {
}

View File

@ -3,6 +3,7 @@ import { useNavigate } from '@reach/router'
import styles from './SearchBar.module.css' import styles from './SearchBar.module.css'
import Loader from '../atoms/Loader' import Loader from '../atoms/Loader'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import Input from '../atoms/Input'
export default function SearchBar({ export default function SearchBar({
placeholder, placeholder,
@ -35,12 +36,12 @@ export default function SearchBar({
return ( return (
<form className={styles.form}> <form className={styles.form}>
<div className={styles.inputGroup}> <div className={styles.inputGroup}>
<input <Input
type="search" type="search"
className={large ? `${styles.input} ${styles.large}` : styles.input} name="search"
placeholder={placeholder || 'What are you looking for?'} placeholder={placeholder || 'What are you looking for?'}
value={value} value={value}
onChange={(e) => handleChange(e)} onChange={handleChange}
required required
/> />
<Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}> <Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}>

View File

@ -1,7 +1,3 @@
.input {
composes: input from './Form/FieldTemplate.module.css';
}
.layout { .layout {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -3,6 +3,7 @@ import SearchFilterSection from '../atoms/SearchFilterSection'
import usePriceQueryParams from '../../hooks/usePriceQueryParams' import usePriceQueryParams from '../../hooks/usePriceQueryParams'
import styles from './SearchPriceFilter.module.css' import styles from './SearchPriceFilter.module.css'
import Input from '../atoms/Input'
export declare type PriceInputProps = { export declare type PriceInputProps = {
label: string label: string
@ -18,18 +19,14 @@ export const PriceInput = ({
text text
}: PriceInputProps) => { }: PriceInputProps) => {
return ( return (
<label htmlFor={label} className={styles.label}> <Input
<input
id={label}
name={label} name={label}
label={text}
type="number" type="number"
min="0" min="0"
value={value} value={value}
onChange={onChange} onChange={onChange}
className={styles.input}
/> />
<span>{text}</span>
</label>
) )
} }

View File

@ -1,26 +1,96 @@
import React from 'react' import React, { ReactElement, useState, useEffect } from 'react'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius' import {
import Layout from '../../components/Layout' QueryResult,
import PageHeader from '../molecules/PageHeader' SearchQuery
} from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import SearchBar from '../molecules/SearchBar' import SearchBar from '../molecules/SearchBar'
import AssetList from '../organisms/AssetList' import AssetList from '../organisms/AssetList'
import { SearchPriceFilter } from '../molecules/SearchPriceFilter' import { SearchPriceFilter } from '../molecules/SearchPriceFilter'
import styles from './Search.module.css' import styles from './Search.module.css'
import { priceQueryParamToWei } from '../../utils'
import { Aquarius, Logger } from '@oceanprotocol/squid'
import { config } from '../../config/ocean'
import { useLocation } from '@reach/router'
import queryString from 'query-string'
export declare type SearchPageProps = { export declare type SearchPageProps = {
text: string | string[] text: string | string[]
tag: string | string[] tag: string | string[]
queryResult: QueryResult queryResult: QueryResult
} }
const SearchPage = ({ text, tag, queryResult }: SearchPageProps) => { export function getSearchQuery(
page?: string | string[],
offset?: string | string[],
text?: string | string[],
tag?: string | string[],
priceQuery?: [string | undefined, string | undefined]
): SearchQuery {
return {
page: Number(page) || 1,
offset: Number(offset) || 20,
query: {
text,
tags: tag ? [tag] : undefined,
price: priceQuery
},
sort: {
created: -1
}
// Something in squid-js is weird when using 'categories: [type]'
// which is the only way the query actually returns desired results.
// But it doesn't follow 'SearchQuery' interface so we have to assign
// it here.
} as SearchQuery
}
export async function getResults(params: any): Promise<QueryResult> {
const { text, tag, page, offset, minPrice, maxPrice } = params
const minPriceParsed = priceQueryParamToWei(
minPrice as string,
'Error parsing context.query.minPrice'
)
const maxPriceParsed = priceQueryParamToWei(
maxPrice as string,
'Error parsing context.query.maxPrice'
)
const priceQuery =
minPriceParsed || maxPriceParsed
? // sometimes TS gets a bit silly
([minPriceParsed, maxPriceParsed] as [
string | undefined,
string | undefined
])
: undefined
const aquarius = new Aquarius(config.aquariusUri as string, Logger)
const queryResult = await aquarius.queryMetadata(
getSearchQuery(page, offset, text, tag, priceQuery)
)
return queryResult
}
export default function SearchPage(): ReactElement {
const location = useLocation()
const parsed = queryString.parse(location.search)
const { text, tag } = parsed
const [queryResult, setQueryResult] = useState<QueryResult>()
useEffect(() => {
async function initSearch() {
const results = await getResults(parsed)
setQueryResult(results)
}
initSearch()
}, [])
return ( return (
<Layout noPageHeader>
<section className={styles.grid}> <section className={styles.grid}>
<div className={styles.search}> <div className={styles.search}>
<PageHeader title={`Search for ${text || tag}`} />
{text && <SearchBar initialValue={text as string} />} {text && <SearchBar initialValue={text as string} />}
</div> </div>
@ -29,15 +99,12 @@ const SearchPage = ({ text, tag, queryResult }: SearchPageProps) => {
</aside> </aside>
<div className={styles.results}> <div className={styles.results}>
{queryResult.results.length > 0 ? ( {queryResult && queryResult.results.length > 0 ? (
<AssetList queryResult={queryResult} /> <AssetList queryResult={queryResult} />
) : ( ) : (
<div className={styles.empty}>No results found.</div> <div className={styles.empty}>No results found.</div>
)} )}
</div> </div>
</section> </section>
</Layout>
) )
} }
export default SearchPage

View File

@ -1,14 +1,15 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useLocation } from '@reach/router' import { useLocation } from '@reach/router'
import queryString from 'query-string'
export default function usePriceQueryParams() { export default function usePriceQueryParams() {
const location = useLocation() const location = useLocation()
const [min, setMin] = useState( const [min, setMin] = useState(
(JSON.parse(location.search).minPrice as string) || '0' (queryString.parse(location.search).minPrice as string) || '0'
) )
const [max, setMax] = useState( const [max, setMax] = useState(
(JSON.parse(location.search).maxPrice as string) || '0' (queryString.parse(location.search).maxPrice as string) || '0'
) )
useEffect(() => { useEffect(() => {

View File

@ -1,77 +1,16 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { Aquarius, Logger } from '@oceanprotocol/squid' import PageSearch from '../components/templates/Search'
import { SearchQuery } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import PageSearch, { SearchPageProps } from '../components/templates/Search'
import { config } from '../config/ocean'
import { JSONparse, priceQueryParamToWei } from '../utils'
import { PageProps } from 'gatsby' import { PageProps } from 'gatsby'
import Layout from '../components/Layout' import Layout from '../components/Layout'
import queryString from 'query-string'
export default function PageGatsbySearch(props: PageProps): ReactElement { export default function PageGatsbySearch(props: PageProps): ReactElement {
const content = (props.data as any).content.edges[0].node.childPagesJson const parsed = queryString.parse(props.location.search)
const { title, description } = content const { text, tag } = parsed
return ( return (
<Layout title={title} description={description} location={props.location}> <Layout title={`Search for ${text || tag}`} location={props.location}>
<PageSearch text={text} tag={tag} queryResult={queryResult} /> <PageSearch />
</Layout> </Layout>
) )
} }
// export function getSearchQuery(
// page?: string | string[],
// offset?: string | string[],
// text?: string | string[],
// tag?: string | string[],
// priceQuery?: [string | undefined, string | undefined]
// ) {
// return {
// page: Number(page) || 1,
// offset: Number(offset) || 20,
// query: {
// text,
// tags: tag ? [tag] : undefined,
// price: priceQuery
// },
// sort: {
// created: -1
// }
// // Something in squid-js is weird when using 'categories: [type]'
// // which is the only way the query actually returns desired results.
// // But it doesn't follow 'SearchQuery' interface so we have to assign
// // it here.
// } as SearchQuery
// }
// Search.getInitialProps = async (context) => {
// const { text, tag, page, offset, minPrice, maxPrice } = context.query
// const minPriceParsed = priceQueryParamToWei(
// minPrice as string,
// 'Error parsing context.query.minPrice'
// )
// const maxPriceParsed = priceQueryParamToWei(
// maxPrice as string,
// 'Error parsing context.query.maxPrice'
// )
// const priceQuery =
// minPriceParsed || maxPriceParsed
// ? // sometimes TS gets a bit silly
// ([minPriceParsed, maxPriceParsed] as [
// string | undefined,
// string | undefined
// ])
// : undefined
// const aquarius = new Aquarius(config.aquariusUri as string, Logger)
// const queryResult = await aquarius.queryMetadata(
// getSearchQuery(page, offset, text, tag, priceQuery)
// )
// return {
// text: text,
// tag: tag,
// queryResult
// }
// }