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, {
|
import SpeciesFacesPicker, {
|
||||||
colorIsBasic,
|
colorIsBasic,
|
||||||
} from "./ItemPage/SpeciesFacesPicker";
|
} from "./ItemPage/SpeciesFacesPicker";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import Head from "next/head";
|
|
||||||
|
|
||||||
function ItemPage() {
|
// Removed for the wardrobe-2020 case.
|
||||||
const { query } = useRouter();
|
// TODO: Refactor this stuff, do we even need ItemPageContent really?
|
||||||
return <ItemPageContent itemId={query.itemId} />;
|
// function ItemPage() {
|
||||||
}
|
// const { query } = useRouter();
|
||||||
|
// return <ItemPageContent itemId={query.itemId} />;
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ItemPageContent is the content of ItemPage, but we also use it as the
|
* ItemPageContent is the content of ItemPage, but we also use it as the
|
||||||
|
@ -102,11 +102,6 @@ export function ItemPageContent({ itemId, isEmbedded = false }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isEmbedded && item?.name && (
|
|
||||||
<Head>
|
|
||||||
<title>{item?.name} | Dress to Impress</title>
|
|
||||||
</Head>
|
|
||||||
)}
|
|
||||||
<ItemPageLayout item={item} isEmbedded={isEmbedded}>
|
<ItemPageLayout item={item} isEmbedded={isEmbedded}>
|
||||||
<VStack spacing="8" marginTop="4">
|
<VStack spacing="8" marginTop="4">
|
||||||
<ItemPageDescription
|
<ItemPageDescription
|
||||||
|
|
|
@ -403,7 +403,7 @@ function CrossFadeImage(incomingImageProps) {
|
||||||
opacity: 0;
|
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 />
|
<img {...prevImageProps} aria-hidden />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -416,7 +416,7 @@ function CrossFadeImage(incomingImageProps) {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{/* eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element */}
|
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||||
<img
|
<img
|
||||||
{...currentImageProps}
|
{...currentImageProps}
|
||||||
// If the current image _is_ the incoming image, we'll allow
|
// If the current image _is_ the incoming image, we'll allow
|
||||||
|
@ -438,7 +438,7 @@ function CrossFadeImage(incomingImageProps) {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{/* eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element */}
|
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||||
<img
|
<img
|
||||||
{...incomingImageProps}
|
{...incomingImageProps}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
|
|
|
@ -49,7 +49,6 @@ 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 { gql, useMutation } from "@apollo/client";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,7 +454,6 @@ function OutfitHeading({ outfitState, outfitSaving, dispatchToOutfit }) {
|
||||||
function DeleteOutfitMenuItem({ outfitState }) {
|
function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
const { id, name } = outfitState;
|
const { id, name } = outfitState;
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const { push: pushHistory } = useRouter();
|
|
||||||
|
|
||||||
const [sendDeleteOutfitMutation, { loading, error }] = useMutation(
|
const [sendDeleteOutfitMutation, { loading, error }] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
|
@ -506,7 +504,7 @@ function DeleteOutfitMenuItem({ outfitState }) {
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
sendDeleteOutfitMutation({ variables: { id } })
|
sendDeleteOutfitMutation({ variables: { id } })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
pushHistory(`/your-outfits`);
|
window.location = "/your-outfits";
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
/* handled in error UI */
|
/* handled in error UI */
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useToast } from "@chakra-ui/react";
|
import { useToast } from "@chakra-ui/react";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
import { emptySearchQuery } from "./SearchToolbar";
|
import { emptySearchQuery } from "./SearchToolbar";
|
||||||
import ItemsAndSearchPanels from "./ItemsAndSearchPanels";
|
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
|
// 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
|
// "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
|
// the indicator isn't on the page, e.g. when searching.
|
||||||
// <Prompt /> in this component to prevent navigating away before saving.
|
// NOTE: This only applies to navigations leaving the wardrobe-2020 app, not
|
||||||
|
// within!
|
||||||
const outfitSaving = useOutfitSaving(outfitState, dispatchToOutfit);
|
const outfitSaving = useOutfitSaving(outfitState, dispatchToOutfit);
|
||||||
|
|
||||||
// TODO: I haven't found a great place for this error UI yet, and this case
|
// TODO: I haven't found a great place for this error UI yet, and this case
|
||||||
|
@ -86,20 +86,6 @@ function WardrobePage() {
|
||||||
<WardrobeDevHacks />
|
<WardrobeDevHacks />
|
||||||
</SupportOnly>
|
</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
|
<WardrobePageLayout
|
||||||
previewAndControls={
|
previewAndControls={
|
||||||
<WardrobePreviewAndControls
|
<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;
|
export default WardrobePage;
|
||||||
|
|
|
@ -292,7 +292,6 @@ function AppearanceLayerSupportReviewStep({
|
||||||
marginTop="2"
|
marginTop="2"
|
||||||
>
|
>
|
||||||
{imageWithAlphaUrl && (
|
{imageWithAlphaUrl && (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
|
||||||
<img
|
<img
|
||||||
src={imageWithAlphaUrl}
|
src={imageWithAlphaUrl}
|
||||||
width={600}
|
width={600}
|
||||||
|
@ -445,10 +444,8 @@ async function mergeIntoImageWithAlpha(
|
||||||
imageOnWhite,
|
imageOnWhite,
|
||||||
conflictMode
|
conflictMode
|
||||||
);
|
);
|
||||||
const [
|
const [imageWithAlphaUrl, imageWithAlphaBlob] =
|
||||||
imageWithAlphaUrl,
|
await writeImageDataToUrlAndBlob(imageWithAlphaData);
|
||||||
imageWithAlphaBlob,
|
|
||||||
] = await writeImageDataToUrlAndBlob(imageWithAlphaData);
|
|
||||||
|
|
||||||
return [imageWithAlphaUrl, imageWithAlphaBlob, numWarnings];
|
return [imageWithAlphaUrl, imageWithAlphaBlob, numWarnings];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useToast } from "@chakra-ui/react";
|
import { useToast } from "@chakra-ui/react";
|
||||||
import { useRouter } from "next/router";
|
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 gql from "graphql-tag";
|
||||||
|
@ -9,7 +9,8 @@ import { outfitStatesAreEqual } from "./useOutfitState";
|
||||||
|
|
||||||
function useOutfitSaving(outfitState, dispatchToOutfit) {
|
function useOutfitSaving(outfitState, dispatchToOutfit) {
|
||||||
const { isLoggedIn, id: currentUserId } = useCurrentUser();
|
const { isLoggedIn, id: currentUserId } = useCurrentUser();
|
||||||
const { pathname, push: pushHistory } = useRouter();
|
const { pathname } = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
// There's not a way to reset an Apollo mutation state to clear out the error
|
// 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
|
// up the data from this mutation response, and combine it with the
|
||||||
// existing cached data, to make this smooth without any loading UI.
|
// existing cached data, to make this smooth without any loading UI.
|
||||||
if (pathname !== `/outfits/[outfitId]`) {
|
if (pathname !== `/outfits/[outfitId]`) {
|
||||||
pushHistory(`/outfits/${outfit.id}`);
|
navigate(`/outfits/${outfit.id}`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
@ -175,7 +176,7 @@ function useOutfitSaving(outfitState, dispatchToOutfit) {
|
||||||
// It's important that this callback _doesn't_ change when the outfit
|
// It's important that this callback _doesn't_ change when the outfit
|
||||||
// changes, so that the auto-save effect is only responding to the
|
// changes, so that the auto-save effect is only responding to the
|
||||||
// debounced state!
|
// debounced state!
|
||||||
[sendSaveOutfitMutation, pathname, pushHistory, toast]
|
[sendSaveOutfitMutation, pathname, navigate, toast]
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveOutfit = React.useCallback(
|
const saveOutfit = React.useCallback(
|
||||||
|
|
|
@ -4,7 +4,6 @@ import produce, { enableMapSet } from "immer";
|
||||||
import { useQuery, useApolloClient } from "@apollo/client";
|
import { useQuery, useApolloClient } from "@apollo/client";
|
||||||
|
|
||||||
import { itemAppearanceFragment } from "../components/useOutfitAppearance";
|
import { itemAppearanceFragment } from "../components/useOutfitAppearance";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
|
@ -382,25 +381,26 @@ const EMPTY_CUSTOMIZATION_STATE = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function useParseOutfitUrl() {
|
function useParseOutfitUrl() {
|
||||||
const { query } = useRouter();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
// We memoize this to make `outfitStateWithoutExtras` an even more reliable
|
// We memoize this to make `outfitStateWithoutExtras` an even more reliable
|
||||||
// stable object!
|
// stable object!
|
||||||
const memoizedOutfitState = React.useMemo(
|
const memoizedOutfitState = React.useMemo(
|
||||||
() => readOutfitStateFromQuery(query),
|
() => readOutfitStateFromSearchParams(searchParams),
|
||||||
[query]
|
[query]
|
||||||
);
|
);
|
||||||
|
|
||||||
return memoizedOutfitState;
|
return memoizedOutfitState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readOutfitStateFromQuery(query) {
|
function readOutfitStateFromSearchParams(searchParams) {
|
||||||
// For the /outfits/:id page, ignore the query string, and just wait for the
|
// For the /outfits/:id page, ignore the query string, and just wait for the
|
||||||
// outfit data to load in!
|
// outfit data to load in!
|
||||||
if (query.outfitId != null) {
|
const outfitId = searchParams.get("outfitId");
|
||||||
|
if (outfitId != null) {
|
||||||
return {
|
return {
|
||||||
...EMPTY_CUSTOMIZATION_STATE,
|
...EMPTY_CUSTOMIZATION_STATE,
|
||||||
id: query.outfitId,
|
id: outfitId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,52 +408,16 @@ export function readOutfitStateFromQuery(query) {
|
||||||
// not specified.
|
// not specified.
|
||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
name: getValueFromQuery(query.name),
|
name: searchParams.get("name"),
|
||||||
speciesId: getValueFromQuery(query.species) || "1",
|
speciesId: searchParams.get("species") || "1",
|
||||||
colorId: getValueFromQuery(query.color) || "8",
|
colorId: searchParams.get("color") || "8",
|
||||||
pose: getValueFromQuery(query.pose) || "HAPPY_FEM",
|
pose: searchParams.get("pose") || "HAPPY_FEM",
|
||||||
appearanceId: getValueFromQuery(query.state) || null,
|
appearanceId: searchParams.get("state") || null,
|
||||||
wornItemIds: new Set(getListFromQuery(query["objects[]"])),
|
wornItemIds: new Set(searchParams.getAll("objects[]")),
|
||||||
closetedItemIds: new Set(getListFromQuery(query["closet[]"])),
|
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) {
|
function getOutfitStateFromOutfitData(outfit) {
|
||||||
if (!outfit) {
|
if (!outfit) {
|
||||||
return EMPTY_CUSTOMIZATION_STATE;
|
return EMPTY_CUSTOMIZATION_STATE;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Box, Button, Flex, Select } from "@chakra-ui/react";
|
import { Box, Button, Flex, Select } from "@chakra-ui/react";
|
||||||
import { useRouter } from "next/router";
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
function PaginationToolbar({
|
function PaginationToolbar({
|
||||||
isLoading,
|
isLoading,
|
||||||
|
@ -72,9 +72,9 @@ function PaginationToolbar({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRouterPagination(totalCount, numPerPage) {
|
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 currentPageIndex = Math.floor(currentOffset / numPerPage);
|
||||||
const currentPageNumber = currentPageIndex + 1;
|
const currentPageNumber = currentPageIndex + 1;
|
||||||
|
@ -82,11 +82,12 @@ export function useRouterPagination(totalCount, numPerPage) {
|
||||||
|
|
||||||
const buildPageUrl = React.useCallback(
|
const buildPageUrl = React.useCallback(
|
||||||
(newPageNumber) => {
|
(newPageNumber) => {
|
||||||
const newParams = new URLSearchParams(query);
|
setSearchParams((newParams) => {
|
||||||
const newPageIndex = newPageNumber - 1;
|
const newPageIndex = newPageNumber - 1;
|
||||||
const newOffset = newPageIndex * numPerPage;
|
const newOffset = newPageIndex * numPerPage;
|
||||||
newParams.set("offset", newOffset);
|
newParams.set("offset", newOffset);
|
||||||
return "?" + newParams.toString();
|
return newParams;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[query, numPerPage]
|
[query, numPerPage]
|
||||||
);
|
);
|
||||||
|
|
|
@ -225,7 +225,6 @@ function ItemThumbnail({ item, tradeMatchingMode }) {
|
||||||
position: relative;
|
position: relative;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
||||||
<img
|
<img
|
||||||
src={safeImageUrl(item.thumbnailUrl, { preferArchive })}
|
src={safeImageUrl(item.thumbnailUrl, { preferArchive })}
|
||||||
alt={`Thumbnail art for ${item.name}`}
|
alt={`Thumbnail art for ${item.name}`}
|
||||||
|
|
|
@ -41,7 +41,8 @@ OpenneoImpressItems::Application.routes.draw do
|
||||||
resources :neopets_users, :only => [:new, :create], :path => 'neopets-users'
|
resources :neopets_users, :only => [:new, :create], :path => 'neopets-users'
|
||||||
end
|
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/load' => 'pets#load', :as => :load_pet
|
||||||
post '/pets/submit' => 'pets#submit', :method => :post
|
post '/pets/submit' => 'pets#submit', :method => :post
|
||||||
|
|
Loading…
Reference in a new issue