Use the main app for outfit deletion, too
This commit is contained in:
parent
d32c6459b0
commit
629706a182
3 changed files with 41 additions and 36 deletions
|
@ -36,19 +36,13 @@ import {
|
||||||
} from "@chakra-ui/icons";
|
} from "@chakra-ui/icons";
|
||||||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||||
|
|
||||||
import {
|
import { Delay, ErrorMessage, Heading1, Heading2 } from "../util";
|
||||||
Delay,
|
|
||||||
ErrorMessage,
|
|
||||||
getGraphQLErrorMessage,
|
|
||||||
Heading1,
|
|
||||||
Heading2,
|
|
||||||
} from "../util";
|
|
||||||
import Item, { ItemListContainer, ItemListSkeleton } from "./Item";
|
import Item, { ItemListContainer, ItemListSkeleton } from "./Item";
|
||||||
import { BiRename } from "react-icons/bi";
|
import { BiRename } from "react-icons/bi";
|
||||||
import { IoCloudUploadOutline } from "react-icons/io5";
|
import { IoCloudUploadOutline } from "react-icons/io5";
|
||||||
import { MdMoreVert } from "react-icons/md";
|
import { MdMoreVert } from "react-icons/md";
|
||||||
import { buildOutfitUrl } from "./useOutfitState";
|
import { buildOutfitUrl } from "./useOutfitState";
|
||||||
import { gql, useMutation } from "@apollo/client";
|
import { useDeleteOutfitMutation } from "../loaders/outfits";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ItemsPanel shows the items in the current outfit, and lets the user toggle
|
* ItemsPanel shows the items in the current outfit, and lets the user toggle
|
||||||
|
@ -455,25 +449,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
const { id, name } = outfitState;
|
const { id, name } = outfitState;
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
const [sendDeleteOutfitMutation, { loading, error }] = useMutation(
|
const { status, error, mutateAsync } = useDeleteOutfitMutation();
|
||||||
gql`
|
|
||||||
mutation DeleteOutfitMenuItem($id: ID!) {
|
|
||||||
deleteOutfit(id: $id)
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
context: { sendAuth: true },
|
|
||||||
update(cache) {
|
|
||||||
// Once this is deleted, evict it from the local cache, and "garbage
|
|
||||||
// collect" to force all queries referencing this outfit to reload the
|
|
||||||
// next time we see them. (This is especially important since we're
|
|
||||||
// about to redirect to the user outfits page, which shouldn't show
|
|
||||||
// the outfit anymore!)
|
|
||||||
cache.evict(`Outfit:${id}`);
|
|
||||||
cache.gc();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -489,10 +465,9 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
We'll delete this data and remove it from your list of outfits.
|
We'll delete this data and remove it from your list of outfits.
|
||||||
Links and image embeds pointing to this outfit will break. Is that
|
Links and image embeds pointing to this outfit will break. Is that
|
||||||
okay?
|
okay?
|
||||||
{error && (
|
{status === "error" && (
|
||||||
<ErrorMessage marginTop="1em">
|
<ErrorMessage marginTop="1em">
|
||||||
Error deleting outfit: "{getGraphQLErrorMessage(error)}". Try
|
Error deleting outfit: "{error.message}". Try again?
|
||||||
again?
|
|
||||||
</ErrorMessage>
|
</ErrorMessage>
|
||||||
)}
|
)}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@ -502,7 +477,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
<Button
|
<Button
|
||||||
colorScheme="red"
|
colorScheme="red"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
sendDeleteOutfitMutation({ variables: { id } })
|
mutateAsync(id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location = "/your-outfits";
|
window.location = "/your-outfits";
|
||||||
})
|
})
|
||||||
|
@ -510,7 +485,9 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
/* handled in error UI */
|
/* handled in error UI */
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
isLoading={loading}
|
// We continue to show the loading spinner in the success case,
|
||||||
|
// while we redirect away!
|
||||||
|
isLoading={status === "pending" || status === "success"}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { useToast } from "@chakra-ui/react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { useDebounce } from "../util";
|
import { useDebounce } from "../util";
|
||||||
import useCurrentUser from "../components/useCurrentUser";
|
import useCurrentUser from "../components/useCurrentUser";
|
||||||
import gql from "graphql-tag";
|
|
||||||
import { useMutation } from "@apollo/client";
|
|
||||||
import { outfitStatesAreEqual } from "./useOutfitState";
|
import { outfitStatesAreEqual } from "./useOutfitState";
|
||||||
import { useSaveOutfitMutation } from "../loaders/outfits";
|
import { useSaveOutfitMutation } from "../loaders/outfits";
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export function useSavedOutfit(id, options) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSaveOutfitMutation(options) {
|
export function useSaveOutfitMutation(options = {}) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
@ -16,7 +16,24 @@ export function useSaveOutfitMutation(options) {
|
||||||
mutationFn: saveOutfit,
|
mutationFn: saveOutfit,
|
||||||
onSuccess: (outfit) => {
|
onSuccess: (outfit) => {
|
||||||
queryClient.setQueryData(["outfits", outfit.id], outfit);
|
queryClient.setQueryData(["outfits", outfit.id], outfit);
|
||||||
|
if (options.onSuccess) {
|
||||||
options.onSuccess(outfit);
|
options.onSuccess(outfit);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDeleteOutfitMutation(options = {}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
...options,
|
||||||
|
mutationFn: deleteOutfit,
|
||||||
|
onSuccess: (emptyData, id, context) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["outfits", String(id)] });
|
||||||
|
if (options.onSuccess) {
|
||||||
|
options.onSuccess(emptyData, id, context);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -80,6 +97,19 @@ async function saveOutfit({
|
||||||
return res.json().then(normalizeOutfit);
|
return res.json().then(normalizeOutfit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteOutfit(id) {
|
||||||
|
const res = await fetch(`/outfits/${encodeURIComponent(id)}.json`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-Token": getCSRFToken(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`deleting outfit failed: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeOutfit(outfit) {
|
function normalizeOutfit(outfit) {
|
||||||
return {
|
return {
|
||||||
id: String(outfit.id),
|
id: String(outfit.id),
|
||||||
|
|
Loading…
Reference in a new issue