Skip to content

chore: onboarding metrics #1626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
// To bundle the code the same way we do for publishing
"vscode-extension:esbuild",
// Start the React app that is used in the extension
"gui:dev",
// Start the docs site, without opening the browser
"docs:start"
"gui:dev"
],
"group": {
"kind": "build",
Expand Down
4 changes: 2 additions & 2 deletions gui/src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import ProgressBar from "./loaders/ProgressBar";
import ModelSelect from "./modelSelection/ModelSelect";
import PostHogPageView from "./PosthogPageView";
import { FREE_TRIAL_LIMIT_REQUESTS } from "../util/freeTrial";
import { shouldBeginOnboarding } from "../pages/onboarding/utils";

// #region Styled Components
const FOOTER_HEIGHT = "1.8em";
Expand Down Expand Up @@ -212,9 +213,8 @@ const Layout = () => {
);

useEffect(() => {
const onboardingComplete = getLocalStorage("onboardingComplete");
if (
!onboardingComplete &&
shouldBeginOnboarding() &&
(location.pathname === "/" || location.pathname === "/index.html")
) {
navigate("/onboarding");
Expand Down
4 changes: 3 additions & 1 deletion gui/src/components/PosthogPageView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import { useSearchParams, useLocation } from "react-router-dom";
*/
export default function PostHogPageView() {
const { pathname } = useLocation();
const searchParams = useSearchParams();
const [searchParams] = useSearchParams();
const posthog = usePostHog();

// Track pageviews
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname;

if (searchParams.toString()) {
url = url + `?${searchParams.toString()}`;
}

posthog.capture("$pageview", {
$current_url: url,
});
Expand Down
27 changes: 13 additions & 14 deletions gui/src/components/dialogs/KeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
lightGray,
vscForeground,
} from "..";
import { getPlatform } from "../../util";
import { getPlatform, isJetBrains } from "../../util";

const GridDiv = styled.div`
display: grid;
Expand Down Expand Up @@ -209,19 +209,18 @@ function KeyboardShortcutsDialog() {
<div className="p-2">
<h3 className="my-3 mx-auto text-center">Keyboard Shortcuts</h3>
<GridDiv>
{(localStorage.getItem("ide") === "jetbrains"
? jetbrainsShortcuts
: vscodeShortcuts
).map((shortcut, i) => {
return (
<KeyboardShortcut
key={i}
mac={shortcut.mac}
windows={shortcut.windows}
description={shortcut.description}
/>
);
})}
{(isJetBrains() ? jetbrainsShortcuts : vscodeShortcuts).map(
(shortcut, i) => {
return (
<KeyboardShortcut
key={i}
mac={shortcut.mac}
windows={shortcut.windows}
description={shortcut.description}
/>
);
},
)}
</GridDiv>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ function GitHubSignInButton(props: GitHubSignInButtonProps) {
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
fill={vscForeground}
/>
Expand Down Expand Up @@ -73,8 +73,8 @@ function GitHubSignInButton(props: GitHubSignInButtonProps) {
>
<svg viewBox="0 0 98 96" height={24} xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
fill={vscForeground}
/>
Expand Down
6 changes: 4 additions & 2 deletions gui/src/pages/onboarding/ApiKeysOnboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import QuickModelSetup from "../../components/modelSelection/quickSetup/QuickMod
import { getLocalStorage } from "../../util/localStorage";
import Toggle from "../../components/modelSelection/Toggle";
import DefaultModelConfigForm from "./DefaultModelConfigForm";
import { useOnboarding } from "./utils";

function ApiKeysOnboarding() {
const ideMessenger = useContext(IdeMessengerContext);
const navigate = useNavigate();

// Controls the toggle between default and custom model setup
const [isBestToggle, setIsBestToggle] = useState(true);

const { completeOnboarding } = useOnboarding();

return (
<div className="p-8 overflow-y-scroll">
<div>
Expand Down Expand Up @@ -50,7 +52,7 @@ function ApiKeysOnboarding() {
ideMessenger.post("showTutorial", undefined);

if (getLocalStorage("signedInToGh")) {
navigate("/");
completeOnboarding();
} else {
navigate("/apiKeyAutocompleteOnboarding");
}
Expand Down
7 changes: 4 additions & 3 deletions gui/src/pages/onboarding/DefaultModelConfigForm.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { Input, lightGray } from "../../components";
import { IdeMessengerContext } from "../../context/IdeMessenger";
import { setDefaultModel } from "../../redux/slices/stateSlice";
import { models } from "../AddNewModel/configs/models";
import { providers } from "../AddNewModel/configs/providers";
import { StyledButton } from "./components";
import { useOnboarding } from "./utils";

const HelperText = styled.p`
font-size: 0.8rem;
Expand All @@ -16,14 +16,15 @@ const HelperText = styled.p`
`;

function DefaultModelConfigForm() {
const navigate = useNavigate();
const dispatch = useDispatch();

const ideMessenger = useContext(IdeMessengerContext);

const [mistralApiKey, setMistralApiKey] = useState("");
const [anthropicApiKey, setAnthropicApiKey] = useState("");

const { completeOnboarding } = useOnboarding();

const isFormComplete = !!mistralApiKey && !!anthropicApiKey;

const { anthropic, mistral } = providers;
Expand Down Expand Up @@ -56,7 +57,7 @@ function DefaultModelConfigForm() {

ideMessenger.post("showTutorial", undefined);

navigate("/");
completeOnboarding();
}

return (
Expand Down
9 changes: 4 additions & 5 deletions gui/src/pages/onboarding/LocalOnboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CopyToTerminalButton } from "./CopyToTerminalButton";
import { CheckMarkHeader } from "./CheckMarkHeader";
import { ONBOARDING_LOCAL_MODEL_TITLE } from "core/config/onboarding";
import { ArrowLeftIcon } from "@heroicons/react/24/outline";
import { useOnboarding } from "./utils";

type OllamaConnectionStatuses =
| "waiting_to_download"
Expand Down Expand Up @@ -34,11 +35,9 @@ function LocalOnboarding() {

const [hasLoadedChatModel, setHasLoadedChatModel] = useState(false);

const isOllamaConnected = ollamaConnectionStatus === "verified";
const { completeOnboarding } = useOnboarding();

function handleCompleteClick() {
navigate("/");
}
const isOllamaConnected = ollamaConnectionStatus === "verified";

function isModelDownloaded(model: string) {
if (!downloadedOllamaModels) {
Expand Down Expand Up @@ -219,7 +218,7 @@ function LocalOnboarding() {
</div>

<div className="flex flex-col justify-end mt-4">
<StyledButton onClick={handleCompleteClick}>
<StyledButton onClick={completeOnboarding}>
Complete onboarding
</StyledButton>
</div>
Expand Down
10 changes: 4 additions & 6 deletions gui/src/pages/onboarding/Onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {
ComputerDesktopIcon,
} from "@heroicons/react/24/outline";
import { ToCoreFromIdeOrWebviewProtocol } from "core/protocol/core";
import { useContext, useEffect, useState } from "react";
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import GitHubSignInButton from "../../components/modelSelection/quickSetup/GitHubSignInButton";
import { IdeMessengerContext } from "../../context/IdeMessenger";
import { isJetBrains } from "../../util";
import { setLocalStorage } from "../../util/localStorage";
import { Div, StyledButton } from "./components";
import { FREE_TRIAL_LIMIT_REQUESTS, hasPassedFTL } from "../../util/freeTrial";
import { useOnboarding } from "./utils";

type OnboardingMode =
ToCoreFromIdeOrWebviewProtocol["completeOnboarding"][0]["mode"];
Expand All @@ -26,9 +26,7 @@ function Onboarding() {
OnboardingMode | undefined
>(undefined);

useEffect(() => {
setLocalStorage("onboardingComplete", true);
}, []);
const { completeOnboarding } = useOnboarding();

function onSubmit() {
ideMessenger.post("completeOnboarding", {
Expand All @@ -54,7 +52,7 @@ function Onboarding() {
break;

case "freeTrial":
navigate("/");
completeOnboarding();
break;

default:
Expand Down
9 changes: 7 additions & 2 deletions gui/src/pages/onboarding/apiKeyAutocompleteOnboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
StyledListboxOptions,
} from "../../components/modelSelection/quickSetup/StyledListbox";
import { IdeMessengerContext } from "../../context/IdeMessenger";
import { useOnboarding } from "./utils";

interface AutocompleteOption {
provider: ModelProvider;
Expand Down Expand Up @@ -123,6 +124,8 @@ function ApiKeyAutocompleteOnboarding() {

const [apiKeyValue, setApiKeyValue] = useState<string>("");

const { completeOnboarding } = useOnboarding();

return (
<div className="p-2 max-w-96 mt-16 mx-auto">
<h1 className="text-center">Autocomplete Model</h1>
Expand Down Expand Up @@ -164,7 +167,8 @@ function ApiKeyAutocompleteOnboarding() {
model: TRIAL_FIM_MODEL,
},
});
navigate("/");

completeOnboarding();
}}
></GitHubSignInButton>
</div>
Expand Down Expand Up @@ -199,7 +203,8 @@ function ApiKeyAutocompleteOnboarding() {
apiKey: apiKeyValue,
},
});
navigate("/");

completeOnboarding();
}}
>
Save
Expand Down
46 changes: 46 additions & 0 deletions gui/src/pages/onboarding/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { usePostHog } from "posthog-js/react";
import { getLocalStorage, setLocalStorage } from "../../util/localStorage";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

// Note that there is no "NotStarted" status since the
// local storage value is null until onboarding begins
export type OnboardingStatus = "Started" | "Completed";

// If there is no value in local storage for "onboardingStatus",
// it implies that the user has not begun or completed onboarding.
export function shouldBeginOnboarding() {
const onboardingStatus = getLocalStorage("onboardingStatus");

return onboardingStatus === undefined;
}

/**
* Telemetry, status tracking, and routing logic for new user onboarding.
*/
export function useOnboarding() {
const posthog = usePostHog();
const navigate = useNavigate();

const completeOnboarding = () => {
const onboardingStatus = getLocalStorage("onboardingStatus");

if (onboardingStatus === "Started") {
setLocalStorage("onboardingStatus", "Completed");
posthog.capture("Onboarding Step", { status: "Completed" });
}

navigate("/");
};

useEffect(() => {
if (shouldBeginOnboarding()) {
setLocalStorage("onboardingStatus", "Started");
posthog.capture("Onboarding Step", { status: "Started" });
}
}, []);

return {
completeOnboarding,
};
}
6 changes: 5 additions & 1 deletion gui/src/util/localStorage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { JSONContent } from "@tiptap/react";
import { IndexingProgressUpdate } from "core";
import { OnboardingStatus } from "../pages/onboarding/utils";

type LocalStorageTypes = {
onboardingComplete: boolean;
onboardingStatus?: OnboardingStatus;
mainTextEntryCounter: number;
ide: "vscode" | "jetbrains";
ftc: number;
Expand All @@ -12,15 +13,18 @@ type LocalStorageTypes = {
extensionVersion: string;
indexingState: IndexingProgressUpdate;
signedInToGh: boolean;
isOnboardingInProgress: boolean;
};

export function getLocalStorage<T extends keyof LocalStorageTypes>(
key: T,
): LocalStorageTypes[T] | undefined {
const value = localStorage.getItem(key);

if (value === null) {
return undefined;
}

try {
return JSON.parse(value);
} catch (error) {
Expand Down
Loading