Skip to content
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

Free Trial Over Dialog and Onboarding Card #3796

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 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
6 changes: 3 additions & 3 deletions extensions/vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion gui/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useAppSelector } from "../redux/hooks";
import { selectDefaultModel } from "../redux/slices/configSlice";
import { FREE_TRIAL_LIMIT_REQUESTS } from "../util/freeTrial";
import { getLocalStorage } from "../util/localStorage";
import FreeTrialProgressBar from "./loaders/FreeTrialProgressBar";

function Footer() {
Expand All @@ -10,7 +11,7 @@ function Footer() {
return (
<footer className="flex flex-col border-0 border-t border-solid border-t-zinc-700 px-2 py-2">
<FreeTrialProgressBar
completed={parseInt(localStorage.getItem("ftc") || "0")}
completed={getLocalStorage("ftc") ?? 0}
total={FREE_TRIAL_LIMIT_REQUESTS}
/>
</footer>
Expand Down
9 changes: 2 additions & 7 deletions gui/src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
newSession,
} from "../redux/slices/sessionSlice";
import { getFontSize, isMetaEquivalentKeyPressed } from "../util";
import { getLocalStorage, setLocalStorage } from "../util/localStorage";
import { ROUTES } from "../util/navigation";
import TextDialog from "./dialogs";
import Footer from "./Footer";
Expand All @@ -23,6 +22,7 @@ import AccountDialog from "./AccountDialog";
import { AuthProvider } from "../context/Auth";
import { exitEditMode } from "../redux/thunks";
import { loadLastSession, saveCurrentSession } from "../redux/thunks/session";
import { incrementFreeTrialCount } from "../util/freeTrial";

const LayoutTopDiv = styled(CustomScrollbarDiv)`
height: 100%;
Expand Down Expand Up @@ -150,12 +150,7 @@ const Layout = () => {
useWebviewListener(
"incrementFtc",
async () => {
const u = getLocalStorage("ftc");
if (u) {
setLocalStorage("ftc", u + 1);
} else {
setLocalStorage("ftc", 1);
}
incrementFreeTrialCount();
},
[],
);
Expand Down
14 changes: 10 additions & 4 deletions gui/src/components/OnboardingCard/OnboardingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export interface OnboardingCardState {
activeTab?: TabTitle;
}

export function OnboardingCard() {
interface OnboardingCardProps {
isDialog?: boolean;
}

export function OnboardingCard({ isDialog }: OnboardingCardProps) {
const onboardingCard = useOnboardingCard();

function renderTabContent() {
Expand Down Expand Up @@ -49,9 +53,11 @@ export function OnboardingCard() {
activeTab={onboardingCard.activeTab || "Best"}
onTabClick={onboardingCard.setActiveTab}
/>
<CloseButton onClick={onboardingCard.close}>
<XMarkIcon className="mt-1.5 hidden h-5 w-5 hover:brightness-125 sm:flex" />
</CloseButton>
{!isDialog && (
<CloseButton onClick={() => onboardingCard.close()}>
<XMarkIcon className="mt-1.5 hidden h-5 w-5 hover:brightness-125 sm:flex" />
</CloseButton>
)}
<div className="content py-4">{renderTabContent()}</div>
</StyledCard>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ import { useSubmitOnboarding } from "../hooks";
import JetBrainsFetchGitHubTokenDialog from "./JetBrainsFetchGitHubTokenDialog";
import { setDefaultModel } from "../../../redux/slices/configSlice";

function QuickstartSubmitButton() {
interface QuickstartSubmitButtonProps {
isDialog?: boolean;
}

function QuickstartSubmitButton({ isDialog }: QuickstartSubmitButtonProps) {
const ideMessenger = useContext(IdeMessengerContext);
const dispatch = useDispatch();

const { submitOnboarding } = useSubmitOnboarding("Quickstart");
const { submitOnboarding } = useSubmitOnboarding("Quickstart", isDialog);

function onComplete() {
submitOnboarding();
Expand Down
22 changes: 11 additions & 11 deletions gui/src/components/OnboardingCard/hooks/useOnboardingCard.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { TabTitle } from "../components/OnboardingCardTabs";
import { setOnboardingCard } from "../../../redux/slices/uiSlice";
import {
setDialogMessage,
setOnboardingCard,
setShowDialog,
} from "../../../redux/slices/uiSlice";
import { OnboardingCardState } from "..";
import { getLocalStorage, setLocalStorage } from "../../../util/localStorage";
import { useAppDispatch, useAppSelector } from "../../../redux/hooks";
Expand All @@ -12,7 +16,7 @@ export interface UseOnboardingCard {
activeTab: OnboardingCardState["activeTab"];
setActiveTab: (tab: TabTitle) => void;
open: (tab: TabTitle) => void;
close: () => void;
close: (isDialog?: boolean) => void;
}

export function useOnboardingCard(): UseOnboardingCard {
Expand All @@ -38,20 +42,16 @@ export function useOnboardingCard(): UseOnboardingCard {

async function open(tab: TabTitle) {
navigate("/");

// Used to clear the chat panel before showing onboarding card
dispatch(
saveCurrentSession({
openNewSession: true,
}),
);

dispatch(setOnboardingCard({ show: true, activeTab: tab }));
}

function close() {
function close(isDialog = false) {
setLocalStorage("hasDismissedOnboardingCard", true);
dispatch(setOnboardingCard({ show: false }));
if (isDialog) {
dispatch(setDialogMessage(undefined));
dispatch(setShowDialog(false));
}
}

function setActiveTab(tab: TabTitle) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { OnboardingModes } from "core/protocol/core";
import { useTutorialCard } from "../../../hooks/useTutorialCard";
import { useOnboardingCard } from "./useOnboardingCard";

export function useSubmitOnboarding(mode: OnboardingModes) {
export function useSubmitOnboarding(mode: OnboardingModes, isDialog = false) {
const posthog = usePostHog();
const ideMessenger = useContext(IdeMessengerContext);
const { openTutorialCard } = useTutorialCard();
Expand All @@ -16,7 +16,7 @@ export function useSubmitOnboarding(mode: OnboardingModes) {
const onboardingStatus = getLocalStorage("onboardingStatus");

// Always close the onboarding card and update config.json
closeOnboardingCard();
closeOnboardingCard(isDialog);
ideMessenger.post("completeOnboarding", {
mode,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export interface OnboardingCardState {
activeTab?: TabTitle;
}

export function PlatformOnboardingCard() {
interface OnboardingCardProps {
isDialog: boolean;
}

export function PlatformOnboardingCard({ isDialog }: OnboardingCardProps) {
const onboardingCard = useOnboardingCard();

if (getLocalStorage("onboardingStatus") === undefined) {
Expand All @@ -34,13 +38,18 @@ export function PlatformOnboardingCard() {

return (
<StyledCard className="xs:py-4 xs:px-4 relative px-2 py-3">
<CloseButton onClick={onboardingCard.close}>
<XMarkIcon className="mt-1.5 hidden h-5 w-5 hover:brightness-125 sm:flex" />
</CloseButton>
{!isDialog && (
<CloseButton onClick={() => onboardingCard.close()}>
<XMarkIcon className="mt-1.5 hidden h-5 w-5 hover:brightness-125 sm:flex" />
</CloseButton>
)}
<div className="content py-4">
<div className="flex h-full w-full items-center justify-center">
{currentTab === "main" ? (
<MainTab onRemainLocal={() => setCurrentTab("local")} />
<MainTab
onRemainLocal={() => setCurrentTab("local")}
isDialog={isDialog}
/>
) : (
<div className="mt-4">
<Alert type="info">
Expand All @@ -53,7 +62,7 @@ export function PlatformOnboardingCard() {
</a>
</Alert>

<OnboardingLocalTab />
<OnboardingLocalTab isDialog={isDialog} />
</div>
)}
</div>
Expand Down
26 changes: 0 additions & 26 deletions gui/src/components/OnboardingCard/platform/tabs/local.tsx

This file was deleted.

68 changes: 49 additions & 19 deletions gui/src/components/OnboardingCard/platform/tabs/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,77 @@ import { Button, ButtonSubtext } from "../../..";
import { useAuth } from "../../../../context/Auth";
import ContinueLogo from "../../../gui/ContinueLogo";
import { useOnboardingCard } from "../../hooks";
import { hasPassedFTL } from "../../../../util/freeTrial";
import { useContext } from "react";
import { IdeMessengerContext } from "../../../../context/IdeMessenger";

export default function MainTab({
onRemainLocal,
isDialog,
}: {
onRemainLocal: () => void;
isDialog: boolean;
}) {
const ideMessenger = useContext(IdeMessengerContext);
const onboardingCard = useOnboardingCard();
const auth = useAuth();

function onGetStarted() {
auth.login(true).then((success) => {
if (success) {
onboardingCard.close();
onboardingCard.close(isDialog);
}
});
}

function openPastFreeTrialOnboarding() {
// NATE TODO - add custom onboarding process handling here
RomneyDa marked this conversation as resolved.
Show resolved Hide resolved
ideMessenger.post("openUrl", "app.continue.dev/onboard?freeTrialStatus=1");
onboardingCard.close(isDialog);
}

const pastFreeTrialLimit = hasPassedFTL();

return (
<div className="xs:px-0 flex w-full max-w-full flex-col items-center justify-center px-4 text-center">
<div className="xs:flex hidden">
<ContinueLogo height={75} />
</div>

<p className="xs:w-3/4 w-full text-sm">
Log in to quickly build your first custom AI code assistant
</p>

<div className="mt-4 w-full">
<Button
onClick={onGetStarted}
className="grid w-full grid-flow-col items-center gap-2"
>
Get started
</Button>
<ButtonSubtext onClick={onRemainLocal}>
<div className="mt-4 flex cursor-pointer items-center justify-center gap-1">
<span>Or, remain local</span>
<ChevronRightIcon className="h-3 w-3" />
</div>
</ButtonSubtext>
</div>
{pastFreeTrialLimit ? (
<>
<p className="xs:w-3/4 w-full text-sm">
You've reached the free trial limit. Visit the Continue Platform to
select a Coding Assistant.
</p>
<Button
onClick={openPastFreeTrialOnboarding}
className="mt-4 grid w-full grid-flow-col items-center gap-2"
>
Go to Continue Platform
</Button>
</>
) : (
<>
<p className="xs:w-3/4 w-full text-sm">
Log in to quickly build your first custom AI code assistant
</p>

<Button
onClick={onGetStarted}
className="mt-4 grid w-full grid-flow-col items-center gap-2"
>
Get started
</Button>
</>
)}

<ButtonSubtext onClick={onRemainLocal}>
<div className="mt-4 flex cursor-pointer items-center justify-center gap-1">
<span>Or, remain local</span>
<ChevronRightIcon className="h-3 w-3" />
</div>
</ButtonSubtext>
</div>
);
}
8 changes: 6 additions & 2 deletions gui/src/components/OnboardingCard/tabs/OnboardingBestTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import BestExperienceConfigForm from "../components/BestExperienceConfigForm";
import ProviderAlert from "../components/ProviderAlert";
import { useSubmitOnboarding } from "../hooks";

function OnboardingBestTab() {
const { submitOnboarding } = useSubmitOnboarding("Best");
interface OnboardingBestTabProps {
isDialog?: boolean;
}

function OnboardingBestTab({ isDialog }: OnboardingBestTabProps) {
const { submitOnboarding } = useSubmitOnboarding("Best", isDialog);

return (
<div className="flex flex-col gap-8">
Expand Down
Loading
Loading