Skip to content
Closed
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
23 changes: 2 additions & 21 deletions apps/desktop/src/components/main/body/onboarding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQueryClient } from "@tanstack/react-query";
import { platform } from "@tauri-apps/plugin-os";
import { Volume2Icon, VolumeXIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";

import { commands as analyticsCommands } from "@hypr/plugin-analytics";
import { commands as sfxCommands } from "@hypr/plugin-sfx";
Expand Down Expand Up @@ -115,20 +115,6 @@ export function TabContentOnboarding({
}
}, [close, currentTab, queryClient]);

const scrollRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const timeout = setTimeout(() => {
const el = scrollRef.current?.querySelector(
`[data-step="${currentStep}"]`,
);
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "start" });
}
}, 350);
return () => clearTimeout(timeout);
}, [currentStep]);

return (
<StandardTabWrapper>
<div className="relative flex h-full flex-col">
Expand All @@ -149,10 +135,9 @@ export function TabContentOnboarding({
</button>
</div>

<div ref={scrollRef} className="flex-1 overflow-y-auto">
<div className="flex-1 overflow-y-auto">
<div className="flex flex-col px-6 pb-16 gap-3">
<OnboardingSection
stepId="permissions"
title="Permissions"
completedTitle="Permissions granted"
description="Required for best experience"
Expand All @@ -164,7 +149,6 @@ export function TabContentOnboarding({
</OnboardingSection>

<OnboardingSection
stepId="login"
title="Account"
description="Sign in to unlock Pro features"
completedTitle="Signed up"
Expand All @@ -176,7 +160,6 @@ export function TabContentOnboarding({
</OnboardingSection>

<OnboardingSection
stepId="calendar"
title="Calendar"
description="Select calendars to sync"
completedTitle="Calendar connected"
Expand All @@ -188,7 +171,6 @@ export function TabContentOnboarding({
</OnboardingSection>

<OnboardingSection
stepId="folder-location"
title="Storage"
description="Where your notes and recordings are stored"
completedTitle="Storage configured"
Expand All @@ -200,7 +182,6 @@ export function TabContentOnboarding({
</OnboardingSection>

<OnboardingSection
stepId="final"
title="Ready to go"
status={getStepStatus("final", currentStep)}
onBack={goBack}
Expand Down
14 changes: 5 additions & 9 deletions apps/desktop/src/components/onboarding/account/before-login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,24 @@ import { useEffect, useState } from "react";

import { useAuth } from "../../../auth";

export function BeforeLogin({
onContinue: _onContinue,
}: {
onContinue: () => void;
}) {
export function BeforeLogin() {
const auth = useAuth();
const triggered = useAutoTriggerSignin();
const autoSignInCompleted = useAutoTriggerSignin();
const [showCallbackUrlInput, setShowCallbackUrlInput] = useState(false);

return (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-3">
<button
onClick={() => auth?.signIn()}
disabled={!triggered}
disabled={!autoSignInCompleted}
className="px-5 py-2.5 rounded-full bg-stone-600 text-white text-sm font-medium duration-150 hover:scale-[1.01] active:scale-[0.99] w-fit"
>
{triggered
{autoSignInCompleted
? "Click here to Sign in"
: "Signing in on your browser..."}
</button>
{triggered && (
{autoSignInCompleted && (
<button
className="text-sm text-neutral-500 hover:text-neutral-600 underline"
onClick={() => setShowCallbackUrlInput(true)}
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/onboarding/account/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ export function LoginSection({ onContinue }: { onContinue: () => void }) {
return <AfterLogin onContinue={onContinue} />;
}

return <BeforeLogin onContinue={onContinue} />;
return <BeforeLogin />;
}
27 changes: 15 additions & 12 deletions apps/desktop/src/components/onboarding/final.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { commands as sfxCommands } from "@hypr/plugin-sfx";
import { commands } from "../../types/tauri.gen";
import { OnboardingButton } from "./shared";

const DEFAULT_ICON_SIZE = 14;

const SOCIALS = [
{
label: "Discord",
icon: "simple-icons:discord",
size: 14,
url: "https://discord.gg/CX8gTH2tj9",
},
{
label: "GitHub",
icon: "simple-icons:github",
size: 14,
url: "https://github.com/fastrepl/hyprnote",
},
{
Expand All @@ -33,16 +33,19 @@ export function FinalSection({ onContinue }: { onContinue: () => void }) {
<div className="flex flex-col gap-6">
<div className="flex items-center gap-1.5 text-sm text-neutral-500">
<span>Join our community and stay updated:</span>
{SOCIALS.map(({ label, icon, size, url }) => (
<button
key={label}
onClick={() => void openerCommands.openUrl(url, null)}
className="inline-flex items-center justify-center size-6 rounded-md text-neutral-400 transition-colors duration-150 hover:text-neutral-700"
aria-label={label}
>
<Icon icon={icon} width={size} height={size} />
</button>
))}
{SOCIALS.map(({ label, icon, url, ...rest }) => {
const size = "size" in rest ? rest.size : DEFAULT_ICON_SIZE;
return (
<button
key={label}
onClick={() => void openerCommands.openUrl(url, null)}
className="inline-flex items-center justify-center size-6 rounded-md text-neutral-400 transition-colors duration-150 hover:text-neutral-700"
aria-label={label}
>
<Icon icon={icon} width={size} height={size} />
</button>
);
})}
</div>

<OnboardingButton onClick={() => void finishOnboarding(onContinue)}>
Expand Down
20 changes: 15 additions & 5 deletions apps/desktop/src/components/onboarding/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {
XCircleIcon,
} from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import type { ReactNode } from "react";
import { type ReactNode, useEffect, useRef } from "react";

import { cn } from "@hypr/utils";

const SCROLL_DELAY_MS = 350;

export type SectionStatus = "completed" | "active" | "upcoming";

export function OnboardingSection({
stepId,
title,
completedTitle,
description,
Expand All @@ -23,7 +24,6 @@ export function OnboardingSection({
onNext,
children,
}: {
stepId: string;
title: string;
completedTitle?: string;
description?: string;
Expand All @@ -32,13 +32,23 @@ export function OnboardingSection({
onNext?: () => void;
children: ReactNode;
}) {
if (!status || status === "upcoming") return null;
const sectionRef = useRef<HTMLElement>(null);

const isActive = status === "active";
const isCompleted = status === "completed";

useEffect(() => {
if (!isActive) return;
const timeout = setTimeout(() => {
sectionRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
}, SCROLL_DELAY_MS);
return () => clearTimeout(timeout);
}, [isActive]);

if (!status || status === "upcoming") return null;

return (
<section data-step={stepId}>
<section ref={sectionRef}>
<div
className={cn([
"flex items-center gap-2 transition-all duration-300",
Expand Down
Loading