1
0
Fork 0
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:
Emi Matchu 2023-08-10 17:15:07 -07:00
parent 6a59fa9f02
commit 3c1fcca986
10 changed files with 44 additions and 134 deletions

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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;

View file

@ -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];
}

View file

@ -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(

View file

@ -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;

View file

@ -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]
);

View file

@ -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}`}

View file

@ -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