Use the main app for outfit deletion, too

This commit is contained in:
Emi Matchu 2023-11-02 17:39:26 -07:00
parent d32c6459b0
commit 629706a182
3 changed files with 41 additions and 36 deletions

View file

@ -36,19 +36,13 @@ import {
} from "@chakra-ui/icons";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import {
Delay,
ErrorMessage,
getGraphQLErrorMessage,
Heading1,
Heading2,
} from "../util";
import { Delay, ErrorMessage, Heading1, Heading2 } from "../util";
import Item, { ItemListContainer, ItemListSkeleton } from "./Item";
import { BiRename } from "react-icons/bi";
import { IoCloudUploadOutline } from "react-icons/io5";
import { MdMoreVert } from "react-icons/md";
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
@ -455,25 +449,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
const { id, name } = outfitState;
const { isOpen, onOpen, onClose } = useDisclosure();
const [sendDeleteOutfitMutation, { loading, error }] = useMutation(
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();
},
},
);
const { status, error, mutateAsync } = useDeleteOutfitMutation();
return (
<>
@ -489,10 +465,9 @@ function DeleteOutfitMenuItem({ outfitState }) {
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
okay?
{error && (
{status === "error" && (
<ErrorMessage marginTop="1em">
Error deleting outfit: "{getGraphQLErrorMessage(error)}". Try
again?
Error deleting outfit: "{error.message}". Try again?
</ErrorMessage>
)}
</ModalBody>
@ -502,7 +477,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
<Button
colorScheme="red"
onClick={() =>
sendDeleteOutfitMutation({ variables: { id } })
mutateAsync(id)
.then(() => {
window.location = "/your-outfits";
})
@ -510,7 +485,9 @@ function DeleteOutfitMenuItem({ outfitState }) {
/* 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
</Button>

View file

@ -3,8 +3,6 @@ import { useToast } from "@chakra-ui/react";
import { useLocation, useNavigate } from "react-router-dom";
import { useDebounce } from "../util";
import useCurrentUser from "../components/useCurrentUser";
import gql from "graphql-tag";
import { useMutation } from "@apollo/client";
import { outfitStatesAreEqual } from "./useOutfitState";
import { useSaveOutfitMutation } from "../loaders/outfits";

View file

@ -8,7 +8,7 @@ export function useSavedOutfit(id, options) {
});
}
export function useSaveOutfitMutation(options) {
export function useSaveOutfitMutation(options = {}) {
const queryClient = useQueryClient();
return useMutation({
@ -16,7 +16,24 @@ export function useSaveOutfitMutation(options) {
mutationFn: saveOutfit,
onSuccess: (outfit) => {
queryClient.setQueryData(["outfits", outfit.id], outfit);
if (options.onSuccess) {
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);
}
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) {
return {
id: String(outfit.id),