Use exclusively search hooks

This commit is contained in:
dominik-zeglen 2019-11-20 11:24:42 +01:00
parent 13a8d398d2
commit 135658eb11
9 changed files with 127 additions and 292 deletions

View file

@ -1,10 +1,10 @@
import { SearchQueryVariables } from "./containers/BaseSearch";
import { SearchVariables } from "./hooks/makeSearch";
import { ListSettings, ListViews } from "./types";
export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/";
export const API_URI = process.env.API_URI || "/graphql/";
export const DEFAULT_INITIAL_SEARCH_DATA: SearchQueryVariables = {
export const DEFAULT_INITIAL_SEARCH_DATA: SearchVariables = {
after: null,
first: 20,
query: ""

View file

@ -1,77 +0,0 @@
import { DocumentNode } from "graphql";
import React from "react";
import Debounce from "../components/Debounce";
import { TypedQuery, TypedQueryResult } from "../queries";
export interface SearchQueryVariables {
after?: string;
first: number;
query: string;
}
interface BaseSearchProps<
TQuery,
TQueryVariables extends SearchQueryVariables
> {
children: (props: {
loadMore: () => void;
search: (query: string) => void;
result: TypedQueryResult<TQuery, TQueryVariables>;
}) => React.ReactElement<any>;
variables: TQueryVariables;
}
function BaseSearch<TQuery, TQueryVariables extends SearchQueryVariables>(
query: DocumentNode,
loadMoreFn: (result: TypedQueryResult<TQuery, TQueryVariables>) => void
) {
const Query = TypedQuery<TQuery, TQueryVariables>(query);
class BaseSearchComponent extends React.Component<
BaseSearchProps<TQuery, TQueryVariables>,
SearchQueryVariables
> {
state: SearchQueryVariables = {
first: this.props.variables.first,
query: this.props.variables.query
};
search = (query: string) => {
if (query === undefined) {
this.setState({ query: "" });
} else {
this.setState({ query });
}
};
render() {
const { children, variables } = this.props;
return (
<Debounce debounceFn={this.search} time={200}>
{search => (
<Query
displayLoader={true}
variables={{
...variables,
query: this.state.query
}}
>
{result =>
children({
loadMore: () => loadMoreFn(result),
result,
search
})
}
</Query>
)}
</Debounce>
);
}
}
return BaseSearchComponent;
}
export default BaseSearch;

View file

@ -1,47 +0,0 @@
import { DocumentNode } from "graphql";
import { PageInfoFragment } from "@saleor/types/PageInfoFragment";
import BaseSearch, { SearchQueryVariables } from "./BaseSearch";
export interface SearchQuery {
search: {
edges: Array<{
node: any;
}>;
pageInfo: PageInfoFragment;
};
}
function TopLevelSearch<
TQuery extends SearchQuery,
TQueryVariables extends SearchQueryVariables
>(query: DocumentNode) {
return BaseSearch<TQuery, TQueryVariables>(query, result => {
if (result.data.search.pageInfo.hasNextPage) {
result.loadMore(
(prev, next) => {
if (
prev.search.pageInfo.endCursor === next.search.pageInfo.endCursor
) {
return prev;
}
return {
...prev,
search: {
...prev.search,
edges: [...prev.search.edges, ...next.search.edges],
pageInfo: next.search.pageInfo
}
};
},
{
...result.variables,
after: result.data.search.pageInfo.endCursor
}
);
}
});
}
export default TopLevelSearch;

View file

@ -1,6 +1,6 @@
import gql from "graphql-tag";
import TopLevelSearch from "../containers/TopLevelSearch";
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
import { TypedQuery } from "../queries";
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
import {
@ -314,7 +314,7 @@ export const searchOrderVariant = gql`
}
}
`;
export const SearchOrderVariant = TopLevelSearch<
export const useOrderVariantSearch = makeTopLevelSearch<
SearchOrderVariantType,
SearchOrderVariantVariables
>(searchOrderVariant);

View file

@ -26,7 +26,7 @@ import OrderPaymentVoidDialog from "../../components/OrderPaymentVoidDialog";
import OrderProductAddDialog from "../../components/OrderProductAddDialog";
import OrderShippingMethodEditDialog from "../../components/OrderShippingMethodEditDialog";
import OrderOperations from "../../containers/OrderOperations";
import { SearchOrderVariant, TypedOrderDetailsQuery } from "../../queries";
import { TypedOrderDetailsQuery, useOrderVariantSearch } from "../../queries";
import { OrderDetails_order } from "../../types/OrderDetails";
import {
orderListUrl,
@ -81,6 +81,13 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
} = useCustomerSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const {
loadMore,
search: variantSearch,
result: variantSearchOpts
} = useOrderVariantSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
return (
<TypedOrderDetailsQuery
@ -502,51 +509,41 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
})
}
/>
<SearchOrderVariant
variables={DEFAULT_INITIAL_SEARCH_DATA}
>
{({
loadMore,
search: variantSearch,
result: variantSearchOpts
}) => (
<OrderProductAddDialog
confirmButtonState={getMutationState(
orderLinesAdd.opts.called,
orderLinesAdd.opts.loading,
maybe(
() =>
orderLinesAdd.opts.data
.draftOrderLinesCreate.errors
)
)}
loading={variantSearchOpts.loading}
open={params.action === "add-order-line"}
hasMore={maybe(
() =>
variantSearchOpts.data.search.pageInfo
.hasNextPage
)}
products={maybe(() =>
variantSearchOpts.data.search.edges.map(
edge => edge.node
)
)}
onClose={closeModal}
onFetch={variantSearch}
onFetchMore={loadMore}
onSubmit={variants =>
orderLinesAdd.mutate({
id,
input: variants.map(variant => ({
quantity: 1,
variantId: variant.id
}))
})
}
/>
<OrderProductAddDialog
confirmButtonState={getMutationState(
orderLinesAdd.opts.called,
orderLinesAdd.opts.loading,
maybe(
() =>
orderLinesAdd.opts.data.draftOrderLinesCreate
.errors
)
)}
</SearchOrderVariant>
loading={variantSearchOpts.loading}
open={params.action === "add-order-line"}
hasMore={maybe(
() =>
variantSearchOpts.data.search.pageInfo
.hasNextPage
)}
products={maybe(() =>
variantSearchOpts.data.search.edges.map(
edge => edge.node
)
)}
onClose={closeModal}
onFetch={variantSearch}
onFetchMore={loadMore}
onSubmit={variants =>
orderLinesAdd.mutate({
id,
input: variants.map(variant => ({
quantity: 1,
variantId: variant.id
}))
})
}
/>
</>
)}
<OrderAddressEditDialog

View file

@ -30,7 +30,7 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc";
import { FetchMoreProps } from "@saleor/types";
import { SearchAttributes_productType_availableAttributes_edges_node } from "../../containers/SearchAttributes/types/SearchAttributes";
import { SearchAttributes_productType_availableAttributes_edges_node } from "../../hooks/useAvailableAttributeSearch/types/SearchAttributes";
const useStyles = makeStyles(theme => ({
actions: {

View file

@ -1,7 +1,7 @@
import gql from "graphql-tag";
import makeSearch from "@saleor/hooks/makeSearch";
import { pageInfoFragment } from "@saleor/queries";
import BaseSearch from "../../../containers/BaseSearch";
import {
SearchAttributes,
SearchAttributesVariables
@ -37,24 +37,33 @@ export const searchAttributes = gql`
}
`;
export default BaseSearch<SearchAttributes, SearchAttributesVariables>(
export default makeSearch<SearchAttributes, SearchAttributesVariables>(
searchAttributes,
result =>
result.loadMore(
(prev, next) => ({
...prev,
productType: {
...prev.productType,
availableAttributes: {
...prev.productType.availableAttributes,
edges: [
...prev.productType.availableAttributes.edges,
...next.productType.availableAttributes.edges
],
pageInfo: next.productType.availableAttributes.pageInfo
}
(prev, next) => {
if (
prev.productType.availableAttributes.pageInfo.endCursor ===
next.productType.availableAttributes.pageInfo.endCursor
) {
return prev;
}
}),
return {
...prev,
productType: {
...prev.productType,
availableAttributes: {
...prev.productType.availableAttributes,
edges: [
...prev.productType.availableAttributes.edges,
...next.productType.availableAttributes.edges
],
pageInfo: next.productType.availableAttributes.pageInfo
}
}
};
},
{
after: result.data.productType.availableAttributes.pageInfo.endCursor
}

View file

@ -4,6 +4,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import { attributeUrl } from "@saleor/attributes/urls";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -19,7 +20,7 @@ import ProductTypeDetailsPage, {
ProductTypeForm
} from "../../components/ProductTypeDetailsPage";
import ProductTypeOperations from "../../containers/ProductTypeOperations";
import SearchAttributes from "../../containers/SearchAttributes";
import useAvailableAttributeSearch from "../../hooks/useAvailableAttributeSearch";
import { TypedProductTypeDetailsQuery } from "../../queries";
import { AssignAttribute } from "../../types/AssignAttribute";
import { ProductTypeDelete } from "../../types/ProductTypeDelete";
@ -46,6 +47,12 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
const productAttributeListActions = useBulkActions();
const variantAttributeListActions = useBulkActions();
const intl = useIntl();
const { loadMore, search, result } = useAvailableAttributeSearch({
variables: {
...DEFAULT_INITIAL_SEARCH_DATA,
id
}
});
return (
<ProductTypeUpdateErrors>
@ -330,109 +337,55 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
)
}}
/>
{!dataLoading && (
<SearchAttributes
variables={{
first: 15,
id,
query: ""
}}
>
{({ search, result }) => {
const fetchMore = () =>
result.loadMore(
(prev, next) => {
if (
prev.productType.availableAttributes
.pageInfo.endCursor ===
next.productType.availableAttributes
.pageInfo.endCursor
) {
return prev;
}
return {
...prev,
productType: {
...prev.productType,
availableAttributes: {
...prev.productType.availableAttributes,
edges: [
...prev.productType
.availableAttributes.edges,
...next.productType
.availableAttributes.edges
],
pageInfo:
next.productType.availableAttributes
.pageInfo
}
}
};
},
{
after:
result.data.productType.availableAttributes
.pageInfo.endCursor
}
);
return (
<>
{Object.keys(AttributeTypeEnum).map(key => (
<AssignAttributeDialog
attributes={maybe(() =>
result.data.productType.availableAttributes.edges.map(
edge => edge.node
{!dataLoading &&
Object.keys(AttributeTypeEnum).map(key => (
<AssignAttributeDialog
attributes={maybe(() =>
result.data.productType.availableAttributes.edges.map(
edge => edge.node
)
)}
confirmButtonState={assignTransactionState}
errors={maybe(
() =>
assignAttribute.opts.data.attributeAssign.errors.map(
err => err.message
),
[]
)}
loading={result.loading}
onClose={closeModal}
onSubmit={handleAssignAttribute}
onFetch={search}
onFetchMore={loadMore}
onOpen={result.refetch}
hasMore={maybe(
() =>
result.data.productType.availableAttributes
.pageInfo.hasNextPage,
false
)}
open={
params.action === "assign-attribute" &&
params.type === AttributeTypeEnum[key]
}
selected={maybe(() => params.ids, [])}
onToggle={attributeId => {
const ids = maybe(() => params.ids, []);
navigate(
productTypeUrl(id, {
...params,
ids: ids.includes(attributeId)
? params.ids.filter(
selectedId => selectedId !== attributeId
)
)}
confirmButtonState={assignTransactionState}
errors={maybe(
() =>
assignAttribute.opts.data.attributeAssign.errors.map(
err => err.message
),
[]
)}
loading={result.loading}
onClose={closeModal}
onSubmit={handleAssignAttribute}
onFetch={search}
onFetchMore={fetchMore}
onOpen={result.refetch}
hasMore={maybe(
() =>
result.data.productType
.availableAttributes.pageInfo
.hasNextPage,
false
)}
open={
params.action === "assign-attribute" &&
params.type === AttributeTypeEnum[key]
}
selected={maybe(() => params.ids, [])}
onToggle={attributeId => {
const ids = maybe(() => params.ids, []);
navigate(
productTypeUrl(id, {
...params,
ids: ids.includes(attributeId)
? params.ids.filter(
selectedId =>
selectedId !== attributeId
)
: [...ids, attributeId]
})
);
}}
key={key}
/>
))}
</>
);
}}
</SearchAttributes>
)}
: [...ids, attributeId]
})
);
}}
key={key}
/>
))}
<ProductTypeDeleteDialog
confirmButtonState={deleteTransactionState}
name={maybe(() => data.productType.name, "...")}