2023-08-10 18:09:10 -07:00
|
|
|
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
|
2023-08-10 18:59:47 -07:00
|
|
|
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
|
2023-08-10 18:09:10 -07:00
|
|
|
import { createPersistedQueryLink } from "apollo-link-persisted-queries";
|
|
|
|
|
2024-01-28 07:00:29 -08:00
|
|
|
import { buildImpress2020Url } from "./impress-2020-config";
|
|
|
|
|
2023-08-10 18:59:47 -07:00
|
|
|
// Use Apollo's error messages in development.
|
|
|
|
if (process.env["NODE_ENV"] === "development") {
|
2024-09-09 16:10:45 -07:00
|
|
|
loadErrorMessages();
|
|
|
|
loadDevMessages();
|
2023-08-10 18:59:47 -07:00
|
|
|
}
|
|
|
|
|
2023-08-10 18:09:10 -07:00
|
|
|
// Teach Apollo to load certain fields from the cache, to avoid extra network
|
|
|
|
// requests. This happens a lot - e.g. reusing data from item search on the
|
|
|
|
// outfit immediately!
|
|
|
|
const typePolicies = {
|
2024-09-09 16:10:45 -07:00
|
|
|
Query: {
|
|
|
|
fields: {
|
|
|
|
closetList: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "ClosetList", id: args.id }, true);
|
|
|
|
},
|
|
|
|
items: (_, { args, toReference }) => {
|
|
|
|
return args.ids.map((id) =>
|
|
|
|
toReference({ __typename: "Item", id }, true),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
item: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "Item", id: args.id }, true);
|
|
|
|
},
|
|
|
|
petAppearanceById: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "PetAppearance", id: args.id }, true);
|
|
|
|
},
|
|
|
|
species: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "Species", id: args.id }, true);
|
|
|
|
},
|
|
|
|
color: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "Color", id: args.id }, true);
|
|
|
|
},
|
|
|
|
outfit: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "Outfit", id: args.id }, true);
|
|
|
|
},
|
|
|
|
user: (_, { args, toReference }) => {
|
|
|
|
return toReference({ __typename: "User", id: args.id }, true);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Item: {
|
|
|
|
fields: {
|
|
|
|
appearanceOn: (appearance, { args, readField, toReference }) => {
|
|
|
|
// If we already have this exact appearance in the cache, serve it!
|
|
|
|
if (appearance) {
|
|
|
|
return appearance;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { speciesId, colorId, altStyleId } = args;
|
|
|
|
console.debug(
|
|
|
|
"[appearanceOn] seeking cached appearance",
|
|
|
|
speciesId,
|
|
|
|
colorId,
|
|
|
|
altStyleId,
|
|
|
|
readField("id"),
|
|
|
|
);
|
|
|
|
|
|
|
|
// If this is an alt style, don't try to mess with clever caching.
|
|
|
|
// (Note that, if it's already in the cache, the first condition will
|
|
|
|
// catch that! This won't *always* force a fresh load!)
|
|
|
|
if (altStyleId != null) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we're going to see if this is a standard color, in which
|
|
|
|
// case we can reuse the standard color appearance if we already have
|
|
|
|
// it! This helps for fast loading when switching between standard
|
|
|
|
// colors.
|
|
|
|
const speciesStandardBodyId = readField(
|
|
|
|
"standardBodyId",
|
|
|
|
toReference({ __typename: "Species", id: speciesId }),
|
|
|
|
);
|
|
|
|
const colorIsStandard = readField(
|
|
|
|
"isStandard",
|
|
|
|
toReference({ __typename: "Color", id: colorId }),
|
|
|
|
);
|
|
|
|
if (speciesStandardBodyId == null || colorIsStandard == null) {
|
|
|
|
// We haven't loaded all the species/colors into cache yet. We might
|
|
|
|
// be loading them, depending on the page? Either way, return
|
|
|
|
// `undefined`, meaning we don't know how to serve this from cache.
|
|
|
|
// This will cause us to start loading it from the server.
|
|
|
|
console.debug("[appearanceOn] species/colors not loaded yet");
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (colorIsStandard) {
|
|
|
|
const itemId = readField("id");
|
|
|
|
console.debug(
|
|
|
|
"[appearanceOn] standard color, will read:",
|
|
|
|
`item-${itemId}-body-${speciesStandardBodyId}`,
|
|
|
|
);
|
|
|
|
return toReference({
|
|
|
|
__typename: "ItemAppearance",
|
|
|
|
id: `item-${itemId}-body-${speciesStandardBodyId}`,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
console.debug("[appearanceOn] non-standard color, failure");
|
|
|
|
// This isn't a standard color, so we don't support special
|
|
|
|
// cross-color caching for it. Return `undefined`, meaning we don't
|
|
|
|
// know how to serve this from cache. This will cause us to start
|
|
|
|
// loading it from the server.
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
currentUserOwnsThis: (cachedValue, { readField }) => {
|
|
|
|
if (cachedValue != null) {
|
|
|
|
return cachedValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we know what items this user owns? If so, scan for this item.
|
|
|
|
const currentUserRef = readField("currentUser", {
|
|
|
|
__ref: "ROOT_QUERY",
|
|
|
|
});
|
|
|
|
if (!currentUserRef) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
const thisItemId = readField("id");
|
|
|
|
const itemsTheyOwn = readField("itemsTheyOwn", currentUserRef);
|
|
|
|
if (!itemsTheyOwn) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const theyOwnThisItem = itemsTheyOwn.some(
|
|
|
|
(itemRef) => readField("id", itemRef) === thisItemId,
|
|
|
|
);
|
|
|
|
return theyOwnThisItem;
|
|
|
|
},
|
|
|
|
currentUserWantsThis: (cachedValue, { readField }) => {
|
|
|
|
if (cachedValue != null) {
|
|
|
|
return cachedValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we know what items this user owns? If so, scan for this item.
|
|
|
|
const currentUserRef = readField("currentUser", {
|
|
|
|
__ref: "ROOT_QUERY",
|
|
|
|
});
|
|
|
|
if (!currentUserRef) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
const thisItemId = readField("id");
|
|
|
|
const itemsTheyWant = readField("itemsTheyWant", currentUserRef);
|
|
|
|
if (!itemsTheyWant) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const theyWantThisItem = itemsTheyWant.some(
|
|
|
|
(itemRef) => readField("id", itemRef) === thisItemId,
|
|
|
|
);
|
|
|
|
return theyWantThisItem;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
ClosetList: {
|
|
|
|
fields: {
|
|
|
|
// When loading the updated contents of a list, replace it entirely.
|
|
|
|
items: { merge: false },
|
|
|
|
},
|
|
|
|
},
|
2023-08-10 18:09:10 -07:00
|
|
|
};
|
|
|
|
|
2023-11-03 18:16:57 -07:00
|
|
|
const cache = new InMemoryCache({ typePolicies });
|
|
|
|
|
2023-08-10 18:09:10 -07:00
|
|
|
const httpLink = createHttpLink({
|
2024-09-09 16:10:45 -07:00
|
|
|
uri: buildImpress2020Url("/api/graphql"),
|
2023-08-10 18:09:10 -07:00
|
|
|
});
|
|
|
|
|
2023-11-03 18:16:57 -07:00
|
|
|
const link = createPersistedQueryLink({
|
2024-09-09 16:10:45 -07:00
|
|
|
useGETForHashedQueries: true,
|
2023-11-03 18:16:57 -07:00
|
|
|
}).concat(httpLink);
|
2023-08-10 18:09:10 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* apolloClient is the global Apollo Client instance we use for GraphQL
|
|
|
|
* queries. This is how we communicate with the server!
|
|
|
|
*/
|
2023-11-03 18:16:57 -07:00
|
|
|
const apolloClient = new ApolloClient({
|
2024-09-09 16:10:45 -07:00
|
|
|
link,
|
|
|
|
cache,
|
|
|
|
connectToDevTools: true,
|
2023-11-03 18:16:57 -07:00
|
|
|
});
|
2023-08-10 18:09:10 -07:00
|
|
|
|
2023-11-03 18:16:57 -07:00
|
|
|
export default apolloClient;
|