Skip to content

Commit

Permalink
Add onboarding process.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tumppi066 committed Jan 16, 2025
1 parent 4f148e5 commit 3512c67
Show file tree
Hide file tree
Showing 9 changed files with 494 additions and 91 deletions.
93 changes: 92 additions & 1 deletion app/csr_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,55 @@ import Snowfall from "react-snowfall"
import useSWR from "swr";
import Loader from "@/components/loader";
import { useCollapsed } from "@/contexts/collapsed";
import { ACTIONS, EVENTS, STATUS, CallBackProps } from 'react-joyride';
import { JoyRideNoSSR, skipAll } from "@/components/joyride-no-ssr";

export default function CSRLayout({ children, }: Readonly<{ children: React.ReactNode; }>) {
const { data: language, isLoading: loadingLanguage } = useSWR("language", GetCurrentLanguage, { refreshInterval: 2000 });
const { isCollapsed, setIsCollapsed } = useCollapsed();
const [isSnowAllowed, setIsSnowAllowed] = useState(false);
const [areFireworksAllowed, setAreFireworksAllowed] = useState(false);
const [loadingTranslations, setLoadingTranslations] = useState(true);
const [hasDoneOnboarding, setHasDoneOnboarding] = useState(false);
const [stepIndex, setStepIndex] = useState(0);
const [run, setRun] = useState(false);
const isMobile = useIsMobile();

const STEPS = [
{
target: "#window_controls",
content: "These are the window controls. The left most button controls some extra options. Hover over it to see a tooltip! (after the onboarding)",
disableBeacon: true,
},
{
target: "#slide_area",
content: "You can move the window around by dragging this area.",
disableBeacon: true,
hideFooter: true,
},
{
target: "#sidebar_rail",
content: 'You can click on the side of the sidebar to collapse it and go into "fullscreen" mode.',
placement: "right",
disableBeacon: true,
hideFooter: true,
},
{
target: "#sidebar",
content: 'You can easily navigate the different app pages using the sidebar. Some of them have more tips to help you get used to the app!',
placement: "right",
disableBeacon: true,
hideFooter: true,
},
{
target: "#settings",
content: 'For now you should head to the settings page!',
placement: "right",
disableBeacon: true,
hideFooter: true,
},
];

useEffect(() => {
if (language) {
changeLanguage(language);
Expand All @@ -55,12 +95,33 @@ export default function CSRLayout({ children, }: Readonly<{ children: React.Reac
const isNewYear = ((month === 0 && day === 1) || (month === 11 && day === 31)) && areFireworksAllowed;
const isSnowing = (month >= 10 || month === 0) && isSnowAllowed;

const handleJoyrideCallback = (data: CallBackProps) => {
const { action, index, origin, status, type } = data;

// @ts-expect-error
if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1));
} // @ts-expect-error
else if ([STATUS.FINISHED].includes(status)) {
localStorage.setItem("hasDoneOnboarding", "true");
} // @ts-expect-error
else if ([STATUS.SKIPPED].includes(status)) {
skipAll();
}
};

useEffect(() => {
loadTranslations().then(() => {
const hasDoneOnboarding = localStorage.getItem("hasDoneOnboarding");
setHasDoneOnboarding(hasDoneOnboarding === "true");
setLoadingTranslations(false);
});
}, []);

const startOnboarding = () => {
setRun(true);
}

const toggleSidebar = () => {
setIsCollapsed(!isCollapsed);
}
Expand Down Expand Up @@ -91,8 +152,38 @@ export default function CSRLayout({ children, }: Readonly<{ children: React.Reac
</div>
) || (
<>
<Disclaimer />
<Disclaimer closed_callback={startOnboarding} />
<ProgressBarProvider>
<JoyRideNoSSR // @ts-expect-error no clue why it's complaining on the steps
steps={STEPS}
run={run && !hasDoneOnboarding}
stepIndex={stepIndex}
showSkipButton
spotlightPadding={5}
styles={
{
options: {
backgroundColor: "#18181b",
arrowColor: "#18181b",
textColor: "#fafafa",
},
buttonClose: {
width: "8px",
height: "8px",
},
buttonNext: {
visibility: "hidden",
},
buttonBack: {
visibility: "hidden",
},
tooltipContent: {
fontSize: "14px",
}
}
}
callback={handleJoyrideCallback}
/>
<Toaster position={isCollapsed ? "bottom-center" : "bottom-right"} toastOptions={{
unstyled: true,
classNames: {
Expand Down
98 changes: 88 additions & 10 deletions app/plugins/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
"use client"

import {
Card
} from "@/components/ui/card"
"use client";

import { toast } from "sonner"
import useSWR, { mutate } from "swr"
Expand Down Expand Up @@ -30,12 +26,59 @@ import { TooltipContent } from "@radix-ui/react-tooltip"
import { ip } from "@/apis/backend"
import { AnimatePresence, motion } from "framer-motion"
import { ScrollArea } from "@/components/ui/scroll-area"
import { JoyRideNoSSR } from "@/components/joyride-no-ssr";
import { ACTIONS, EVENTS, ORIGIN, STATUS, CallBackProps } from 'react-joyride';
import { useEffect } from "react";

const STEPS = [
{
target: "#search_options",
content: "You can use the search options to easily find what you are looking for.",
disableBeacon: true,
hideFooter: true,
},
{
target: "#selected_tags",
content: "By default the Base tag is selected. You can add more selections by clicking on the tags in the plugin list or by opening the dropdown here.",
disableBeacon: true,
hideFooter: true,
},
{
target: "#clear_button",
content: 'Use the "Clear" button to remove all current search options.',
disableBeacon: true,
hideFooter: true,
},
{
target: "#enable_all_checkbox",
content: 'You can enable/disable all the currently visible plugins by clicking this checkbox.',
placement: "right",
disableBeacon: true,
hideFooter: true,
},
{
target: "#plugins_list",
content: 'To get started you can enable everything but the "Data Collection End-To-End Driving" and "Navigation Detection" plugins. If your PC is less powerful, then you can disable "Object Detection", at the cost of losing any vision.',
placement: "right",
disableBeacon: true,
hideFooter: true,
},
];

export default function Home() {
const [ search, setSearch ] = useState<string>("")
const [ searchTags, setSearchTags ] = useState<string[]>(["Base"])
const [ searchAuthors, setSearchAuthors ] = useState<string[]>([])
const { data, error, isLoading } = useSWR("plugins", () => GetPlugins(), { refreshInterval: 1000 }) as any
const [hasDonePluginsOnboarding, setHasDonePluginsOnboarding] = useState(false);
const [stepIndex, setStepIndex] = useState(0);

useEffect(() => {
if (localStorage.getItem("hasDonePluginsOnboarding") === "true") {
setHasDonePluginsOnboarding(true);
}
}, []);

if (isLoading) return <p className="absolute left-5 top-5 font-semibold text-xs text-stone-400">{translate("loading")}</p>
if (error){
toast.error(translate("frontend.menubar.error_fetching_plugins", ip), {description: error.message})
Expand Down Expand Up @@ -150,22 +193,57 @@ export default function Home() {
}
}

const handleJoyrideCallback = (data: CallBackProps) => {
const { action, index, origin, status, type } = data;

// @ts-expect-error
if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1));
} // @ts-expect-error
else if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) {
localStorage.setItem("hasDonePluginsOnboarding", "true");
}
};

return (
<motion.div className="h-full font-geist p-2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6 }}
>
<JoyRideNoSSR // @ts-expect-error no clue why it's complaining on the steps
steps={STEPS}
run={!hasDonePluginsOnboarding}
stepIndex={stepIndex}
spotlightPadding={5}
styles={
{
options: {
backgroundColor: "#18181b",
arrowColor: "#18181b",
textColor: "#fafafa",
},
buttonClose: {
width: "8px",
height: "8px",
},
tooltipContent: {
fontSize: "14px",
}
}
}
callback={handleJoyrideCallback}
/>
<div className="flex flex-col gap-2 p-5 pb-0 pt-2">
<div className="flex gap-3 items-center pr-10">
<div className="flex gap-3 items-center pr-10" id="search_options">
<p className="text-lg font-semibold pr-2 min-w-20">{translate("frontend.sidebar.plugins")}</p>
{/* Search options */}
<Input placeholder={translate("search")} value={search} onChange={(e) => setSearch(e.target.value)} />
<div className="p-0 h-3"></div> {/* Makeshift separator */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="flex text-left items-center justify-start">
<Button variant="outline" className="flex text-left items-center justify-start" id="selected_tags">
<p className={searchTags.length > 0 ? "font-normal" : "font-normal text-muted-foreground"}>
{searchTags.length > 0 ? translate("frontend.plugins.selected_tags", searchTags.length) : translate("frontend.plugins.select_tags")}
</p>
Expand Down Expand Up @@ -213,7 +291,7 @@ export default function Home() {
setSearch("")
setSearchTags([])
setSearchAuthors([])
}} className="text-muted-foreground font-normal">{translate("clear")}</Button>
}} className="text-muted-foreground font-normal" id="clear_button" >{translate("clear")}</Button>
) : null
}
</div>
Expand Down Expand Up @@ -254,10 +332,10 @@ export default function Home() {
setTimeout(() => {
mutate("plugins")
}, 200)
}} className={all_visible_enabled ? "left-3.5 absolute opacity-100" : "left-3.5 absolute opacity-60"} />
}} id="enable_all_checkbox" className={all_visible_enabled ? "left-3.5 absolute opacity-100" : "left-3.5 absolute opacity-60"} />
</div>

<ScrollArea className="max-h-[calc(100vh-190px)] overflow-y-auto">
<ScrollArea className="max-h-[calc(100vh-190px)] overflow-y-auto" id="plugins_list">
{enabled_plugins.map((plugin:any, index) => (
<motion.div key={plugin} className="w-full group relative border border-t-0 h-10 grid grid-cols-8 items-center text-left pl-12 cursor-pointer" layout animate={{ opacity: 1 }} initial={{ opacity: 0 }} exit={{ opacity: 0 }}>
<Checkbox checked={data[plugin].enabled} onClick={() => {
Expand Down
Loading

0 comments on commit 3512c67

Please sign in to comment.