diff --git a/next.config.js b/next.config.js
index f7c64e8..1147494 100644
--- a/next.config.js
+++ b/next.config.js
@@ -29,6 +29,17 @@ module.exports = {
destination: "/user/:userId/lists",
permanent: true,
},
+ {
+ source: "/items/:itemId/trades/offering",
+ destination:
+ "https://impress.openneo.net/items/:itemId/trades/offering",
+ permanent: true,
+ },
+ {
+ source: "/items/:itemId/trades/seeking",
+ destination: "https://impress.openneo.net/items/:itemId/trades/seeking",
+ permanent: true,
+ },
];
},
};
diff --git a/pages/items/[itemId]/trades/offering.tsx b/pages/items/[itemId]/trades/offering.tsx
deleted file mode 100644
index 3e6a4a6..0000000
--- a/pages/items/[itemId]/trades/offering.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { GetServerSideProps } from "next";
-import { ItemTradesOfferingPage } from "../../../../src/app/ItemTradesPage";
-import { gql, loadGraphqlQuery } from "../../../../src/server/ssr-graphql";
-// @ts-ignore doesn't understand module.exports
-import { oneDay, oneWeek } from "../../../../src/server/util";
-
-export default function ItemTradesOfferingPageWrapper() {
- return ;
-}
-
-export const getServerSideProps: GetServerSideProps = async ({
- params,
- res,
-}) => {
- if (params?.itemId == null) {
- throw new Error(`assertion error: itemId param is missing`);
- }
-
- // Load the most important, most stable item data to get onto the page ASAP.
- // We'll cache it real hard, to help it load extra-fast for popular items!
- const { errors, graphqlState } = await loadGraphqlQuery({
- query: gql`
- query ItemsTradesOffering_GetServerSideProps($itemId: ID!) {
- item(id: $itemId) {
- id
- name
- thumbnailUrl
- description
- isNc
- isPb
- createdAt
- }
- }
- `,
- variables: { itemId: params.itemId },
- });
- if (errors) {
- console.warn(
- `[SSR: /items/[itemId]/trades/offering] Skipping GraphQL preloading, got errors:`
- );
- for (const error of errors) {
- console.warn(`[SSR: /items/[itemId]/trades/offering]`, error);
- }
- return { props: { graphqlState: {} } };
- }
-
- // Cache this very aggressively, because it's such stable data!
- res.setHeader(
- "Cache-Control",
- `public, s-maxage=${oneDay}, stale-while-revalidate=${oneWeek}`
- );
-
- return { props: { graphqlState } };
-};
diff --git a/pages/items/[itemId]/trades/seeking.tsx b/pages/items/[itemId]/trades/seeking.tsx
deleted file mode 100644
index f8fa05b..0000000
--- a/pages/items/[itemId]/trades/seeking.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { GetServerSideProps } from "next";
-import { ItemTradesSeekingPage } from "../../../../src/app/ItemTradesPage";
-import { gql, loadGraphqlQuery } from "../../../../src/server/ssr-graphql";
-// @ts-ignore doesn't understand module.exports
-import { oneDay, oneWeek } from "../../../../src/server/util";
-
-export default function ItemTradesSeekingPageWrapper() {
- return ;
-}
-
-export const getServerSideProps: GetServerSideProps = async ({
- params,
- res,
-}) => {
- if (params?.itemId == null) {
- throw new Error(`assertion error: itemId param is missing`);
- }
-
- // Load the most important, most stable item data to get onto the page ASAP.
- // We'll cache it real hard, to help it load extra-fast for popular items!
- const { errors, graphqlState } = await loadGraphqlQuery({
- query: gql`
- query ItemsTradesSeeking_GetServerSideProps($itemId: ID!) {
- item(id: $itemId) {
- id
- name
- thumbnailUrl
- description
- isNc
- isPb
- createdAt
- }
- }
- `,
- variables: { itemId: params.itemId },
- });
- if (errors) {
- console.warn(
- `[SSR: /items/[itemId]/trades/seeking] Skipping GraphQL preloading, got errors:`
- );
- for (const error of errors) {
- console.warn(`[SSR: /items/[itemId]/trades/seeking]`, error);
- }
- return { props: { graphqlState: {} } };
- }
-
- // Cache this very aggressively, because it's such stable data!
- res.setHeader(
- "Cache-Control",
- `public, s-maxage=${oneDay}, stale-while-revalidate=${oneWeek}`
- );
-
- return { props: { graphqlState } };
-};
diff --git a/src/app/ItemPage.js b/src/app/ItemPage.js
index bbed293..f4da1a5 100644
--- a/src/app/ItemPage.js
+++ b/src/app/ItemPage.js
@@ -92,7 +92,7 @@ export function ItemPageContent({ itemId, isEmbedded = false }) {
}
}
`,
- { variables: { itemId }, returnPartialData: true }
+ { variables: { itemId }, returnPartialData: true },
);
if (error) {
@@ -302,7 +302,7 @@ const ItemPageOwnWantListsDropdownButton = React.forwardRef(
);
- }
+ },
);
function ItemPageOwnWantListsDropdownContent({ closetLists, item }) {
@@ -336,7 +336,7 @@ function ItemPageOwnWantsListsDropdownRow({ closetList, item }) {
}
}
`,
- { context: { sendAuth: true } }
+ { context: { sendAuth: true } },
);
const [sendRemoveFromListMutation] = useMutation(
@@ -352,7 +352,7 @@ function ItemPageOwnWantsListsDropdownRow({ closetList, item }) {
}
}
`,
- { context: { sendAuth: true } }
+ { context: { sendAuth: true } },
);
const onChange = React.useCallback(
@@ -397,7 +397,13 @@ function ItemPageOwnWantsListsDropdownRow({ closetList, item }) {
});
}
},
- [closetList, item, sendAddToListMutation, sendRemoveFromListMutation, toast]
+ [
+ closetList,
+ item,
+ sendAddToListMutation,
+ sendRemoveFromListMutation,
+ toast,
+ ],
);
return (
@@ -445,7 +451,7 @@ function ItemPageOwnButton({ itemId, isChecked }) {
context: { sendAuth: true },
},
],
- }
+ },
);
const [sendRemoveMutation] = useMutation(
@@ -476,7 +482,7 @@ function ItemPageOwnButton({ itemId, isChecked }) {
context: { sendAuth: true },
},
],
- }
+ },
);
return (
@@ -571,7 +577,7 @@ function ItemPageWantButton({ itemId, isChecked }) {
context: { sendAuth: true },
},
],
- }
+ },
);
const [sendRemoveMutation] = useMutation(
@@ -602,7 +608,7 @@ function ItemPageWantButton({ itemId, isChecked }) {
context: { sendAuth: true },
},
],
- }
+ },
);
return (
@@ -676,7 +682,7 @@ function ItemPageTradeLinks({ itemId, isEmbedded }) {
}
}
`,
- { variables: { itemId } }
+ { variables: { itemId } },
);
if (error) {
@@ -690,7 +696,7 @@ function ItemPageTradeLinks({ itemId, isEmbedded }) {
(Math.random() > 0.5 ? "HAPPY_FEM" : "HAPPY_MASC"),
- []
+ [],
);
const [petState, setPetState] = React.useState({
// We'll fill these in once the canonical appearance data arrives.
@@ -787,11 +793,11 @@ function ItemPageOutfitPreview({ itemId }) {
});
const [preferredSpeciesId, setPreferredSpeciesId] = useLocalStorage(
"DTIItemPreviewPreferredSpeciesId",
- null
+ null,
);
const [preferredColorId, setPreferredColorId] = useLocalStorage(
"DTIItemPreviewPreferredColorId",
- null
+ null,
);
const setPetStateFromUserAction = React.useCallback(
@@ -826,7 +832,7 @@ function ItemPageOutfitPreview({ itemId }) {
return newPetState;
}),
- [setPreferredColorId, setPreferredSpeciesId]
+ [setPreferredColorId, setPreferredSpeciesId],
);
// We don't need to reload this query when preferred species/color change, so
@@ -845,7 +851,11 @@ function ItemPageOutfitPreview({ itemId }) {
// query after this loads, because our Apollo cache can't detect the
// shared item appearance. (For standard colors though, our logic to
// cover standard-color switches works for this preloading too.)
- const { loading: loadingGQL, error: errorGQL, data } = useQuery(
+ const {
+ loading: loadingGQL,
+ error: errorGQL,
+ data,
+ } = useQuery(
gql`
query ItemPageOutfitPreview(
$itemId: ID!
@@ -920,7 +930,7 @@ function ItemPageOutfitPreview({ itemId }) {
appearanceId: canonicalPetAppearance?.id,
});
},
- }
+ },
);
const compatibleBodies =
@@ -936,7 +946,7 @@ function ItemPageOutfitPreview({ itemId }) {
compatibleBodies.length === 1 &&
!compatibleBodies[0].representsAllBodies &&
(data?.item?.name || "").includes(
- data?.item?.canonicalAppearance?.body?.canonicalAppearance?.species?.name
+ data?.item?.canonicalAppearance?.body?.canonicalAppearance?.species?.name,
);
const couldProbablyModelMoreData = !isProbablySpeciesSpecific;
@@ -982,7 +992,7 @@ function ItemPageOutfitPreview({ itemId }) {
appearanceId: null,
});
},
- [valids, idealPose, setPetStateFromUserAction]
+ [valids, idealPose, setPetStateFromUserAction],
);
const borderColor = useColorModeValue("green.700", "green.400");
@@ -1201,8 +1211,8 @@ function ExpandOnGroupHover({ children, ...props }) {
// I don't think this is possible, but I'd like to know if it happens!
logAndCapture(
new Error(
- `Measurer node not ready during effect. Transition won't be smooth.`
- )
+ `Measurer node not ready during effect. Transition won't be smooth.`,
+ ),
);
return;
}
@@ -1283,8 +1293,8 @@ export function ItemZonesInfo({
const sortedZonesAndTheirBodies = [...zoneLabelsAndTheirBodies].sort((a, b) =>
buildSortKeyForZoneLabelsAndTheirBodies(a).localeCompare(
- buildSortKeyForZoneLabelsAndTheirBodies(b)
- )
+ buildSortKeyForZoneLabelsAndTheirBodies(b),
+ ),
);
const restrictedZoneLabels = [
@@ -1296,8 +1306,8 @@ export function ItemZonesInfo({
// preview available in the list has the zones listed here.
const bodyGroups = new Set(
zoneLabelsAndTheirBodies.map(({ bodies }) =>
- bodies.map((b) => b.id).join(",")
- )
+ bodies.map((b) => b.id).join(","),
+ ),
);
const showBodyInfo = bodyGroups.size > 1;
diff --git a/src/app/ItemTradesPage.js b/src/app/ItemTradesPage.js
deleted file mode 100644
index 25c15f1..0000000
--- a/src/app/ItemTradesPage.js
+++ /dev/null
@@ -1,517 +0,0 @@
-import React from "react";
-import { ClassNames } from "@emotion/react";
-import {
- Box,
- Button,
- Flex,
- Skeleton,
- useColorModeValue,
- useToken,
-} from "@chakra-ui/react";
-import gql from "graphql-tag";
-import { useQuery } from "@apollo/client";
-import Link from "next/link";
-import { useRouter } from "next/router";
-
-import { Heading2 } from "./util";
-import ItemPageLayout from "./ItemPageLayout";
-import useCurrentUser from "./components/useCurrentUser";
-import { ChevronDownIcon, ChevronUpIcon } from "@chakra-ui/icons";
-import Head from "next/head";
-
-export function ItemTradesOfferingPage() {
- return (
-
- );
-}
-
-export function ItemTradesSeekingPage() {
- return (
-
- );
-}
-
-function ItemTradesPage({
- title,
- userHeading,
- compareColumnLabel,
- tradesQuery,
-}) {
- const { query } = useRouter();
- const { itemId } = query;
-
- const { error, data } = useQuery(
- gql`
- query ItemTradesPage($itemId: ID!) {
- item(id: $itemId) {
- id
- name
- isNc
- isPb
- thumbnailUrl
- description
- createdAt
- ncTradeValueText
- }
- }
- `,
- { variables: { itemId }, returnPartialData: true }
- );
-
- if (error) {
- return {error.message};
- }
-
- return (
- <>
-
- {data?.item?.name && (
-
- {data?.item?.name} | {title} | Dress to Impress
-
- )}
-
-
-
- {title}
-
-
-
- >
- );
-}
-
-function ItemTradesTable({
- itemId,
- userHeading,
- compareColumnLabel,
- tradesQuery,
-}) {
- const { isLoggedIn } = useCurrentUser();
- const { loading, error, data } = useQuery(tradesQuery, {
- variables: { itemId },
- context: { sendAuth: true },
- });
-
- const [isShowingInactiveTrades, setIsShowingInactiveTrades] = React.useState(
- false
- );
-
- const shouldShowCompareColumn = isLoggedIn;
-
- // We partially randomize trade sorting, but we want it to stay stable across
- // re-renders. To do this, we can use `getTradeSortKey`, which will either
- // build a new sort key for the trade, or return the cached one from the
- // `tradeSortKeys` map.
- const tradeSortKeys = React.useMemo(() => new Map(), []);
- const getTradeSortKey = (trade) => {
- if (!tradeSortKeys.has(trade.id)) {
- tradeSortKeys.set(
- trade.id,
- getVaguelyRandomizedTradeSortKey(
- trade.user.lastTradeActivity,
- trade.user.matchingItems.length
- )
- );
- }
- return tradeSortKeys.get(trade.id);
- };
-
- const allTrades = [...(data?.item?.trades || [])];
-
- // Only trades from users active within the last 6 months are shown by
- // default. The user can toggle to the full view, though!
- const sixMonthsAgo = new Date();
- sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
- const activeTrades = allTrades.filter(
- (t) => new Date(t.user.lastTradeActivity) > sixMonthsAgo
- );
-
- const trades = isShowingInactiveTrades ? allTrades : activeTrades;
- trades.sort((a, b) => getTradeSortKey(b).localeCompare(getTradeSortKey(a)));
-
- const numInactiveTrades = allTrades.length - activeTrades.length;
-
- if (error) {
- return {error.message};
- }
-
- const minorColumnWidth = {
- base: shouldShowCompareColumn ? "23%" : "30%",
- md: "20ex",
- };
-
- return (
-
- {({ css }) => (
-
-
-
-
-
- {/* A small wording tweak to fit better on the xsmall screens! */}
- Last active
- Last edit
-
- {shouldShowCompareColumn && (
-
-
- {compareColumnLabel}
-
- Matches
-
- )}
-
- {userHeading}
-
- List
-
-
-
- {loading && (
- <>
-
-
-
-
-
- >
- )}
- {!loading &&
- trades.length > 0 &&
- trades.map((trade) => (
-
- ))}
- {!loading && trades.length === 0 && (
-
-
- No trades yet!
-
-
- )}
-
-
- {numInactiveTrades > 0 && (
-
-
-
- )}
-
- )}
-
- );
-}
-
-function ItemTradesTableRow({
- href,
- username,
- listName,
- lastTradeActivity,
- matchingItems,
- shouldShowCompareColumn,
-}) {
- const { push: pushHistory } = useRouter();
- const onClick = React.useCallback(() => pushHistory(href), [
- pushHistory,
- href,
- ]);
- const focusBackground = useColorModeValue("gray.100", "gray.600");
-
- const sortedMatchingItems = [...matchingItems].sort((a, b) =>
- a.name.localeCompare(b.name)
- );
-
- return (
-
- {({ css }) => (
-
-
- {formatVagueDate(lastTradeActivity)}
-
- {shouldShowCompareColumn && (
-
- {matchingItems.length > 0 ? (
-
- {sortedMatchingItems.slice(0, 4).map((item) => (
-
-
- {item.name}
-
-
- ))}
- {matchingItems.length > 4 && (
- + {matchingItems.length - 4} more
- )}
-
- ) : (
- <>
- No matches
- None
- >
- )}
-
- )}
- {username}
-
-
-
- {listName}
-
-
-
-
- )}
-
- );
-}
-
-function ItemTradesTableRowSkeleton({ shouldShowCompareColumn }) {
- return (
-
-
- X
-
-
- X
-
-
- X
-
- {shouldShowCompareColumn && (
-
- X
-
- )}
-
- );
-}
-
-function ItemTradesTableCell({ children, as = "td", ...props }) {
- const borderColor = useColorModeValue("gray.300", "gray.400");
- const borderColorCss = useToken("colors", borderColor);
- const borderRadiusCss = useToken("radii", "md");
-
- return (
-
- {({ css }) => (
-
- {children}
-
- )}
-
- );
-}
-
-function isThisWeek(date) {
- const startOfThisWeek = new Date();
- startOfThisWeek.setDate(startOfThisWeek.getDate() - 7);
- return date > startOfThisWeek;
-}
-
-const shortMonthYearFormatter = new Intl.DateTimeFormat("en", {
- month: "short",
- year: "numeric",
-});
-
-function formatVagueDate(dateString) {
- const date = new Date(dateString);
-
- if (isThisWeek(date)) {
- return "This week";
- }
-
- return shortMonthYearFormatter.format(date);
-}
-
-function getVaguelyRandomizedTradeSortKey(dateString, numMatchingItems) {
- const date = new Date(dateString);
- const hasMatchingItems = numMatchingItems >= 1;
-
- // "This week" sorts after all other dates, but with a random factor! I don't
- // want people worrying about gaming themselves up to the very top, just be
- // active and trust the system 😅 (I figure that, if you care enough to "game"
- // the system by faking activity every week, you probably also care enough to
- // be... making real trades every week lmao)
- //
- // We also prioritize having matches, but we don't bother to sort _how many_
- // matches, to decrease the power of gaming with large honeypot lists, and
- // because it's hard to judge how good matches are anyway.
- if (isThisWeek(date)) {
- const matchingItemsKey = hasMatchingItems
- ? "ZZmatchingZZ"
- : "AAnotmatchingAA";
- return `ZZZthisweekZZZ-${matchingItemsKey}-${Math.random()}`;
- }
-
- return dateString;
-}