forked from OpenNeo/impress
Add AppProvider to wardrobe-2020
Hey the app runs now! How exciting! It doesn't run *correctly* but it renders at all!!
This commit is contained in:
parent
aa76fbc933
commit
8d7eabf1e3
14 changed files with 549 additions and 64 deletions
|
@ -1,7 +1,19 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
|
||||
|
||||
import { WardrobePage } from "./wardrobe-2020";
|
||||
import { AppProvider, WardrobePage } from "./wardrobe-2020";
|
||||
|
||||
// Use Apollo's error messages in development.
|
||||
if (process.env["NODE_ENV"] === "development") {
|
||||
loadErrorMessages();
|
||||
loadDevMessages();
|
||||
}
|
||||
|
||||
const rootNode = document.querySelector("#wardrobe-2020-root");
|
||||
ReactDOM.render(<WardrobePage />, rootNode);
|
||||
ReactDOM.render(
|
||||
<AppProvider>
|
||||
<WardrobePage />
|
||||
</AppProvider>,
|
||||
rootNode
|
||||
);
|
||||
|
|
157
app/javascript/wardrobe-2020/AppProvider.js
Normal file
157
app/javascript/wardrobe-2020/AppProvider.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
import React from "react";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { Integrations } from "@sentry/tracing";
|
||||
import { Auth0Provider } from "@auth0/auth0-react";
|
||||
import { CSSReset, ChakraProvider, extendTheme } from "@chakra-ui/react";
|
||||
import { ApolloProvider } from "@apollo/client";
|
||||
import { useAuth0 } from "@auth0/auth0-react";
|
||||
import { mode } from "@chakra-ui/theme-tools";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
import buildApolloClient from "./apolloClient";
|
||||
|
||||
const theme = extendTheme({
|
||||
styles: {
|
||||
global: (props) => ({
|
||||
html: {
|
||||
// HACK: Chakra sets body as the relative position element, which is
|
||||
// fine, except its `min-height: 100%` doesn't actually work
|
||||
// unless paired with height on the root element too!
|
||||
height: "100%",
|
||||
},
|
||||
body: {
|
||||
background: mode("gray.50", "gray.800")(props),
|
||||
color: mode("green.800", "green.50")(props),
|
||||
transition: "all 0.25s",
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export default function AppProvider({ children }) {
|
||||
React.useEffect(() => setupLogging(), []);
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Auth0Provider
|
||||
domain="openneo.us.auth0.com"
|
||||
clientId="8LjFauVox7shDxVufQqnviUIywMuuC4r"
|
||||
redirectUri={
|
||||
process.env.NODE_ENV === "development"
|
||||
? "http://localhost:3000"
|
||||
: "https://impress-2020.openneo.net"
|
||||
}
|
||||
audience="https://impress-2020.openneo.net/api"
|
||||
scope=""
|
||||
>
|
||||
<DTIApolloProvider>
|
||||
<ChakraProvider theme={theme}>
|
||||
<CSSReset />
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
</DTIApolloProvider>
|
||||
</Auth0Provider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
function DTIApolloProvider({ children, additionalCacheState = {} }) {
|
||||
const auth0 = useAuth0();
|
||||
const auth0Ref = React.useRef(auth0);
|
||||
|
||||
React.useEffect(() => {
|
||||
auth0Ref.current = auth0;
|
||||
}, [auth0]);
|
||||
|
||||
// Save the first `additionalCacheState` we get as our `initialCacheState`,
|
||||
// which we'll use to initialize the client without having to wait a tick.
|
||||
const [initialCacheState, unusedSetInitialCacheState] =
|
||||
React.useState(additionalCacheState);
|
||||
|
||||
const client = React.useMemo(
|
||||
() =>
|
||||
buildApolloClient({
|
||||
getAuth0: () => auth0Ref.current,
|
||||
initialCacheState,
|
||||
}),
|
||||
[initialCacheState]
|
||||
);
|
||||
|
||||
// When we get a new `additionalCacheState` object, merge it into the cache:
|
||||
// copy the previous cache state, merge the new cache state's entries in,
|
||||
// and "restore" the new merged cache state.
|
||||
//
|
||||
// HACK: Using `useMemo` for this is a dastardly trick!! What we want is the
|
||||
// semantics of `useEffect` kinda, but we need to ensure it happens
|
||||
// *before* all the children below get rendered, so they don't fire off
|
||||
// unnecessary network requests. Using `useMemo` but throwing away the
|
||||
// result kinda does that. It's evil! It's nasty! It's... perfect?
|
||||
// (This operation is safe to run multiple times too, in case memo
|
||||
// re-runs it. It's just, y'know, a performance loss. Maybe it's
|
||||
// actually kinda perfect lol)
|
||||
//
|
||||
// I feel like there's probably a better way to do this... like, I want
|
||||
// the semantic of replacing this client with an updated client - but I
|
||||
// don't want to actually replace the client, because that'll break
|
||||
// other kinds of state, like requests loading in the shared layout.
|
||||
// Idk! I'll see how it goes!
|
||||
React.useMemo(() => {
|
||||
const previousCacheState = client.cache.extract();
|
||||
const mergedCacheState = { ...previousCacheState };
|
||||
for (const key of Object.keys(additionalCacheState)) {
|
||||
mergedCacheState[key] = {
|
||||
...mergedCacheState[key],
|
||||
...additionalCacheState[key],
|
||||
};
|
||||
}
|
||||
console.debug(
|
||||
"Merging Apollo cache:",
|
||||
additionalCacheState,
|
||||
mergedCacheState
|
||||
);
|
||||
client.cache.restore(mergedCacheState);
|
||||
}, [client, additionalCacheState]);
|
||||
|
||||
return <ApolloProvider client={client}>{children}</ApolloProvider>;
|
||||
}
|
||||
|
||||
function setupLogging() {
|
||||
Sentry.init({
|
||||
dsn: "https://c55875c3b0904264a1a99e5b741a221e@o506079.ingest.sentry.io/5595379",
|
||||
autoSessionTracking: true,
|
||||
integrations: [
|
||||
new Integrations.BrowserTracing({
|
||||
beforeNavigate: (context) => ({
|
||||
...context,
|
||||
// Assume any path segment starting with a digit is an ID, and replace
|
||||
// it with `:id`. This will help group related routes in Sentry stats.
|
||||
// NOTE: I'm a bit uncertain about the timing on this for tracking
|
||||
// client-side navs... but we now only track first-time
|
||||
// pageloads, and it definitely works correctly for them!
|
||||
name: window.location.pathname.replaceAll(/\/[0-9][^/]*/g, "/:id"),
|
||||
}),
|
||||
|
||||
// We have a _lot_ of location changes that don't actually signify useful
|
||||
// navigations, like in the wardrobe page. It could be useful to trace
|
||||
// them with better filtering someday, but frankly we don't use the perf
|
||||
// features besides Web Vitals right now, and those only get tracked on
|
||||
// first-time pageloads, anyway. So, don't track client-side navs!
|
||||
startTransactionOnLocationChange: false,
|
||||
}),
|
||||
],
|
||||
denyUrls: [
|
||||
// Don't log errors that were probably triggered by extensions and not by
|
||||
// our own app. (Apparently Sentry's setting to ignore browser extension
|
||||
// errors doesn't do this anywhere near as consistently as I'd expect?)
|
||||
//
|
||||
// Adapted from https://gist.github.com/impressiver/5092952, as linked in
|
||||
// https://docs.sentry.io/platforms/javascript/configuration/filtering/.
|
||||
/^chrome-extension:\/\//,
|
||||
/^moz-extension:\/\//,
|
||||
],
|
||||
|
||||
// Since we're only tracking first-page loads and not navigations, 100%
|
||||
// sampling isn't actually so much! Tune down if it becomes a problem, tho.
|
||||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
|
@ -854,7 +854,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
name
|
||||
restrictedZones {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
compatibleBodiesAndTheirZones {
|
||||
body {
|
||||
|
@ -867,7 +867,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
}
|
||||
zones {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
}
|
||||
canonicalAppearance(
|
||||
|
|
|
@ -80,40 +80,34 @@ function WardrobePage() {
|
|||
// that need it, where it's more useful and more performant to access
|
||||
// via context.
|
||||
return (
|
||||
<>
|
||||
<OutfitStateContext.Provider value={outfitState}>
|
||||
<SupportOnly>
|
||||
<WardrobeDevHacks />
|
||||
</SupportOnly>
|
||||
|
||||
<WardrobePageLayout
|
||||
previewAndControls={
|
||||
<WardrobePreviewAndControls
|
||||
isLoading={loading}
|
||||
outfitState={outfitState}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
/>
|
||||
}
|
||||
itemsAndMaybeSearchPanel={
|
||||
<ItemsAndSearchPanels
|
||||
loading={loading}
|
||||
searchQuery={searchQuery}
|
||||
onChangeSearchQuery={setSearchQuery}
|
||||
outfitState={outfitState}
|
||||
outfitSaving={outfitSaving}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
/>
|
||||
}
|
||||
searchFooter={
|
||||
<SearchFooter
|
||||
searchQuery={searchQuery}
|
||||
onChangeSearchQuery={setSearchQuery}
|
||||
outfitState={outfitState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</OutfitStateContext.Provider>
|
||||
</>
|
||||
<OutfitStateContext.Provider value={outfitState}>
|
||||
<WardrobePageLayout
|
||||
previewAndControls={
|
||||
<WardrobePreviewAndControls
|
||||
isLoading={loading}
|
||||
outfitState={outfitState}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
/>
|
||||
}
|
||||
itemsAndMaybeSearchPanel={
|
||||
<ItemsAndSearchPanels
|
||||
loading={loading}
|
||||
searchQuery={searchQuery}
|
||||
onChangeSearchQuery={setSearchQuery}
|
||||
outfitState={outfitState}
|
||||
outfitSaving={outfitSaving}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
/>
|
||||
}
|
||||
searchFooter={
|
||||
<SearchFooter
|
||||
searchQuery={searchQuery}
|
||||
onChangeSearchQuery={setSearchQuery}
|
||||
outfitState={outfitState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</OutfitStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ function PosePickerSupport({
|
|||
id
|
||||
zone {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
|
||||
# For AppearanceLayerSupportModal
|
||||
|
@ -59,7 +59,7 @@ function PosePickerSupport({
|
|||
}
|
||||
restrictedZones {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
|
||||
# For AppearanceLayerSupportModal to know the name
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import gql from "graphql-tag";
|
||||
import produce, { enableMapSet } from "immer";
|
||||
import { useQuery, useApolloClient } from "@apollo/client";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { itemAppearanceFragment } from "../components/useOutfitAppearance";
|
||||
|
||||
|
@ -156,13 +157,13 @@ function useOutfitState() {
|
|||
layers {
|
||||
zone {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
}
|
||||
restrictedZones {
|
||||
id
|
||||
label @client
|
||||
isCommonlyUsedByItems @client
|
||||
label
|
||||
isCommonlyUsedByItems
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +388,7 @@ function useParseOutfitUrl() {
|
|||
// stable object!
|
||||
const memoizedOutfitState = React.useMemo(
|
||||
() => readOutfitStateFromSearchParams(searchParams),
|
||||
[query]
|
||||
[searchParams]
|
||||
);
|
||||
|
||||
return memoizedOutfitState;
|
||||
|
|
|
@ -87,13 +87,13 @@ export function useSearchResults(
|
|||
layers {
|
||||
zone {
|
||||
id
|
||||
label @client
|
||||
label
|
||||
}
|
||||
}
|
||||
restrictedZones {
|
||||
id
|
||||
label @client
|
||||
isCommonlyUsedByItems @client
|
||||
label
|
||||
isCommonlyUsedByItems
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
230
app/javascript/wardrobe-2020/apolloClient.js
Normal file
230
app/javascript/wardrobe-2020/apolloClient.js
Normal file
|
@ -0,0 +1,230 @@
|
|||
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
|
||||
import { setContext } from "@apollo/client/link/context";
|
||||
import { createPersistedQueryLink } from "apollo-link-persisted-queries";
|
||||
|
||||
import { getAuthModeFeatureFlag } from "./components/useCurrentUser";
|
||||
|
||||
// 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 = {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 { speciesId, colorId } = args;
|
||||
console.debug(
|
||||
"[appearanceOn] seeking cached appearance",
|
||||
speciesId,
|
||||
colorId,
|
||||
readField("id")
|
||||
);
|
||||
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 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const httpLink = createHttpLink({
|
||||
uri: "https://impress-2020.openneo.net/api/graphql",
|
||||
});
|
||||
const buildAuthLink = (getAuth0) =>
|
||||
setContext(async (_, { headers = {}, sendAuth = false }) => {
|
||||
if (!sendAuth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await getAccessToken(getAuth0);
|
||||
if (token) {
|
||||
return {
|
||||
headers: {
|
||||
...headers,
|
||||
authorization: token ? `Bearer ${token}` : "",
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// This is a temporary way to pass the DTIAuthMode feature flag back to the
|
||||
// server!
|
||||
const authModeLink = setContext((_, { headers = {} }) => {
|
||||
const authMode = getAuthModeFeatureFlag();
|
||||
return {
|
||||
headers: {
|
||||
...headers,
|
||||
"DTI-Auth-Mode": authMode,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
async function getAccessToken(getAuth0) {
|
||||
// Wait for auth0 to stop loading, so we can maybe get a token!
|
||||
// We'll do this hackily by checking every 100ms until it's true.
|
||||
await new Promise((resolve) => {
|
||||
function check() {
|
||||
if (getAuth0().isLoading) {
|
||||
setTimeout(check, 100);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
check();
|
||||
});
|
||||
|
||||
const { isAuthenticated, getAccessTokenSilently } = getAuth0();
|
||||
if (isAuthenticated) {
|
||||
const token = await getAccessTokenSilently();
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
const buildLink = (getAuth0) =>
|
||||
buildAuthLink(getAuth0)
|
||||
.concat(authModeLink)
|
||||
.concat(
|
||||
createPersistedQueryLink({
|
||||
useGETForHashedQueries: true,
|
||||
})
|
||||
)
|
||||
.concat(httpLink);
|
||||
|
||||
/**
|
||||
* apolloClient is the global Apollo Client instance we use for GraphQL
|
||||
* queries. This is how we communicate with the server!
|
||||
*/
|
||||
const buildClient = ({ getAuth0, initialCacheState }) => {
|
||||
return new ApolloClient({
|
||||
link: buildLink(getAuth0),
|
||||
cache: new InMemoryCache({ typePolicies }).restore(initialCacheState),
|
||||
connectToDevTools: true,
|
||||
});
|
||||
};
|
||||
|
||||
export default buildClient;
|
|
@ -31,7 +31,11 @@ function SpeciesColorPicker({
|
|||
colorTestId = null,
|
||||
onChange,
|
||||
}) {
|
||||
const { loading: loadingMeta, error: errorMeta, data: meta } = useQuery(gql`
|
||||
const {
|
||||
loading: loadingMeta,
|
||||
error: errorMeta,
|
||||
data: meta,
|
||||
} = useQuery(gql`
|
||||
query SpeciesColorPicker {
|
||||
allSpecies {
|
||||
id
|
||||
|
@ -340,11 +344,14 @@ let cachedResponseForAllValidPetPoses = null;
|
|||
* data from GraphQL serves on the first render, without a loading state.
|
||||
*/
|
||||
export function useAllValidPetPoses() {
|
||||
const networkResponse = useFetch("/api/validPetPoses", {
|
||||
responseType: "arrayBuffer",
|
||||
// If we already have globally-cached valids, skip the request.
|
||||
skip: cachedResponseForAllValidPetPoses != null,
|
||||
});
|
||||
const networkResponse = useFetch(
|
||||
"https://impress-2020.openneo.net/api/validPetPoses",
|
||||
{
|
||||
responseType: "arrayBuffer",
|
||||
// If we already have globally-cached valids, skip the request.
|
||||
skip: cachedResponseForAllValidPetPoses != null,
|
||||
}
|
||||
);
|
||||
|
||||
// Use the globally-cached response if we have one, or await the network
|
||||
// response if not.
|
||||
|
|
|
@ -94,8 +94,6 @@ function getVisibleLayers(petAppearance, itemAppearances) {
|
|||
return visibleLayers;
|
||||
}
|
||||
|
||||
// TODO: The web client could save bandwidth by applying @client to the `depth`
|
||||
// field, because it already has zone depths cached.
|
||||
export const itemAppearanceFragmentForGetVisibleLayers = gql`
|
||||
fragment ItemAppearanceForGetVisibleLayers on ItemAppearance {
|
||||
id
|
||||
|
@ -113,8 +111,6 @@ export const itemAppearanceFragmentForGetVisibleLayers = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
// TODO: The web client could save bandwidth by applying @client to the `depth`
|
||||
// field, because it already has zone depths cached.
|
||||
export const petAppearanceFragmentForGetVisibleLayers = gql`
|
||||
fragment PetAppearanceForGetVisibleLayers on PetAppearance {
|
||||
id
|
||||
|
|
|
@ -136,8 +136,8 @@ export const appearanceLayerFragment = gql`
|
|||
knownGlitches # For HTML5 & Known Glitches UI
|
||||
zone {
|
||||
id
|
||||
depth @client
|
||||
label @client
|
||||
depth
|
||||
label
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -149,7 +149,7 @@ export const appearanceLayerFragmentForSupport = gql`
|
|||
swfUrl # HACK: This is for Support tools, but other views don't need it
|
||||
zone {
|
||||
id
|
||||
label @client # HACK: This is for Support tools, but other views don't need it
|
||||
label # HACK: This is for Support tools, but other views don't need it
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import AppProvider from "./AppProvider";
|
||||
import WardrobePage from "./WardrobePage";
|
||||
|
||||
export { WardrobePage };
|
||||
export { AppProvider, WardrobePage };
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
"@emotion/styled": "^11.0.0",
|
||||
"@loadable/component": "^5.12.0",
|
||||
"@sentry/react": "^5.30.0",
|
||||
"@sentry/tracing": "^5.30.0",
|
||||
"apollo-link-persisted-queries": "^0.2.2",
|
||||
"easeljs": "^1.0.2",
|
||||
"esbuild": "^0.19.0",
|
||||
"framer-motion": "^4.1.11",
|
||||
|
@ -25,6 +27,7 @@
|
|||
"tweenjs": "^1.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets --loader:.js=jsx --loader:.png=file --loader:.svg=file --loader:.min.js=text"
|
||||
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets --loader:.js=jsx --loader:.png=file --loader:.svg=file --loader:.min.js=text",
|
||||
"build:production": "yarn build --minify"
|
||||
}
|
||||
}
|
||||
|
|
88
yarn.lock
88
yarn.lock
|
@ -985,6 +985,17 @@
|
|||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@^5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f"
|
||||
integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/minimal" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
|
||||
|
@ -1046,6 +1057,13 @@
|
|||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@wry/equality@^0.1.2":
|
||||
version "0.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790"
|
||||
integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@wry/equality@^0.5.6":
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e"
|
||||
|
@ -1072,6 +1090,34 @@ ansi-styles@^3.2.1:
|
|||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
apollo-link-persisted-queries@^0.2.2:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/apollo-link-persisted-queries/-/apollo-link-persisted-queries-0.2.5.tgz#76deabf68dac218d83f2fa23eebc3b25772fd914"
|
||||
integrity sha512-PYWsMFcRGT9NZ6e6EK5rlhNDtcK6FR76JDy1RIngEfR6RdM5a2Z0IhZdn9RTTNB3V/+s7iWviQmoCfQrTVXu0A==
|
||||
dependencies:
|
||||
apollo-link "^1.2.1"
|
||||
hash.js "^1.1.7"
|
||||
|
||||
apollo-link@^1.2.1:
|
||||
version "1.2.14"
|
||||
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9"
|
||||
integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==
|
||||
dependencies:
|
||||
apollo-utilities "^1.3.0"
|
||||
ts-invariant "^0.4.0"
|
||||
tslib "^1.9.3"
|
||||
zen-observable-ts "^0.8.21"
|
||||
|
||||
apollo-utilities@^1.3.0:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf"
|
||||
integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==
|
||||
dependencies:
|
||||
"@wry/equality" "^0.1.2"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
ts-invariant "^0.4.0"
|
||||
tslib "^1.10.0"
|
||||
|
||||
aria-hidden@^1.1.1:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
|
||||
|
@ -1244,6 +1290,11 @@ escape-string-regexp@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
fast-text-encoding@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867"
|
||||
|
@ -1315,6 +1366,14 @@ has@^1.0.3:
|
|||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hash.js@^1.1.7:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hey-listen@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
|
||||
|
@ -1340,6 +1399,11 @@ import-fresh@^3.2.1:
|
|||
parent-module "^1.0.0"
|
||||
resolve-from "^4.0.0"
|
||||
|
||||
inherits@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
|
@ -1398,6 +1462,11 @@ lru-cache@^6.0.0:
|
|||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||
|
||||
object-assign@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
|
||||
|
@ -1689,7 +1758,14 @@ ts-invariant@^0.10.3:
|
|||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
tslib@^1.0.0, tslib@^1.9.3:
|
||||
ts-invariant@^0.4.0:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86"
|
||||
integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
tslib@^1.0.0, tslib@^1.10.0, tslib@^1.9.3:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
@ -1741,6 +1817,14 @@ yaml@^1.10.0:
|
|||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
zen-observable-ts@^0.8.21:
|
||||
version "0.8.21"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d"
|
||||
integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
zen-observable "^0.8.0"
|
||||
|
||||
zen-observable-ts@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
|
||||
|
@ -1748,7 +1832,7 @@ zen-observable-ts@^1.2.5:
|
|||
dependencies:
|
||||
zen-observable "0.8.15"
|
||||
|
||||
zen-observable@0.8.15:
|
||||
zen-observable@0.8.15, zen-observable@^0.8.0:
|
||||
version "0.8.15"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
|
||||
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
|
||||
|
|
Loading…
Reference in a new issue