Use mutation hooks
This commit is contained in:
parent
8170f9e14e
commit
1dd0488c62
6 changed files with 506 additions and 490 deletions
|
@ -47,7 +47,7 @@
|
|||
"moment-timezone": "^0.5.26",
|
||||
"qs": "^6.9.0",
|
||||
"react": "^16.9.0",
|
||||
"react-apollo": "^3.0.0",
|
||||
"react-apollo": "^3.1.3",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-dropzone": "^8.2.0",
|
||||
"react-error-boundary": "^1.2.5",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import { TypedMutation } from "../mutations";
|
||||
import makeMutation from "@saleor/hooks/makeMutation";
|
||||
import { categoryDetailsFragment } from "./queries";
|
||||
import {
|
||||
CategoryBulkDelete,
|
||||
|
@ -29,7 +29,7 @@ export const categoryDeleteMutation = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
export const TypedCategoryDeleteMutation = TypedMutation<
|
||||
export const useCategoryDeleteMutation = makeMutation<
|
||||
CategoryDelete,
|
||||
CategoryDeleteVariables
|
||||
>(categoryDeleteMutation);
|
||||
|
@ -48,7 +48,7 @@ export const categoryCreateMutation = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
export const TypedCategoryCreateMutation = TypedMutation<
|
||||
export const useCategoryCreateMutation = makeMutation<
|
||||
CategoryCreate,
|
||||
CategoryCreateVariables
|
||||
>(categoryCreateMutation);
|
||||
|
@ -67,7 +67,7 @@ export const categoryUpdateMutation = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
export const TypedCategoryUpdateMutation = TypedMutation<
|
||||
export const useCategoryUpdateMutation = makeMutation<
|
||||
CategoryUpdate,
|
||||
CategoryUpdateVariables
|
||||
>(categoryUpdateMutation);
|
||||
|
@ -82,7 +82,7 @@ export const categoryBulkDeleteMutation = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
export const TypedCategoryBulkDeleteMutation = TypedMutation<
|
||||
export const useCategoryBulkDeleteMutation = makeMutation<
|
||||
CategoryBulkDelete,
|
||||
CategoryBulkDeleteVariables
|
||||
>(categoryBulkDeleteMutation);
|
||||
|
|
|
@ -6,7 +6,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
|
|||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import CategoryCreatePage from "../components/CategoryCreatePage";
|
||||
import { TypedCategoryCreateMutation } from "../mutations";
|
||||
import { useCategoryCreateMutation } from "../mutations";
|
||||
import { CategoryCreate } from "../types/CategoryCreate";
|
||||
import { categoryListUrl, categoryUrl } from "../urls";
|
||||
|
||||
|
@ -31,9 +31,11 @@ export const CategoryCreateView: React.FC<CategoryCreateViewProps> = ({
|
|||
navigate(categoryUrl(data.categoryCreate.category.id));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<TypedCategoryCreateMutation onCompleted={handleSuccess}>
|
||||
{(createCategory, createCategoryResult) => {
|
||||
|
||||
const [createCategory, createCategoryResult] = useCategoryCreateMutation({
|
||||
onCompleted: handleSuccess
|
||||
});
|
||||
|
||||
const errors = maybe(
|
||||
() => createCategoryResult.data.categoryCreate.errors,
|
||||
[]
|
||||
|
@ -78,8 +80,5 @@ export const CategoryCreateView: React.FC<CategoryCreateViewProps> = ({
|
|||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedCategoryCreateMutation>
|
||||
);
|
||||
};
|
||||
export default CategoryCreateView;
|
||||
|
|
|
@ -24,9 +24,9 @@ import {
|
|||
CategoryUpdatePage
|
||||
} from "../components/CategoryUpdatePage/CategoryUpdatePage";
|
||||
import {
|
||||
TypedCategoryBulkDeleteMutation,
|
||||
TypedCategoryDeleteMutation,
|
||||
TypedCategoryUpdateMutation
|
||||
useCategoryBulkDeleteMutation,
|
||||
useCategoryDeleteMutation,
|
||||
useCategoryUpdateMutation
|
||||
} from "../mutations";
|
||||
import { TypedCategoryDetailsQuery } from "../queries";
|
||||
import { CategoryBulkDelete } from "../types/CategoryBulkDelete";
|
||||
|
@ -73,6 +73,11 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
navigate(categoryListUrl());
|
||||
}
|
||||
};
|
||||
|
||||
const [deleteCategory, deleteResult] = useCategoryDeleteMutation({
|
||||
onCompleted: handleCategoryDelete
|
||||
});
|
||||
|
||||
const handleCategoryUpdate = (data: CategoryUpdate) => {
|
||||
if (data.categoryUpdate.errors.length > 0) {
|
||||
const backgroundImageError = data.categoryUpdate.errors.find(
|
||||
|
@ -86,6 +91,27 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const [updateCategory, updateResult] = useCategoryUpdateMutation({
|
||||
onCompleted: handleCategoryUpdate
|
||||
});
|
||||
|
||||
const handleBulkCategoryDelete = (data: CategoryBulkDelete) => {
|
||||
if (data.categoryBulkDelete.errors.length === 0) {
|
||||
closeModal();
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
const [
|
||||
categoryBulkDelete,
|
||||
categoryBulkDeleteOpts
|
||||
] = useCategoryBulkDeleteMutation({
|
||||
onCompleted: handleBulkCategoryDelete
|
||||
});
|
||||
|
||||
const changeTab = (tabName: CategoryPageTab) => {
|
||||
reset();
|
||||
navigate(
|
||||
|
@ -114,11 +140,6 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedCategoryDeleteMutation onCompleted={handleCategoryDelete}>
|
||||
{(deleteCategory, deleteResult) => (
|
||||
<TypedCategoryUpdateMutation onCompleted={handleCategoryUpdate}>
|
||||
{(updateCategory, updateResult) => {
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const formTransitionState = getMutationState(
|
||||
updateResult.called,
|
||||
|
@ -130,6 +151,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
deleteResult.loading,
|
||||
maybe(() => deleteResult.data.categoryDelete.errors)
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedCategoryDetailsQuery
|
||||
displayLoader
|
||||
|
@ -137,19 +159,6 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
require={["category"]}
|
||||
>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleBulkCategoryDelete = (
|
||||
data: CategoryBulkDelete
|
||||
) => {
|
||||
if (data.categoryBulkDelete.errors.length === 0) {
|
||||
closeModal();
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
refetch();
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkProductDelete = (data: productBulkDelete) => {
|
||||
if (data.productBulkDelete.errors.length === 0) {
|
||||
closeModal();
|
||||
|
@ -172,10 +181,6 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
return (
|
||||
<>
|
||||
<WindowTitle title={maybe(() => data.category.name)} />
|
||||
<TypedCategoryBulkDeleteMutation
|
||||
onCompleted={handleBulkCategoryDelete}
|
||||
>
|
||||
{(categoryBulkDelete, categoryBulkDeleteOpts) => (
|
||||
<TypedProductBulkDeleteMutation
|
||||
onCompleted={handleBulkProductDelete}
|
||||
>
|
||||
|
@ -184,18 +189,14 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
categoryBulkDeleteOpts.called,
|
||||
categoryBulkDeleteOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
categoryBulkDeleteOpts.data
|
||||
.categoryBulkDelete.errors
|
||||
() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors
|
||||
)
|
||||
);
|
||||
const productBulkDeleteMutationState = getMutationState(
|
||||
productBulkDeleteOpts.called,
|
||||
productBulkDeleteOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
productBulkDeleteOpts.data.productBulkDelete
|
||||
.errors
|
||||
() => productBulkDeleteOpts.data.productBulkDelete.errors
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -207,26 +208,19 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
category={maybe(() => data.category)}
|
||||
disabled={loading}
|
||||
errors={maybe(
|
||||
() =>
|
||||
updateResult.data.categoryUpdate.errors
|
||||
() => updateResult.data.categoryUpdate.errors
|
||||
)}
|
||||
onAddCategory={() =>
|
||||
navigate(categoryAddUrl(id))
|
||||
}
|
||||
onAddCategory={() => navigate(categoryAddUrl(id))}
|
||||
onAddProduct={() => navigate(productAddUrl)}
|
||||
onBack={() =>
|
||||
navigate(
|
||||
maybe(
|
||||
() =>
|
||||
categoryUrl(
|
||||
data.category.parent.id
|
||||
),
|
||||
() => categoryUrl(data.category.parent.id),
|
||||
categoryListUrl()
|
||||
)
|
||||
)
|
||||
}
|
||||
onCategoryClick={id => () =>
|
||||
navigate(categoryUrl(id))}
|
||||
onCategoryClick={id => () => navigate(categoryUrl(id))}
|
||||
onDelete={() => openModal("delete")}
|
||||
onImageDelete={() =>
|
||||
updateCategory({
|
||||
|
@ -251,22 +245,19 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
onNextPage={loadNextPage}
|
||||
onPreviousPage={loadPreviousPage}
|
||||
pageInfo={pageInfo}
|
||||
onProductClick={id => () =>
|
||||
navigate(productUrl(id))}
|
||||
onProductClick={id => () => navigate(productUrl(id))}
|
||||
onSubmit={formData =>
|
||||
updateCategory({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
backgroundImageAlt:
|
||||
formData.backgroundImageAlt,
|
||||
backgroundImageAlt: formData.backgroundImageAlt,
|
||||
descriptionJson: JSON.stringify(
|
||||
formData.description
|
||||
),
|
||||
name: formData.name,
|
||||
seo: {
|
||||
description:
|
||||
formData.seoDescription,
|
||||
description: formData.seoDescription,
|
||||
title: formData.seoTitle
|
||||
}
|
||||
}
|
||||
|
@ -274,24 +265,17 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
data.category.products.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
data.category.products.edges.map(edge => edge.node)
|
||||
)}
|
||||
saveButtonBarState={formTransitionState}
|
||||
subcategories={maybe(() =>
|
||||
data.category.children.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
data.category.children.edges.map(edge => edge.node)
|
||||
)}
|
||||
subcategoryListToolbar={
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
openModal(
|
||||
"delete-categories",
|
||||
listElements
|
||||
)
|
||||
openModal("delete-categories", listElements)
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
|
@ -301,10 +285,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
openModal(
|
||||
"delete-products",
|
||||
listElements
|
||||
)
|
||||
openModal("delete-products", listElements)
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
|
@ -316,13 +297,9 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
toggleAll={toggleAll}
|
||||
/>
|
||||
<ActionDialog
|
||||
confirmButtonState={
|
||||
removeDialogTransitionState
|
||||
}
|
||||
confirmButtonState={removeDialogTransitionState}
|
||||
onClose={closeModal}
|
||||
onConfirm={() =>
|
||||
deleteCategory({ variables: { id } })
|
||||
}
|
||||
onConfirm={() => deleteCategory({ variables: { id } })}
|
||||
open={params.action === "delete"}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete category",
|
||||
|
@ -336,10 +313,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
values={{
|
||||
categoryName: (
|
||||
<strong>
|
||||
{maybe(
|
||||
() => data.category.name,
|
||||
"..."
|
||||
)}
|
||||
{maybe(() => data.category.name, "...")}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
|
@ -354,14 +328,12 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
params.action === "delete-categories" &&
|
||||
maybe(() => params.ids.length > 0)
|
||||
}
|
||||
confirmButtonState={
|
||||
categoryBulkDeleteMutationState
|
||||
}
|
||||
confirmButtonState={categoryBulkDeleteMutationState}
|
||||
onClose={closeModal}
|
||||
onConfirm={() =>
|
||||
categoryBulkDelete({
|
||||
variables: { ids: params.ids }
|
||||
})
|
||||
}).then(() => refetch())
|
||||
}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete categories",
|
||||
|
@ -371,15 +343,11 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} categories}}?"
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?"
|
||||
values={{
|
||||
counter: maybe(
|
||||
() => params.ids.length
|
||||
),
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: (
|
||||
<strong>
|
||||
{maybe(() => params.ids.length)}
|
||||
</strong>
|
||||
<strong>{maybe(() => params.ids.length)}</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
@ -390,14 +358,12 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={params.action === "delete-products"}
|
||||
confirmButtonState={
|
||||
productBulkDeleteMutationState
|
||||
}
|
||||
confirmButtonState={productBulkDeleteMutationState}
|
||||
onClose={closeModal}
|
||||
onConfirm={() =>
|
||||
productBulkDelete({
|
||||
variables: { ids: params.ids }
|
||||
})
|
||||
}).then(() => refetch())
|
||||
}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete products",
|
||||
|
@ -405,18 +371,13 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
})}
|
||||
variant="delete"
|
||||
>
|
||||
{" "}
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} products}}?"
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this product} other{{displayQuantity} products}}?"
|
||||
values={{
|
||||
counter: maybe(
|
||||
() => params.ids.length
|
||||
),
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: (
|
||||
<strong>
|
||||
{maybe(() => params.ids.length)}
|
||||
</strong>
|
||||
<strong>{maybe(() => params.ids.length)}</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
@ -426,17 +387,10 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
);
|
||||
}}
|
||||
</TypedProductBulkDeleteMutation>
|
||||
)}
|
||||
</TypedCategoryBulkDeleteMutation>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedCategoryDetailsQuery>
|
||||
);
|
||||
}}
|
||||
</TypedCategoryUpdateMutation>
|
||||
)}
|
||||
</TypedCategoryDeleteMutation>
|
||||
);
|
||||
};
|
||||
export default CategoryDetails;
|
||||
|
|
|
@ -18,7 +18,7 @@ import usePaginator, {
|
|||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
|
||||
import { TypedCategoryBulkDeleteMutation } from "../../mutations";
|
||||
import { useCategoryBulkDeleteMutation } from "../../mutations";
|
||||
import { TypedRootCategoriesQuery } from "../../queries";
|
||||
import { CategoryBulkDelete } from "../../types/CategoryBulkDelete";
|
||||
import {
|
||||
|
@ -138,17 +138,18 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
|||
reset();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<TypedCategoryBulkDeleteMutation
|
||||
onCompleted={handleCategoryBulkDelete}
|
||||
>
|
||||
{(categoryBulkDelete, categoryBulkDeleteOpts) => {
|
||||
|
||||
const [
|
||||
categoryBulkDelete,
|
||||
categoryBulkDeleteOpts
|
||||
] = useCategoryBulkDeleteMutation({
|
||||
onCompleted: handleCategoryBulkDelete
|
||||
});
|
||||
|
||||
const bulkDeleteState = getMutationState(
|
||||
categoryBulkDeleteOpts.called,
|
||||
categoryBulkDeleteOpts.loading,
|
||||
maybe(
|
||||
() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors
|
||||
)
|
||||
maybe(() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors)
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -251,9 +252,6 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
|||
</>
|
||||
);
|
||||
}}
|
||||
</TypedCategoryBulkDeleteMutation>
|
||||
);
|
||||
}}
|
||||
</TypedRootCategoriesQuery>
|
||||
);
|
||||
};
|
||||
|
|
65
src/hooks/makeMutation.ts
Normal file
65
src/hooks/makeMutation.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { ApolloError } from "apollo-client";
|
||||
import { DocumentNode } from "graphql";
|
||||
import {
|
||||
MutationFunction,
|
||||
MutationResult,
|
||||
useMutation as useBaseMutation
|
||||
} from "react-apollo";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import useNotifier from "./useNotifier";
|
||||
|
||||
type UseMutation<TData, TVariables> = [
|
||||
MutationFunction<TData, TVariables>,
|
||||
MutationResult<TData>
|
||||
];
|
||||
type UseMutationCbs<TData> = Partial<{
|
||||
onCompleted: (data: TData) => void;
|
||||
onError: (error: ApolloError) => void;
|
||||
}>;
|
||||
type UseMutationHook<TData, TVariables> = (
|
||||
cbs: UseMutationCbs<TData>
|
||||
) => UseMutation<TData, TVariables>;
|
||||
|
||||
function makeMutation<TData, TVariables>(
|
||||
mutation: DocumentNode
|
||||
): UseMutationHook<TData, TVariables> {
|
||||
function useMutation<TData, TVariables>({
|
||||
onCompleted,
|
||||
onError
|
||||
}: UseMutationCbs<TData>): UseMutation<TData, TVariables> {
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const [mutateFn, result] = useBaseMutation(mutation, {
|
||||
onCompleted,
|
||||
onError: (err: ApolloError) => {
|
||||
if (
|
||||
maybe(
|
||||
() =>
|
||||
err.graphQLErrors[0].extensions.exception.code ===
|
||||
"ReadOnlyException"
|
||||
)
|
||||
) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.readOnly)
|
||||
});
|
||||
} else {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
||||
});
|
||||
}
|
||||
if (onError) {
|
||||
onError(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [mutateFn, result];
|
||||
}
|
||||
|
||||
return useMutation;
|
||||
}
|
||||
|
||||
export default makeMutation;
|
Loading…
Reference in a new issue