Skip to content

Commit

Permalink
feat: improve data loading ux/ui
Browse files Browse the repository at this point in the history
  • Loading branch information
jog1t committed Sep 28, 2024
1 parent b9158a9 commit f356731
Show file tree
Hide file tree
Showing 69 changed files with 1,575 additions and 515 deletions.
17 changes: 9 additions & 8 deletions apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
"@rivet-gg/components": "workspace:*",
"@rivet-gg/icons": "workspace:*",
"@sentry/react": "^8.26.0",
"@tanstack/query-broadcast-client-experimental": "^5.51.24",
"@tanstack/query-sync-storage-persister": "^5.51.24",
"@tanstack/react-query": "^5.51.24",
"@tanstack/react-query-devtools": "^5.51.24",
"@tanstack/react-query-persist-client": "^5.51.24",
"@tanstack/react-router": "^1.50.1",
"@tanstack/router-devtools": "^1.33.4",
"@tanstack/query-broadcast-client-experimental": "^5.56.2",
"@tanstack/query-sync-storage-persister": "^5.56.2",
"@tanstack/react-query": "^5.56.2",
"@tanstack/react-query-devtools": "^5.58.0",
"@tanstack/react-query-persist-client": "^5.56.2",
"@tanstack/react-router": "^1.58.12",
"@tanstack/router-devtools": "^1.58.12",
"@tanstack/router-zod-adapter": "^1.58.12",
"@types/bcryptjs": "^2.4.6",
"bcryptjs": "^2.4.3",
"file-saver": "^2.0.5",
Expand All @@ -47,7 +48,7 @@
},
"devDependencies": {
"@sentry/vite-plugin": "^2.22.2",
"@tanstack/router-vite-plugin": "^1.48.6",
"@tanstack/router-vite-plugin": "^1.58.12",
"@types/file-saver": "^2",
"@types/mime": "^4.0.0",
"@types/node": "^20.11.30",
Expand Down
11 changes: 9 additions & 2 deletions apps/hub/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import {
TooltipProvider,
getConfig,
} from "@rivet-gg/components";
import { PageLayout } from "@rivet-gg/components/layout";
import * as Sentry from "@sentry/react";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { Suspense } from "react";
import { ThirdPartyProviders } from "./components/third-party-providers";
import { AuthProvider, useAuth } from "./domains/auth/contexts/auth";
import { routeMasks } from "./lib/route-masks";
import { queryClient, queryClientPersister } from "./queries/global";
import { routeTree } from "./routeTree.gen";
Expand All @@ -36,13 +38,16 @@ export const router = createRouter({
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
defaultPendingMs: 0,
defaultPendingComponent: PageLayout.Root.Skeleton,
defaultOnCatch: (error) => {
Sentry.captureException(error);
},
});

function InnerApp() {
return <RouterProvider router={router} />;
const auth = useAuth();
return <RouterProvider router={router} context={{ auth }} />;
}

export function App() {
Expand All @@ -55,7 +60,9 @@ export function App() {
<ThirdPartyProviders>
<Suspense fallback={<FullscreenLoading />}>
<TooltipProvider>
<InnerApp />
<AuthProvider>
<InnerApp />
</AuthProvider>
</TooltipProvider>
</Suspense>

Expand Down
1 change: 1 addition & 0 deletions apps/hub/src/components/breadcrumbs/game-breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function GameBreadcrumb({ gameId }: GameBreadcrumbProps) {
const navigate = useNavigate();
const handleGameChange = (gameId: string) => {
navigate({
to: "/games/$gameId",
params: { gameId },
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export function RivetCommandGroup() {
Docs
</CommandItem>

<CommandItem onSelect={() => navigate({ search: { modal: "feedback" } })}>
<CommandItem
onSelect={() => navigate({ to: ".", search: { modal: "feedback" } })}
>
<Icon icon={faComment} />
Feedback
</CommandItem>
Expand Down
19 changes: 14 additions & 5 deletions apps/hub/src/components/error-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,31 @@ import {
} from "@rivet-gg/components";
import { Icon, faBomb } from "@rivet-gg/icons";
import * as Sentry from "@sentry/react";
import { useQueryClient } from "@tanstack/react-query";
import type { ErrorComponentProps } from "@tanstack/react-router";
import {
useQueryClient,
useQueryErrorResetBoundary,
} from "@tanstack/react-query";
import { type ErrorComponentProps, useRouter } from "@tanstack/react-router";
import { useEffect } from "react";
import { NotFoundComponent } from "./not-found-component";

export const ErrorComponent = ({
error,
reset,
}: Partial<ErrorComponentProps>) => {
const router = useRouter();
const queryClient = useQueryClient();
const queryErrorResetBoundary = useQueryErrorResetBoundary();

useEffect(() => {
console.dir(error);
if (error) {
Sentry.captureException(error);
}
}, [error]);

// Reset the query error boundary
queryErrorResetBoundary.reset();
}, [error, queryErrorResetBoundary]);

if (isRivetError(error)) {
if (error.statusCode === 404) {
Expand Down Expand Up @@ -54,8 +63,8 @@ export const ErrorComponent = ({
</CardContent>
<CardFooter>
<Button
onClick={async () => {
await queryClient.invalidateQueries({ refetchType: "all" });
onClick={() => {
router.invalidate();
reset?.();
}}
>
Expand Down
11 changes: 8 additions & 3 deletions apps/hub/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SheetTrigger,
} from "@rivet-gg/components";
import { Icon, faBars } from "@rivet-gg/icons";
import { Link } from "@tanstack/react-router";
import { Link, useLocation } from "@tanstack/react-router";
import { Breadcrumbs } from "../breadcrumbs/breadcrumbs";
import { MobileBreadcrumbs } from "../breadcrumbs/mobile-breadcrumbs";
import { Changelog } from "./changelog";
Expand Down Expand Up @@ -35,6 +35,7 @@ const UserProfileButton = () => {
};

export function Header() {
const location = useLocation();
return (
<header className="bg-background/60 sticky top-0 z-10 flex items-center gap-4 border-b py-2 backdrop-blur">
<HeaderRouteLoader />
Expand Down Expand Up @@ -71,7 +72,9 @@ export function Header() {
</div>
<Flex direction="col" justify="end" gap="6">
<NavItem asChild>
<Link search={{ modal: "feedback" }}>Feedback</Link>
<Link to={"."} search={{ modal: "feedback" }}>
Feedback
</Link>
</NavItem>
<NavItem asChild>
<a
Expand Down Expand Up @@ -121,7 +124,9 @@ export function Header() {
asChild
className="hidden md:inline-block border px-4 py-2 rounded-md"
>
<Link search={{ modal: "feedback" }}>Feedback</Link>
<Link to="." search={{ modal: "feedback" }}>
Feedback
</Link>
</NavItem>
<Changelog />
<NavItem asChild className="hidden md:inline-block">
Expand Down
4 changes: 3 additions & 1 deletion apps/hub/src/domains/auth/components/feedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { Link } from "@tanstack/react-router";
export function Feedback() {
return (
<LinkCmp asChild>
<Link search={{ modal: "feedback" }}>Missing something? Spot a bug?</Link>
<Link to="." search={{ modal: "feedback" }}>
Missing something? Spot a bug?
</Link>
</LinkCmp>
);
}
3 changes: 3 additions & 0 deletions apps/hub/src/domains/auth/contexts/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IdentifyUser } from "@/components/third-party-providers";
import {
identityTokenQueryOptions,
selfProfileQueryOptions,
useLogoutMutation,
} from "@/domains/user/queries";
Expand All @@ -17,6 +18,8 @@ export interface AuthContext {
const AuthContext = createContext<AuthContext | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
useSuspenseQuery(identityTokenQueryOptions());

const {
data: profile,
isSuccess,
Expand Down
2 changes: 2 additions & 0 deletions apps/hub/src/domains/auth/forms/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const Captcha = () => {
<FormControl>
{data.turnstile?.siteKey ? (
<Turnstile
refreshExpired="auto"
retry="auto"
sitekey={data.turnstile.siteKey}
onVerify={(token) => {
field.onChange({ target: { value: token } });
Expand Down
7 changes: 6 additions & 1 deletion apps/hub/src/domains/auth/views/login-view/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,13 @@ export const useOtpFormSubmitHandler = ({
);
},
});
await queryClient.invalidateQueries({ refetchType: "all" });
await queryClient.fetchQuery(selfProfileQueryOptions());
await refreshToken();
return onSuccess?.();
}

await queryClient.invalidateQueries({ refetchType: "all" });
return onSuccess?.();
} catch (error) {
Sentry.captureException(error);
Expand All @@ -101,11 +104,13 @@ export const useOtpFormSubmitHandler = ({
}
},
[
refreshToken,
mutateAsync,
onSuccess,
verificationId,
refreshToken,
queryClient.removeQueries,
queryClient.invalidateQueries,
queryClient.fetchQuery,
],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const GameBackendEnvironmentDatabaseLink = forwardRef<
return;
}
if (!data) {
return navigate({ search: { modal: "database" } });
return navigate({ to: ".", search: { modal: "database" } });
}
if (typeof data === "string") {
window.open(data, "_blank", "noreferrer,noopener");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function GameBackendEventsListPanel({
asChild
>
<Link
to="."
search={{ eventId: event.eventTimestamp }}
className="truncate min-w-0"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function GameMatchmakerListLobbyButton({
return (
<Button key={lobbyId} variant={isActive ? "secondary" : "outline"} asChild>
<Link
to="."
search={(old) => ({ ...old, lobbyId: lobbyId })}
className="truncate min-w-0"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function GameServerRow({
asChild
>
<Link
to="."
search={{ serverId: server.id }}
className="min-w-0 flex flex-wrap gap-2"
>
Expand Down
1 change: 0 additions & 1 deletion apps/hub/src/domains/game/helpers/try-create-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export async function tryCreateBackend({
return;
}
}
throw error;
}
return;
}
38 changes: 36 additions & 2 deletions apps/hub/src/domains/game/layouts/backend-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import {
Flex,
SidebarNavigation,
SidebarPageContent,
Skeleton,
cn,
} from "@rivet-gg/components";
import { Icon, faSpinnerThird } from "@rivet-gg/icons";
import { Link } from "@tanstack/react-router";
import type { ComponentProps, ReactNode } from "react";
import type { ComponentProps, PropsWithChildren, ReactNode } from "react";
import { GameBackendEnvironmentDatabaseLink } from "../components/game-backend/game-backend-environment-database-link";

const LINKS = [
Expand Down Expand Up @@ -88,4 +89,37 @@ function BackendPage({ namespaceId, gameId, children }: BackendPageProps) {
);
}

export { BackendPage as Root };
BackendPage.Skeleton = function BackendPageSkeleton() {
return (
<SidebarPageContent
sidebar={
<SidebarNavigation>
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
</SidebarNavigation>
}
>
<Flex gap="4" direction="col" className="w-full min-h-0 h-full md:h-auto">
<Skeleton className="w-full h-56" />
<Skeleton className="w-full h-56" />
</Flex>
</SidebarPageContent>
);
};

function Content({ children }: PropsWithChildren) {
return children;
}

Content.Skeleton = function ContentSkeleton() {
return (
<>
<Skeleton className="w-full h-56" />
<Skeleton className="w-full h-56" />
</>
);
};

export { BackendPage as Root, Content };
24 changes: 22 additions & 2 deletions apps/hub/src/domains/game/layouts/game-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Page } from "@rivet-gg/components";
import { Page, Skeleton } from "@rivet-gg/components";
import { useMatches } from "@tanstack/react-router";
import type { PropsWithChildren, ReactNode } from "react";

Expand All @@ -15,6 +15,26 @@ function GamePage({ children }: GamePageProps) {
);
}

GamePage.Skeleton = Page.Skeleton;

function Content({ children }: PropsWithChildren) {
return <>{children}</>;
}

Content.Skeleton = function ContentSkeleton() {
return (
<>
<Skeleton className="my-4 h-12 w-1/3" />
<div className="flex flex-row gap-4">
<Skeleton className="h-64 w-2/3" />
<Skeleton className="h-64 w-1/3" />
</div>
<Skeleton className="h-64 w-full" />
<Skeleton className="h-64 w-full" />
</>
);
};

function EmptyGamePage({ children }: PropsWithChildren) {
const matches = useMatches();
return (
Expand All @@ -24,4 +44,4 @@ function EmptyGamePage({ children }: PropsWithChildren) {
);
}

export { GamePage as Root, EmptyGamePage as EmptyRoot };
export { GamePage as Root, EmptyGamePage as EmptyRoot, Content };
Loading

0 comments on commit f356731

Please sign in to comment.