import React from "react";
import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";
import {
ChakraProvider,
Box,
css as resolveCSS,
extendTheme,
useColorMode,
useTheme,
} from "@chakra-ui/react";
import { mode } from "@chakra-ui/theme-tools";
import { ApolloProvider } from "@apollo/client";
import { BrowserRouter } from "react-router-dom";
import { css, Global } from "@emotion/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import produce from "immer";
import apolloClient from "./apolloClient";
const reactQueryClient = new QueryClient();
let theme = extendTheme({
styles: {
global: (props) => ({
body: {
background: mode("gray.50", "gray.800")(props),
color: mode("green.800", "green.50")(props),
transition: "all 0.25s",
},
}),
},
});
// Capture the global styles function from our theme, but remove it from the
// theme's `styles.global` key. We don't actually want to apply these styles
// globally, because they conflict with the app's other styles when embedded in
// e.g. the item page! We'll apply them more carefully in `ScopedCSSReset`.
//
// NOTE: We get this from `theme`, instead of just declaring the function
// separately, because `extendTheme` returns a wrapped version of our function
// that also returns some of Chakra's own default global styles.
const globalStyles = theme.styles.global;
theme.styles.global = {};
export default function AppProvider({ children }) {
React.useEffect(() => setupLogging(), []);
return (
{children}
);
}
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,
});
}
/**
* ScopedCSSReset applies a copy of Chakra UI's CSS reset, but only to its
* children (or, well, any element with the chakra-css-reset class). It also
* applies to .chakra-portal elements mounted by components.
*
* We also apply some base styles, like the default text color.
*
* NOTE: We use the `:where` CSS selector, instead of the .chakra-css-reset
* selector directly, to avoid specificity conflicts. e.g. the selector
* `.chakra-css-reset h1` is considered MORE specific than `.my-h1`, whereas
* the selector `:where(.chakra-css-reset) h1` is lower specificity.
*/
function ScopedCSSReset({ children }) {
// Get the current theme and color mode.
//
// NOTE: The theme object returned by `useTheme` has some extensions that are
// necessary for the code below, but aren't present in the theme config
// returned by `extendTheme`! That's why we use this here instead of `theme`.
const liveTheme = useTheme();
const colorMode = useColorMode();
// Resolve the theme's global styles into CSS objects for Emotion.
const globalStylesCSS = resolveCSS(
globalStyles({ theme: liveTheme, colorMode }),
)(liveTheme);
// Prepend our special scope selector to the global styles.
const scopedGlobalStylesCSS = {};
for (let [selector, rules] of Object.entries(globalStylesCSS)) {
// The `body` selector is where typography etc rules go, but `body` isn't
// actually *inside* our scoped element! Instead, ignore the `body` part,
// and just apply it to the scoping element itself.
if (selector.trim() === "body") {
selector = "";
}
const scopedSelector =
":where(.chakra-css-reset, .chakra-portal) " + selector;
scopedGlobalStylesCSS[scopedSelector] = rules;
}
return (
<>
{children}
>
);
}