diff --git a/apps/gnocchi/web/src/components/recipes/cook/CookingActionBar.tsx b/apps/gnocchi/web/src/components/recipes/cook/CookingActionBar.tsx
index f86be160..d27d024d 100644
--- a/apps/gnocchi/web/src/components/recipes/cook/CookingActionBar.tsx
+++ b/apps/gnocchi/web/src/components/recipes/cook/CookingActionBar.tsx
@@ -4,6 +4,7 @@ import {
PromoteSubscriptionButton,
useCanSync,
useIsLoggedIn,
+ useQuery,
} from '@biscuits/client';
import { useActiveCookingSession } from '@/components/recipes/hooks.js';
import {
@@ -26,7 +27,7 @@ import {
} from '@a-type/ui/components/dialog';
import { ErrorBoundary } from '@a-type/ui/components/errorBoundary';
import { Cross2Icon } from '@radix-ui/react-icons';
-import { ManagePlanButton, graphql, useSuspenseQuery } from '@biscuits/client';
+import { ManagePlanButton, graphql } from '@biscuits/client';
import {
Popover,
PopoverContent,
@@ -89,7 +90,7 @@ const planMembersQuery = graphql(`
function AddChefsAction() {
const isLoggedIn = useIsLoggedIn();
- const { data: members } = useSuspenseQuery(planMembersQuery, {
+ const { data: members } = useQuery(planMembersQuery, {
skip: !isLoggedIn,
});
const isSubscribed = useCanSync();
diff --git a/apps/trip-tick/web/src/components/weather/WeatherForecast.tsx b/apps/trip-tick/web/src/components/weather/WeatherForecast.tsx
index 4b25ef57..a8cecd09 100644
--- a/apps/trip-tick/web/src/components/weather/WeatherForecast.tsx
+++ b/apps/trip-tick/web/src/components/weather/WeatherForecast.tsx
@@ -4,6 +4,7 @@ import { graphql, useCanSync, useSuspenseQuery } from '@biscuits/client';
import { Trip } from '@trip-tick.biscuits/verdant';
import { Suspense } from 'react';
import { Chip } from '@a-type/ui/components/chip';
+import { ErrorBoundary } from '@a-type/ui/components/errorBoundary';
export interface WeatherForecastProps {
className?: string;
@@ -33,14 +34,16 @@ export function WeatherForecast({ className, trip }: WeatherForecastProps) {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/packages/client/src/components/Changelog.tsx b/packages/client/src/components/Changelog.tsx
index 2bcd2bc3..e755b3e3 100644
--- a/packages/client/src/components/Changelog.tsx
+++ b/packages/client/src/components/Changelog.tsx
@@ -10,7 +10,7 @@ import {
DialogTrigger,
} from '@a-type/ui/components/dialog';
import { useState } from 'react';
-import { graphql, useSuspenseQuery } from '../index.js';
+import { graphql, useQuery } from '../index.js';
import { useAppId } from './Context.js';
export interface ChangelogDisplayProps {
@@ -46,7 +46,7 @@ export function ChangelogDisplay({
);
const [capturedSeen] = useState(seen);
const appId = useAppId();
- const res = useSuspenseQuery(changelogQuery, { variables: { appId } });
+ const res = useQuery(changelogQuery, { variables: { appId } });
const data = res.data?.changelog || [];
const lastSeenIndex = data.findIndex((x) => x.id === capturedSeen);
const hasUnseen = !!data.length && lastSeenIndex !== 0;
diff --git a/packages/client/src/components/SubscriptionPromotion.tsx b/packages/client/src/components/SubscriptionPromotion.tsx
index eeb883da..e05ed77e 100644
--- a/packages/client/src/components/SubscriptionPromotion.tsx
+++ b/packages/client/src/components/SubscriptionPromotion.tsx
@@ -11,7 +11,7 @@ import {
import { Button } from '@a-type/ui/components/button';
import { ReactNode } from 'react';
import { Icon } from '@a-type/ui/components/icon';
-import { graphql, useSuspenseQuery } from '../index.js';
+import { graphql, useQuery } from '../index.js';
const subscriptionPromotionState = proxy({
status: 'closed' as 'closed' | 'open',
@@ -39,7 +39,7 @@ export function SubscriptionPromotion({
children,
}: SubscriptionPromotionProps) {
const { status } = useSnapshot(subscriptionPromotionState);
- const { data } = useSuspenseQuery(promotionProductQuery);
+ const { data } = useQuery(promotionProductQuery);
const price = data?.productInfo.price;
const currency = data?.productInfo.currency;
diff --git a/packages/client/src/components/UserMenu.tsx b/packages/client/src/components/UserMenu.tsx
index dfe688a9..531314d9 100644
--- a/packages/client/src/components/UserMenu.tsx
+++ b/packages/client/src/components/UserMenu.tsx
@@ -13,10 +13,12 @@ import {
graphql,
useAppId,
useIsLoggedIn,
+ useIsOffline,
useSuspenseQuery,
} from '../index.js';
import { Icon } from '@a-type/ui/components/icon';
import { ReactNode } from 'react';
+import { ErrorBoundary } from '@a-type/ui/components/errorBoundary';
export interface UserMenuProps {
className?: string;
@@ -30,6 +32,7 @@ export function UserMenu({
children,
}: UserMenuProps) {
const isLoggedIn = useIsLoggedIn();
+ const isOffline = useIsOffline();
const appId = useAppId();
const openPwaHackCatalog = () => {
@@ -44,10 +47,19 @@ export function UserMenu({
+ {isOffline && (
+
+ Offline - some features may be unavailable
+
+ )}
{getIsPWAInstalled() ? (
More apps
@@ -97,14 +109,16 @@ export function UserMenu({
)}
-
-
- Log out
-
-
-
-
-
+ {!!isLoggedIn && (
+
+
+ Log out
+
+
+
+
+
+ )}
);
diff --git a/packages/client/src/graphql.ts b/packages/client/src/graphql.ts
index 76b79028..4a0a53e4 100644
--- a/packages/client/src/graphql.ts
+++ b/packages/client/src/graphql.ts
@@ -161,6 +161,7 @@ const meQuery = graphql(`
query CommonMe {
me {
id
+ name
plan {
id
canSync
@@ -172,7 +173,7 @@ const meQuery = graphql(`
`);
export function useMe() {
- return useSuspenseQuery(meQuery, {
+ return useQuery(meQuery, {
fetchPolicy: 'cache-first',
});
}
@@ -188,8 +189,6 @@ export function useCanSync() {
}
export function useIsOffline() {
- const { error } = useQuery(meQuery, {
- fetchPolicy: 'cache-first',
- });
+ const { error } = useMe();
return !!error?.networkError;
}
diff --git a/web/src/components/auth/UserMenu.tsx b/web/src/components/auth/UserMenu.tsx
index 7f7af397..f328213f 100644
--- a/web/src/components/auth/UserMenu.tsx
+++ b/web/src/components/auth/UserMenu.tsx
@@ -1,24 +1,14 @@
import { Button } from '@a-type/ui/components/button';
+import { useMe } from '@biscuits/client';
import { Link } from '@verdant-web/react-router';
import classNames from 'classnames';
-import { graphql } from '@/graphql.js';
-import { useSuspenseQuery } from '@biscuits/client';
export interface UserMenuProps {
className?: string;
}
-const userMenuMe = graphql(`
- query UserMenuMe {
- me {
- id
- name
- }
- }
-`);
-
export function UserMenu({ className }: UserMenuProps) {
- const { data } = useSuspenseQuery(userMenuMe);
+ const { data } = useMe();
if (!data?.me) {
return (
diff --git a/web/src/components/subscription/ManageSubscription.tsx b/web/src/components/subscription/ManageSubscription.tsx
index 56c9e94e..76849bd6 100644
--- a/web/src/components/subscription/ManageSubscription.tsx
+++ b/web/src/components/subscription/ManageSubscription.tsx
@@ -6,7 +6,7 @@ import { toast } from 'react-hot-toast';
import { PlanInfo, planProductInfo } from './PlanInfo.js';
import { CancelPlanButton } from './CancelPlanButton.js';
import classNames from 'classnames';
-import { useLazyQuery, useSuspenseQuery } from '@biscuits/client';
+import { useLazyQuery } from '@biscuits/client';
import { Icon } from '@a-type/ui/components/icon';
import { H2 } from '@a-type/ui/components/typography';
import { CONFIG } from '@biscuits/client';
diff --git a/web/src/components/subscription/SubscriptionSelect.tsx b/web/src/components/subscription/SubscriptionSelect.tsx
index bbeb64da..749f647a 100644
--- a/web/src/components/subscription/SubscriptionSelect.tsx
+++ b/web/src/components/subscription/SubscriptionSelect.tsx
@@ -1,6 +1,6 @@
import { graphql } from '@/graphql.js';
import { H2 } from '@a-type/ui/components/typography';
-import { useMutation, useSuspenseQuery } from '@biscuits/client';
+import { useMutation, useQuery } from '@biscuits/client';
import {
CardGrid,
CardMain,
@@ -84,7 +84,7 @@ function SubscriptionChoiceButton({
onClick: () => void;
lookupKey: 'for_two' | 'family_style';
}) {
- const { data } = useSuspenseQuery(subscriptionPlanInfo, {
+ const { data } = useQuery(subscriptionPlanInfo, {
variables: { lookupKey },
});
return (
diff --git a/web/src/components/subscription/SubscriptionSetup.tsx b/web/src/components/subscription/SubscriptionSetup.tsx
index cf2d0191..ffdf6135 100644
--- a/web/src/components/subscription/SubscriptionSetup.tsx
+++ b/web/src/components/subscription/SubscriptionSetup.tsx
@@ -1,6 +1,6 @@
import { graphql } from '@/graphql.js';
import { Spinner } from '@a-type/ui/components/spinner';
-import { useSuspenseQuery } from '@biscuits/client';
+import { useQuery } from '@biscuits/client';
import {
ManageSubscription,
manageSubscriptionInfo,
@@ -41,7 +41,7 @@ export function SubscriptionSetup({}: SubscriptionSetupProps) {
const [params, setParams] = useSearchParams();
const didJustCheckout = params.get('paymentComplete');
- const { data, refetch } = useSuspenseQuery(PlanSubscriptionInfo);
+ const { data, refetch } = useQuery(PlanSubscriptionInfo);
const subscriptionStatus = data?.plan?.subscriptionStatus;
const isTerminalStatus =
diff --git a/web/src/pages/ErrorPage.tsx b/web/src/pages/ErrorPage.tsx
new file mode 100644
index 00000000..71d77fbe
--- /dev/null
+++ b/web/src/pages/ErrorPage.tsx
@@ -0,0 +1,17 @@
+import { PageContent, PageRoot } from '@a-type/ui/components/layouts';
+import { H1, P } from '@a-type/ui/components/typography';
+
+export interface ErrorPageProps {}
+
+export function ErrorPage({}: ErrorPageProps) {
+ return (
+
+
+ Something went wrong
+ Sorry, we couldn't load this page. Please try again later.
+
+
+ );
+}
+
+export default ErrorPage;
diff --git a/web/src/pages/JoinPage.tsx b/web/src/pages/JoinPage.tsx
index 334a8e8b..53958c7a 100644
--- a/web/src/pages/JoinPage.tsx
+++ b/web/src/pages/JoinPage.tsx
@@ -7,7 +7,7 @@ import {
} from '@a-type/ui/components/layouts';
import { Button } from '@a-type/ui/components/button';
import { Icon } from '@a-type/ui/components/icon';
-import { graphql, useLocalStorage, useSuspenseQuery } from '@biscuits/client';
+import { graphql, useLocalStorage, useQuery } from '@biscuits/client';
import { Link, useNavigate } from '@verdant-web/react-router';
import { useEffect } from 'react';
import classNames from 'classnames';
@@ -79,7 +79,7 @@ export function JoinPage({}: JoinPageProps) {
}
const StartingPrice = () => {
- const { data } = useSuspenseQuery(startingPriceQuery);
+ const { data } = useQuery(startingPriceQuery);
return (
Your Plan
-
-
- {!!data?.plan && (
-
-
Members
-
-
- )}
- {!!data?.plan && (
-
-
App data
-
-
- )}
-
+ Something went wrong. Couldn't load this content.
+ }
+ >
+
+
+ {!!data?.plan && (
+
+
Members
+
+
+ )}
+ {!!data?.plan && (
+
+
App data
+
+
+ )}
+
+
);
diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx
index 4c7c49c2..85afd7d0 100644
--- a/web/src/pages/index.tsx
+++ b/web/src/pages/index.tsx
@@ -2,7 +2,9 @@ import { TopLoader } from '@/components/nav/TopLoader.jsx';
import { Outlet, Router, makeRoutes } from '@verdant-web/react-router';
import { Suspense, lazy } from 'react';
import RefreshSessionPage from './RefreshSessionPage.jsx';
+import { ErrorBoundary } from '@a-type/ui/components/errorBoundary';
const HomePage = lazy(() => import('./HomePage.js'));
+const ErrorPage = lazy(() => import('./ErrorPage.jsx'));
const routes = makeRoutes([
{
@@ -43,10 +45,12 @@ const routes = makeRoutes([
export function Pages() {
return (
-
-
-
-
+ }>
+
+
+
+
+
);
}