forked from OpenNeo/impress
Remove next/router references
Once again, not really tested, but we don't have the same errors as before so!
This commit is contained in:
parent
6a59fa9f02
commit
3c1fcca986
10 changed files with 44 additions and 134 deletions
|
@ -56,13 +56,13 @@ import useCurrentUser from "./components/useCurrentUser";
|
|||
import SpeciesFacesPicker, {
|
||||
colorIsBasic,
|
||||
} from "./ItemPage/SpeciesFacesPicker";
|
||||
import { useRouter } from "next/router";
|
||||
import Head from "next/head";
|
||||
|
||||
function ItemPage() {
|
||||
const { query } = useRouter();
|
||||
return <ItemPageContent itemId={query.itemId} />;
|
||||
}
|
||||
// Removed for the wardrobe-2020 case.
|
||||
// TODO: Refactor this stuff, do we even need ItemPageContent really?
|
||||
// function ItemPage() {
|
||||
// const { query } = useRouter();
|
||||
// return <ItemPageContent itemId={query.itemId} />;
|
||||
// }
|
||||
|
||||
/**
|
||||
* ItemPageContent is the content of ItemPage, but we also use it as the
|
||||
|
@ -102,11 +102,6 @@ export function ItemPageContent({ itemId, isEmbedded = false }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
{!isEmbedded && item?.name && (
|
||||
<Head>
|
||||
<title>{item?.name} | Dress to Impress</title>
|
||||
</Head>
|
||||
)}
|
||||
<ItemPageLayout item={item} isEmbedded={isEmbedded}>
|
||||
<VStack spacing="8" marginTop="4">
|
||||
<ItemPageDescription
|
||||
|
|
|
@ -403,7 +403,7 @@ function CrossFadeImage(incomingImageProps) {
|
|||
opacity: 0;
|
||||
`}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element */}
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<img {...prevImageProps} aria-hidden />
|
||||
</div>
|
||||
)}
|
||||
|
@ -416,7 +416,7 @@ function CrossFadeImage(incomingImageProps) {
|
|||
opacity: 1;
|
||||
`}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element */}
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<img
|
||||
{...currentImageProps}
|
||||
// If the current image _is_ the incoming image, we'll allow
|
||||
|
@ -438,7 +438,7 @@ function CrossFadeImage(incomingImageProps) {
|
|||
opacity: 0;
|
||||
`}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element */}
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<img
|
||||
{...incomingImageProps}
|
||||
aria-hidden
|
||||
|
|
|
@ -49,7 +49,6 @@ import { IoCloudUploadOutline } from "react-icons/io5";
|
|||
import { MdMoreVert } from "react-icons/md";
|
||||
import { buildOutfitUrl } from "./useOutfitState";
|
||||
import { gql, useMutation } from "@apollo/client";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
/**
|
||||
* ItemsPanel shows the items in the current outfit, and lets the user toggle
|
||||
|
@ -455,7 +454,6 @@ function OutfitHeading({ outfitState, outfitSaving, dispatchToOutfit }) {
|
|||
function DeleteOutfitMenuItem({ outfitState }) {
|
||||
const { id, name } = outfitState;
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { push: pushHistory } = useRouter();
|
||||
|
||||
const [sendDeleteOutfitMutation, { loading, error }] = useMutation(
|
||||
gql`
|
||||
|
@ -506,7 +504,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
|||
onClick={() =>
|
||||
sendDeleteOutfitMutation({ variables: { id } })
|
||||
.then(() => {
|
||||
pushHistory(`/your-outfits`);
|
||||
window.location = "/your-outfits";
|
||||
})
|
||||
.catch((e) => {
|
||||
/* handled in error UI */
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
import { useToast } from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { emptySearchQuery } from "./SearchToolbar";
|
||||
import ItemsAndSearchPanels from "./ItemsAndSearchPanels";
|
||||
|
@ -30,8 +29,9 @@ function WardrobePage() {
|
|||
|
||||
// We manage outfit saving up here, rather than at the point of the UI where
|
||||
// "Saving" indicators appear. That way, auto-saving still happens even when
|
||||
// the indicator isn't on the page, e.g. when searching. We also mount a
|
||||
// <Prompt /> in this component to prevent navigating away before saving.
|
||||
// the indicator isn't on the page, e.g. when searching.
|
||||
// NOTE: This only applies to navigations leaving the wardrobe-2020 app, not
|
||||
// within!
|
||||
const outfitSaving = useOutfitSaving(outfitState, dispatchToOutfit);
|
||||
|
||||
// TODO: I haven't found a great place for this error UI yet, and this case
|
||||
|
@ -86,20 +86,6 @@ function WardrobePage() {
|
|||
<WardrobeDevHacks />
|
||||
</SupportOnly>
|
||||
|
||||
{/*
|
||||
* TODO: This might unnecessarily block navigations that we don't
|
||||
* necessarily need to, e.g., navigating back to Your Outfits while the
|
||||
* save request is in flight. We could instead submit the save mutation
|
||||
* immediately on client-side nav, and have each outfit save mutation
|
||||
* install a `beforeunload` handler that ensures that you don't close
|
||||
* the page altogether while it's in flight. But let's start simple and
|
||||
* see how annoying it actually is in practice lol
|
||||
*/}
|
||||
<Prompt
|
||||
when={shouldBlockNavigation}
|
||||
message="Are you sure you want to leave? Your changes might not be saved."
|
||||
/>
|
||||
|
||||
<WardrobePageLayout
|
||||
previewAndControls={
|
||||
<WardrobePreviewAndControls
|
||||
|
@ -163,36 +149,4 @@ function SavedOutfitMetaTags({ outfitState }) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt blocks client-side navigation via Next.js when the `when` prop is
|
||||
* true. This is our attempt at a drop-in replacement for the Prompt component
|
||||
* offered by react-router!
|
||||
*
|
||||
* Adapted from https://github.com/vercel/next.js/issues/2694#issuecomment-778225625
|
||||
*/
|
||||
function Prompt({ when, message }) {
|
||||
const router = useRouter();
|
||||
React.useEffect(() => {
|
||||
const handleWindowClose = (e) => {
|
||||
if (!when) return;
|
||||
e.preventDefault();
|
||||
return (e.returnValue = message);
|
||||
};
|
||||
const handleBrowseAway = () => {
|
||||
if (!when) return;
|
||||
if (window.confirm(message)) return;
|
||||
router.events.emit("routeChangeError");
|
||||
throw "routeChange aborted by <Prompt>.";
|
||||
};
|
||||
window.addEventListener("beforeunload", handleWindowClose);
|
||||
router.events.on("routeChangeStart", handleBrowseAway);
|
||||
return () => {
|
||||
window.removeEventListener("beforeunload", handleWindowClose);
|
||||
router.events.off("routeChangeStart", handleBrowseAway);
|
||||
};
|
||||
}, [when, message, router]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default WardrobePage;
|
||||
|
|
|
@ -292,7 +292,6 @@ function AppearanceLayerSupportReviewStep({
|
|||
marginTop="2"
|
||||
>
|
||||
{imageWithAlphaUrl && (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={imageWithAlphaUrl}
|
||||
width={600}
|
||||
|
@ -445,10 +444,8 @@ async function mergeIntoImageWithAlpha(
|
|||
imageOnWhite,
|
||||
conflictMode
|
||||
);
|
||||
const [
|
||||
imageWithAlphaUrl,
|
||||
imageWithAlphaBlob,
|
||||
] = await writeImageDataToUrlAndBlob(imageWithAlphaData);
|
||||
const [imageWithAlphaUrl, imageWithAlphaBlob] =
|
||||
await writeImageDataToUrlAndBlob(imageWithAlphaData);
|
||||
|
||||
return [imageWithAlphaUrl, imageWithAlphaBlob, numWarnings];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { useToast } from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useDebounce } from "../util";
|
||||
import useCurrentUser from "../components/useCurrentUser";
|
||||
import gql from "graphql-tag";
|
||||
|
@ -9,7 +9,8 @@ import { outfitStatesAreEqual } from "./useOutfitState";
|
|||
|
||||
function useOutfitSaving(outfitState, dispatchToOutfit) {
|
||||
const { isLoggedIn, id: currentUserId } = useCurrentUser();
|
||||
const { pathname, push: pushHistory } = useRouter();
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
// There's not a way to reset an Apollo mutation state to clear out the error
|
||||
|
@ -159,7 +160,7 @@ function useOutfitSaving(outfitState, dispatchToOutfit) {
|
|||
// up the data from this mutation response, and combine it with the
|
||||
// existing cached data, to make this smooth without any loading UI.
|
||||
if (pathname !== `/outfits/[outfitId]`) {
|
||||
pushHistory(`/outfits/${outfit.id}`);
|
||||
navigate(`/outfits/${outfit.id}`);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
|
@ -175,7 +176,7 @@ function useOutfitSaving(outfitState, dispatchToOutfit) {
|
|||
// It's important that this callback _doesn't_ change when the outfit
|
||||
// changes, so that the auto-save effect is only responding to the
|
||||
// debounced state!
|
||||
[sendSaveOutfitMutation, pathname, pushHistory, toast]
|
||||
[sendSaveOutfitMutation, pathname, navigate, toast]
|
||||
);
|
||||
|
||||
const saveOutfit = React.useCallback(
|
||||
|
|
|
@ -4,7 +4,6 @@ import produce, { enableMapSet } from "immer";
|
|||
import { useQuery, useApolloClient } from "@apollo/client";
|
||||
|
||||
import { itemAppearanceFragment } from "../components/useOutfitAppearance";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
enableMapSet();
|
||||
|
||||
|
@ -382,25 +381,26 @@ const EMPTY_CUSTOMIZATION_STATE = {
|
|||
};
|
||||
|
||||
function useParseOutfitUrl() {
|
||||
const { query } = useRouter();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
// We memoize this to make `outfitStateWithoutExtras` an even more reliable
|
||||
// stable object!
|
||||
const memoizedOutfitState = React.useMemo(
|
||||
() => readOutfitStateFromQuery(query),
|
||||
() => readOutfitStateFromSearchParams(searchParams),
|
||||
[query]
|
||||
);
|
||||
|
||||
return memoizedOutfitState;
|
||||
}
|
||||
|
||||
export function readOutfitStateFromQuery(query) {
|
||||
function readOutfitStateFromSearchParams(searchParams) {
|
||||
// For the /outfits/:id page, ignore the query string, and just wait for the
|
||||
// outfit data to load in!
|
||||
if (query.outfitId != null) {
|
||||
const outfitId = searchParams.get("outfitId");
|
||||
if (outfitId != null) {
|
||||
return {
|
||||
...EMPTY_CUSTOMIZATION_STATE,
|
||||
id: query.outfitId,
|
||||
id: outfitId,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -408,52 +408,16 @@ export function readOutfitStateFromQuery(query) {
|
|||
// not specified.
|
||||
return {
|
||||
id: null,
|
||||
name: getValueFromQuery(query.name),
|
||||
speciesId: getValueFromQuery(query.species) || "1",
|
||||
colorId: getValueFromQuery(query.color) || "8",
|
||||
pose: getValueFromQuery(query.pose) || "HAPPY_FEM",
|
||||
appearanceId: getValueFromQuery(query.state) || null,
|
||||
wornItemIds: new Set(getListFromQuery(query["objects[]"])),
|
||||
closetedItemIds: new Set(getListFromQuery(query["closet[]"])),
|
||||
name: searchParams.get("name"),
|
||||
speciesId: searchParams.get("species") || "1",
|
||||
colorId: searchParams.get("color") || "8",
|
||||
pose: searchParams.get("pose") || "HAPPY_FEM",
|
||||
appearanceId: searchParams.get("state") || null,
|
||||
wornItemIds: new Set(searchParams.getAll("objects[]")),
|
||||
closetedItemIds: new Set(searchParams.getAll("closet[]")),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getValueFromQuery reads the given value from Next's `router.query` as a
|
||||
* single value. For example:
|
||||
*
|
||||
* ?foo=bar -> "bar" -> "bar"
|
||||
* ?foo=bar&foo=baz -> ["bar", "baz"] -> "bar"
|
||||
* ?lol=huh -> undefined -> null
|
||||
*/
|
||||
function getValueFromQuery(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0];
|
||||
} else if (value != null) {
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getListFromQuery reads the given value from Next's `router.query` as a list
|
||||
* of values. For example:
|
||||
*
|
||||
* ?foo=bar -> "bar" -> ["bar"]
|
||||
* ?foo=bar&foo=baz -> ["bar", "baz"] -> ["bar", "baz"]
|
||||
* ?lol=huh -> undefined -> []
|
||||
*/
|
||||
function getListFromQuery(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
} else if (value != null) {
|
||||
return [value];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function getOutfitStateFromOutfitData(outfit) {
|
||||
if (!outfit) {
|
||||
return EMPTY_CUSTOMIZATION_STATE;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { Box, Button, Flex, Select } from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
function PaginationToolbar({
|
||||
isLoading,
|
||||
|
@ -72,9 +72,9 @@ function PaginationToolbar({
|
|||
}
|
||||
|
||||
export function useRouterPagination(totalCount, numPerPage) {
|
||||
const { query, push: pushHistory } = useRouter();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const currentOffset = parseInt(query.offset) || 0;
|
||||
const currentOffset = parseInt(searchParams.get("offset")) || 0;
|
||||
|
||||
const currentPageIndex = Math.floor(currentOffset / numPerPage);
|
||||
const currentPageNumber = currentPageIndex + 1;
|
||||
|
@ -82,11 +82,12 @@ export function useRouterPagination(totalCount, numPerPage) {
|
|||
|
||||
const buildPageUrl = React.useCallback(
|
||||
(newPageNumber) => {
|
||||
const newParams = new URLSearchParams(query);
|
||||
setSearchParams((newParams) => {
|
||||
const newPageIndex = newPageNumber - 1;
|
||||
const newOffset = newPageIndex * numPerPage;
|
||||
newParams.set("offset", newOffset);
|
||||
return "?" + newParams.toString();
|
||||
return newParams;
|
||||
});
|
||||
},
|
||||
[query, numPerPage]
|
||||
);
|
||||
|
|
|
@ -225,7 +225,6 @@ function ItemThumbnail({ item, tradeMatchingMode }) {
|
|||
position: relative;
|
||||
`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={safeImageUrl(item.thumbnailUrl, { preferArchive })}
|
||||
alt={`Thumbnail art for ${item.name}`}
|
||||
|
|
|
@ -41,7 +41,8 @@ OpenneoImpressItems::Application.routes.draw do
|
|||
resources :neopets_users, :only => [:new, :create], :path => 'neopets-users'
|
||||
end
|
||||
|
||||
get '/users/current-user/outfits' => 'outfits#index', :as => :current_user_outfits
|
||||
get '/your-outfits', to: 'outfits#index', as: :current_user_outfits
|
||||
get '/users/current-user/outfits', to: redirect('/your-outfits')
|
||||
|
||||
post '/pets/load' => 'pets#load', :as => :load_pet
|
||||
post '/pets/submit' => 'pets#submit', :method => :post
|
||||
|
|
Loading…
Reference in a new issue