Skip to content

Commit

Permalink
feat: new onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
jog1t committed Dec 16, 2024
1 parent d41a10e commit 4ee877a
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 75 deletions.
10 changes: 10 additions & 0 deletions apps/hub/src/components/command-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CommandLoading,
cn,
} from "@rivet-gg/components";
import { useIsFetching } from "@tanstack/react-query";
import { useMatchRoute } from "@tanstack/react-router";
import {
type KeyboardEventHandler,
Expand All @@ -25,6 +26,7 @@ import { EnvironmentCommandPanelPage } from "./command-panel/command-panel-page/
import { GroupCommandPanelPage } from "./command-panel/command-panel-page/group-command-panel-page";
import { IndexCommandPanelPage } from "./command-panel/command-panel-page/index-command-panel-page";
import { ProjectCommandPanelPage } from "./command-panel/command-panel-page/project-command-panel-page";
import { ShimmerLine } from "./shimmer-line";

export function CommandPanel() {
const [isOpen, setOpen] = useState(false);
Expand Down Expand Up @@ -125,6 +127,11 @@ export function CommandPanel() {
[pages.length, search],
);

const isLoading =
useIsFetching({
predicate: (query) => !query.queryKey.includes("watch"),
}) > 0;

return (
<>
<Button
Expand All @@ -143,6 +150,7 @@ export function CommandPanel() {
<CommandDialog
commandProps={{
onKeyDown: handleKeyDown,
shouldFilter: !isLoading,
}}
open={isOpen}
onOpenChange={setOpen}
Expand All @@ -154,11 +162,13 @@ export function CommandPanel() {
placeholder="Type a command or search..."
/>
<CommandPanelNavigationProvider
isLoading={isLoading}
onClose={handleClose}
onChangePage={handlePageChange}
>
<CommandList>
<Suspense fallback={<CommandLoading>Hang on…</CommandLoading>}>
{isLoading ? <ShimmerLine className="-top-[1px]" /> : null}
<CommandEmpty>No results found.</CommandEmpty>
{!page ? <IndexCommandPanelPage /> : null}
{page?.key === "group" ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ export function CommandPanelNavigationProvider({
children,
onClose,
onChangePage,
isLoading,
}: {
children: ReactNode;
onClose: () => void;
onChangePage: (page: CommandPanelPage) => void;
isLoading: boolean;
}) {
const routerNavigate = useNavigate();

Expand All @@ -73,9 +75,16 @@ export function CommandPanelNavigationProvider({
[onClose, routerNavigate],
);

const handleChangePage = (page: CommandPanelPage) => {
if (isLoading) {
return;
}
onChangePage(page);
};

return (
<CommandPanelNavigationContext.Provider
value={{ changePage: onChangePage, close: onClose, navigate }}
value={{ changePage: handleChangePage, close: onClose, navigate }}
>
{children}
</CommandPanelNavigationContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function EnvironmentCommandPanelPage({
},
] = useSuspenseQueries({
queries: [
projectQueryOptions(projectNameId),
projectQueryOptions(projectId),
projectMetadataQueryOptions({ projectId, environmentId }),
],
});
Expand Down
2 changes: 2 additions & 0 deletions apps/hub/src/components/error-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export const ErrorComponent = ({
<Button
onClick={() => {
router.invalidate();
queryClient.resetQueries();
queryClient.invalidateQueries();
reset?.();
}}
>
Expand Down
51 changes: 4 additions & 47 deletions apps/hub/src/components/get-started.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@ import {
faChevronRight,
faCircleNodes,
faCode,
faCog,
faDiagramNext,
faGamepadAlt,
faHandWave,
faSparkles,
faToolbox,
faUpRightAndDownLeftFromCenter,
faWifiSlash,
} from "@rivet-gg/icons";
Expand Down Expand Up @@ -70,7 +67,7 @@ export function GetStarted() {
<Button
onClick={() => {
document
.getElementById("getting-started")
.getElementById("examples")
?.scrollIntoView({ behavior: "smooth" });
}}
endIcon={<Icon icon={faChevronDoubleDown} />}
Expand Down Expand Up @@ -109,49 +106,7 @@ export function GetStarted() {
</div>
</CardContent>
</Card>
<Card
id="getting-started"
asChild
className="max-w-xl w-full mx-auto my-6 scroll-mt-28"
>
<motion.div>
<CardHeader>
<CardTitle>Getting Started</CardTitle>
<CardDescription>
Learn how Rivet works and how you can customize it to suit your
needs.
</CardDescription>
</CardHeader>
<CardContent>
<motion.div
variants={containerVariants}
initial="hidden"
whileInView="show"
className="grid md:grid-cols-3 gap-4"
>
<ExampleLink
href="examples"
title="Intro to Rivet"
size="md"
icon={faHandWave}
/>
<ExampleLink
href="examples"
title="Initial Setup"
size="md"
icon={faToolbox}
/>
<ExampleLink
href="examples"
title="Configuration"
size="md"
icon={faCog}
/>
</motion.div>
</CardContent>
</motion.div>
</Card>
<Card asChild className="max-w-xl w-full mx-auto my-6">
<Card id="examples" asChild className="max-w-xl w-full mx-auto my-6">
<motion.div>
<CardHeader>
<CardTitle>Examples</CardTitle>
Expand All @@ -165,6 +120,7 @@ export function GetStarted() {
variants={containerVariants}
initial="hidden"
whileInView="show"
viewport={{ once: true }}
className="grid md:grid-cols-3 gap-4"
>
<ExampleLink
Expand Down Expand Up @@ -214,6 +170,7 @@ export function GetStarted() {
variants={containerVariants}
initial="hidden"
whileInView="show"
viewport={{ once: true }}
className="grid md:grid-cols-2 gap-4"
>
<ExampleLink
Expand Down
7 changes: 2 additions & 5 deletions apps/hub/src/components/header/header-route-loader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRouterState } from "@tanstack/react-router";
import { ShimmerLine } from "../shimmer-line";

export function HeaderRouteLoader() {
const isLoading = useRouterState({
Expand All @@ -8,9 +9,5 @@ export function HeaderRouteLoader() {
if (!isLoading) {
return null;
}
return (
<div className="animate-in fade-in absolute inset-x-0 -bottom-1 w-full overflow-hidden">
<div className="animate-bounce-x from-secondary/0 via-primary to-secondary/0 relative -bottom-px h-1 bg-gradient-to-r" />
</div>
);
return <ShimmerLine className="-bottom-1" />;
}
35 changes: 29 additions & 6 deletions apps/hub/src/components/intro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import {
CardTitle,
Skeleton,
} from "@rivet-gg/components";
import * as Sentry from "@sentry/react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { Navigate, useNavigate } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Suspense, useState } from "react";

Expand All @@ -27,7 +28,12 @@ enum Step {
ChoosePlan = 2,
}

export function Intro({ initialStep }: { initialStep?: Step }) {
interface IntroProps {
initialStep?: Step;
initialProjectName?: string;
}

export function Intro({ initialStep, initialProjectName }: IntroProps) {
const { mutateAsync, data: createdGroupResponse } = useGroupCreateMutation();
const { mutateAsync: createProject, data: projectCreationData } =
useProjectCreateMutation();
Expand All @@ -38,14 +44,13 @@ export function Intro({ initialStep }: { initialStep?: Step }) {
data
.flatMap((team) => team.projects)
.find((project) => project.gameId === projectCreationData?.gameId) ||
// biome-ignore lint/style/noNonNullAssertion: at this point user should have at least one project
data.find((team) => team.projects.length > 0)!.projects[0]!;
data.find((team) => team.projects.length > 0)?.projects[0];

const [step, setStep] = useState<Step>(
() => initialStep ?? (!project ? Step.CreateGroup : Step.CreateProject),
);

const groupId = createdGroupResponse?.groupId || project.developer.groupId;
const groupId = createdGroupResponse?.groupId || project?.developer.groupId;

const navigate = useNavigate();

Expand All @@ -60,7 +65,10 @@ export function Intro({ initialStep }: { initialStep?: Step }) {
exit={{ opacity: 0 }}
>
<GroupCreateProjectForm.Form
defaultValues={{ slug: "", name: "" }}
defaultValues={{
slug: "",
name: initialProjectName ?? "",
}}
onSubmit={async (values) => {
await createProject({
displayName: values.name,
Expand All @@ -80,6 +88,12 @@ export function Intro({ initialStep }: { initialStep?: Step }) {
</CardHeader>
<CardContent>
<div className="grid grid-cols-[auto_auto_min-content] items-center gap-4 ">
{initialProjectName ? (
<GroupCreateProjectForm.SetValue
name="name"
value={initialProjectName}
/>
) : null}
<GroupCreateProjectForm.Name className="contents space-y-0" />
<GroupCreateProjectForm.Slug className="contents space-y-0" />
<GroupCreateProjectForm.Submit
Expand All @@ -98,6 +112,15 @@ export function Intro({ initialStep }: { initialStep?: Step }) {
}

if (step === Step.ChoosePlan) {
if (!groupId || !project) {
// At this point those values should be defined, if not, we should redirect to the home page
// It's unlikely that this will happen, but it's better to be safe than sorry
Sentry.captureMessage(
"Group or project not defined in Intro component",
"fatal",
);
return <Navigate to="/" replace />;
}
return (
<Suspense
fallback={
Expand Down
17 changes: 17 additions & 0 deletions apps/hub/src/components/shimmer-line.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { cn } from "@rivet-gg/components";

interface ShimmerLineProps {
className?: string;
}
export const ShimmerLine = ({ className }: ShimmerLineProps) => {
return (
<div
className={cn(
"animate-in fade-in absolute inset-x-0 w-full overflow-hidden",
className,
)}
>
<div className="animate-bounce-x from-secondary/0 via-primary to-secondary/0 relative -bottom-px h-1 bg-gradient-to-r" />
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export type SubmitHandler = (
form: UseFormReturn<FormValues>,
) => Promise<void>;

const { Form, Submit } = createSchemaForm(formSchema);
export { Form, Submit };
const { Form, Submit, SetValue } = createSchemaForm(formSchema);
export { Form, Submit, SetValue };

export const Name = ({ className }: { className?: string }) => {
const { control } = useFormContext<FormValues>();
Expand Down
2 changes: 1 addition & 1 deletion apps/hub/src/domains/project/queries/backend/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Event = z.infer<typeof Event>;

export const BackendEvent = z
.object({
dispatchEnvironment: z.string(),
dispatchEnvironment: z.string().optional(),
event: Event,
eventTimestamp: z.string(),
logs: z
Expand Down
8 changes: 6 additions & 2 deletions apps/hub/src/routes/_authenticated/_layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { LayoutGroup, motion } from "framer-motion";
import { z } from "zod";

function IndexRoute() {
const { initialStep } = Route.useSearch();
const { initialStep, project_name: projectName } = Route.useSearch();
return (
<>
<OnboardingBackground />
Expand All @@ -34,7 +34,10 @@ function IndexRoute() {
animate={{ opacity: 1, transition: { delay: 0.5 } }}
>
<LayoutGroup>
<Intro initialStep={initialStep} />
<Intro
initialProjectName={projectName}
initialStep={initialStep}
/>
</LayoutGroup>
</motion.div>
</div>
Expand All @@ -46,6 +49,7 @@ function IndexRoute() {
const searchSchema = z.object({
newbie: z.coerce.boolean().optional(),
initialStep: z.coerce.number().optional(),
project_name: z.coerce.string().optional(),
});

export const Route = createFileRoute("/_authenticated/_layout/")({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ function ProjectActorsRoute() {
if (data.length === 0) {
return (
<div className="w-full h-full flex flex-col justify-center">
<Icon icon={faActors} className="text-6xl mx-auto my-4" />
<h3 className="text-center font-bold text-xl max-w-md mb-2 mx-auto">
Deploy your first Actor
</h3>
<p className="text-center text-muted-foreground max-w-sm mx-auto">
Install Rivet to get started or use an existing template to get
started.
</p>
<div className="flex flex-col justify-center my-8">
<Icon icon={faActors} className="text-6xl mx-auto my-4" />
<h3 className="text-center font-bold text-xl max-w-md mb-2 mx-auto">
Deploy your first Actor
</h3>
<p className="text-center text-muted-foreground max-w-sm mx-auto">
Install Rivet to get started or use an existing template to get
started.
</p>
</div>
<GetStarted />
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/components/src/lib/create-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ export const createSchemaForm = <Schema extends z.ZodSchema>(
name: Path;
value: PathValue<z.TypeOf<Schema>, Path>;
}) => {
const { setValue } = useFormContext<z.TypeOf<Schema>>();
const { setValue, reset } = useFormContext<z.TypeOf<Schema>>();
useEffect(() => {
setValue(props.name, props.value, { shouldDirty: true });
}, [props.name, setValue, props.value]);
reset({}, { keepDirty: true, keepValues: true, keepDirtyValues: true });
}, [props.name, setValue, reset, props.value]);
return null;
},
useContext: () => useFormContext<z.TypeOf<Schema>>(),
Expand Down

0 comments on commit 4ee877a

Please sign in to comment.