From 99e0fdbf5990734acd911f8f5f89e313e1f8f945 Mon Sep 17 00:00:00 2001 From: Matchu Date: Thu, 22 Apr 2021 02:35:59 -0700 Subject: [PATCH] Add indicator for whether changes are saved Now, when viewing a saved outfit that you own, you'll see a "Saved" indicator if it matches the version on the server, or a temporary UI of "Not saved" and a tooltip if not. Auto-save coming next! --- src/app/WardrobePage/ItemsPanel.js | 147 +++++++++++++++++++------ src/app/WardrobePage/useOutfitState.js | 29 +++-- 2 files changed, 135 insertions(+), 41 deletions(-) diff --git a/src/app/WardrobePage/ItemsPanel.js b/src/app/WardrobePage/ItemsPanel.js index 430d587..d2ce512 100644 --- a/src/app/WardrobePage/ItemsPanel.js +++ b/src/app/WardrobePage/ItemsPanel.js @@ -17,8 +17,19 @@ import { Portal, Button, useToast, + Popover, + PopoverTrigger, + PopoverContent, + PopoverArrow, + PopoverBody, } from "@chakra-ui/react"; -import { EditIcon, QuestionIcon } from "@chakra-ui/icons"; +import { + CheckIcon, + EditIcon, + ExternalLinkIcon, + QuestionIcon, + WarningIcon, +} from "@chakra-ui/icons"; import { CSSTransition, TransitionGroup } from "react-transition-group"; import { useHistory } from "react-router-dom"; @@ -30,6 +41,7 @@ import { MdMoreVert } from "react-icons/md"; import useCurrentUser from "../components/useCurrentUser"; import gql from "graphql-tag"; import { useMutation } from "@apollo/client"; +import { outfitStatesAreEqual } from "./useOutfitState"; /** * ItemsPanel shows the items in the current outfit, and lets the user toggle @@ -255,16 +267,19 @@ function useOutfitSaving(outfitState) { const history = useHistory(); const toast = useToast(); - // Whether this outfit has *ever* been saved, vs a brand-new local outfit. - const hasBeenSaved = Boolean(outfitState.id); + // Whether this outfit is new, i.e. local-only, i.e. has _never_ been saved + // to the server. + const isNewOutfit = outfitState.id == null; + + // Whether this outfit's latest local changes have been saved to the server. + const latestVersionIsSaved = + outfitState.savedOutfitState && + outfitStatesAreEqual(outfitState, outfitState.savedOutfitState); // Only logged-in users can save outfits - and they can only save new outfits, // or outfits they created. const canSaveOutfit = - isLoggedIn && - (!hasBeenSaved || outfitState.creator?.id === currentUserId) && - // TODO: Add support for updating outfits - !hasBeenSaved; + isLoggedIn && (isNewOutfit || outfitState.creator?.id === currentUserId); const [sendSaveOutfitMutation, { loading: isSaving }] = useMutation( gql` @@ -365,18 +380,104 @@ function useOutfitSaving(outfitState) { return { canSaveOutfit, + isNewOutfit, isSaving, + latestVersionIsSaved, saveOutfit, }; } +/** + * OutfitSavingIndicator shows a Save button, or the "Saved" or "Saving" state, + * if the user can save this outfit. If not, this is empty! + */ +function OutfitSavingIndicator({ outfitState }) { + const { + canSaveOutfit, + isNewOutfit, + isSaving, + latestVersionIsSaved, + saveOutfit, + } = useOutfitSaving(outfitState); + + if (!canSaveOutfit) { + return null; + } + + if (isNewOutfit) { + return ( + + ); + } + + if (latestVersionIsSaved) { + return ( + + + Saved + + ); + } + + return ( + + + + + Not saved + + + + + + We're still working on this! For now, use{" "} + + + Classic DTI + + + {" "} + to save existing outfits. + + + + ); +} + /** * OutfitHeading is an editable outfit name, as a big pretty page heading! * It also contains the outfit menu, for saving etc. */ function OutfitHeading({ outfitState, dispatchToOutfit }) { - const { canSaveOutfit, isSaving, saveOutfit } = useOutfitSaving(outfitState); - return ( // The Editable wraps everything, including the menu, because the menu has // a Rename option. @@ -401,30 +502,10 @@ function OutfitHeading({ outfitState, dispatchToOutfit }) { - {canSaveOutfit && ( - <> - - - - )} - + + + +