From a226f3347cb77affffca21dd00fb512f7bc0388b Mon Sep 17 00:00:00 2001 From: mrjvs Date: Tue, 16 Jan 2024 22:07:21 +0100 Subject: [PATCH] Add onboarding functionality --- src/pages/onboarding/Onboarding.tsx | 4 +- src/pages/onboarding/OnboardingExtension.tsx | 55 +++++++++++++++++++- src/pages/onboarding/OnboardingProxy.tsx | 27 ++++++++-- src/pages/onboarding/onboardingHooks.ts | 10 ++-- src/stores/onboarding/index.tsx | 10 ++-- src/utils/onboarding.ts | 6 +-- 6 files changed, 92 insertions(+), 20 deletions(-) diff --git a/src/pages/onboarding/Onboarding.tsx b/src/pages/onboarding/Onboarding.tsx index 439cc87d..3247a7ee 100644 --- a/src/pages/onboarding/Onboarding.tsx +++ b/src/pages/onboarding/Onboarding.tsx @@ -12,7 +12,7 @@ import { PageTitle } from "@/pages/parts/util/PageTitle"; export function OnboardingPage() { const navigate = useNavigate(); const skipModal = useModal("skip"); - const { skipAndRedirect } = useRedirectBack(); + const { completeAndRedirect } = useRedirectBack(); return ( @@ -25,7 +25,7 @@ export function OnboardingPage() { - diff --git a/src/pages/onboarding/OnboardingExtension.tsx b/src/pages/onboarding/OnboardingExtension.tsx index 8ee85be7..c3fa5380 100644 --- a/src/pages/onboarding/OnboardingExtension.tsx +++ b/src/pages/onboarding/OnboardingExtension.tsx @@ -1,14 +1,64 @@ +import { ReactNode } from "react"; import { useNavigate } from "react-router-dom"; +import { useAsyncFn, useInterval } from "react-use"; +import { isAllowedExtensionVersion } from "@/backend/extension/compatibility"; +import { extensionInfo } from "@/backend/extension/messaging"; import { Button } from "@/components/buttons/Button"; import { Stepper } from "@/components/layout/Stepper"; import { CenterContainer } from "@/components/layout/ThinContainer"; import { Heading2, Paragraph } from "@/components/utils/Text"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; +import { useRedirectBack } from "@/pages/onboarding/onboardingHooks"; import { PageTitle } from "@/pages/parts/util/PageTitle"; +type ExtensionStatus = + | "unknown" + | "failed" + | "disallowed" + | "noperms" + | "outdated" + | "success"; + +async function getExtensionState(): Promise { + const info = await extensionInfo(); + if (!info) return "unknown"; // cant talk to extension + if (!info.success) return "failed"; // extension failed to respond + if (!info.allowed) return "disallowed"; // extension is not enabled on this page + if (!info.hasPermission) return "noperms"; // extension has no perms to do it's tasks + if (!isAllowedExtensionVersion(info.version)) return "outdated"; // extension is too old + return "success"; // no problems +} + +export function ExtensionStatus(props: { + status: ExtensionStatus; + loading: boolean; +}) { + let content: ReactNode = null; + if (props.loading || props.status === "unknown") + content =

waiting on extension...

; + if (props.status === "disallowed") + content =

Extension disabled for this page

; + else if (props.status === "failed") content =

Failed to request status

; + else if (props.status === "outdated") content =

Extension too old

; + else if (props.status === "noperms") content =

No permissions to act

; + else if (props.status === "success") content =

Extension is working!

; + return
{content}
; +} + export function OnboardingExtensionPage() { const navigate = useNavigate(); + const { completeAndRedirect } = useRedirectBack(); + + const [{ loading, value }, exec] = useAsyncFn( + async (triggeredManually: boolean = false) => { + const status = await getExtensionState(); + if (status === "success" && triggeredManually) completeAndRedirect(); + return status; + }, + [completeAndRedirect], + ); + useInterval(exec, 1000); return ( @@ -17,9 +67,10 @@ export function OnboardingExtensionPage() { Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum + - diff --git a/src/pages/onboarding/OnboardingProxy.tsx b/src/pages/onboarding/OnboardingProxy.tsx index 45d22aea..0cb2b38b 100644 --- a/src/pages/onboarding/OnboardingProxy.tsx +++ b/src/pages/onboarding/OnboardingProxy.tsx @@ -1,14 +1,33 @@ +import { useState } from "react"; import { useNavigate } from "react-router-dom"; +import { useAsyncFn } from "react-use"; +import { singularProxiedFetch } from "@/backend/helpers/fetch"; import { Button } from "@/components/buttons/Button"; import { Stepper } from "@/components/layout/Stepper"; import { CenterContainer } from "@/components/layout/ThinContainer"; +import { AuthInputBox } from "@/components/text-inputs/AuthInputBox"; import { Heading2, Paragraph } from "@/components/utils/Text"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; +import { useRedirectBack } from "@/pages/onboarding/onboardingHooks"; import { PageTitle } from "@/pages/parts/util/PageTitle"; +import { useAuthStore } from "@/stores/auth"; + +const testUrl = "https://postman-echo.com/get"; export function OnboardingProxyPage() { const navigate = useNavigate(); + const { completeAndRedirect } = useRedirectBack(); + const [url, setUrl] = useState(""); + const setProxySet = useAuthStore((s) => s.setProxySet); + + const [{ loading, error }, test] = useAsyncFn(async () => { + if (!url.startsWith("http")) throw new Error("Not a valid URL"); + const res = await singularProxiedFetch(url, testUrl, {}); + if (res.url !== testUrl) throw new Error("Not a proxy"); + setProxySet([url]); + completeAndRedirect(); + }, [url, completeAndRedirect, setProxySet]); return ( @@ -17,10 +36,12 @@ export function OnboardingProxyPage() { Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum - - + ); diff --git a/src/pages/onboarding/onboardingHooks.ts b/src/pages/onboarding/onboardingHooks.ts index e80234e7..bcbbea48 100644 --- a/src/pages/onboarding/onboardingHooks.ts +++ b/src/pages/onboarding/onboardingHooks.ts @@ -7,16 +7,16 @@ import { useOnboardingStore } from "@/stores/onboarding"; export function useRedirectBack() { const [url] = useQueryParam("redirect"); const navigate = useNavigate(); - const setSkipped = useOnboardingStore((s) => s.setSkipped); + const setCompleted = useOnboardingStore((s) => s.setCompleted); const redirectBack = useCallback(() => { navigate(url ?? "/"); }, [navigate, url]); - const skipAndRedirect = useCallback(() => { - setSkipped(true); + const completeAndRedirect = useCallback(() => { + setCompleted(true); redirectBack(); - }, [redirectBack, setSkipped]); + }, [redirectBack, setCompleted]); - return { redirectBack, skipAndRedirect }; + return { completeAndRedirect }; } diff --git a/src/stores/onboarding/index.tsx b/src/stores/onboarding/index.tsx index 17d1965d..be5af563 100644 --- a/src/stores/onboarding/index.tsx +++ b/src/stores/onboarding/index.tsx @@ -3,17 +3,17 @@ import { persist } from "zustand/middleware"; import { immer } from "zustand/middleware/immer"; export interface OnboardingStore { - skipped: boolean; - setSkipped(v: boolean): void; + completed: boolean; + setCompleted(v: boolean): void; } export const useOnboardingStore = create( persist( immer((set) => ({ - skipped: false, - setSkipped(v) { + completed: false, + setCompleted(v) { set((s) => { - s.skipped = v; + s.completed = v; }); }, })), diff --git a/src/utils/onboarding.ts b/src/utils/onboarding.ts index 7289fd1c..c2678b1c 100644 --- a/src/utils/onboarding.ts +++ b/src/utils/onboarding.ts @@ -15,9 +15,9 @@ export async function needsOnboarding(): Promise { const proxyUrls = useAuthStore.getState().proxySet; if (proxyUrls) return false; - // if onboarding has been skipped, no onboarding needed - const skipped = useOnboardingStore.getState().skipped; - if (skipped) return false; + // if onboarding has been completed, no onboarding needed + const completed = useOnboardingStore.getState().completed; + if (completed) return false; return true; }