1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-30 16:17:41 +01:00

Add onboarding functionality

This commit is contained in:
mrjvs 2024-01-16 22:07:21 +01:00
parent 925f3dff19
commit a226f3347c
6 changed files with 92 additions and 20 deletions

View file

@ -12,7 +12,7 @@ import { PageTitle } from "@/pages/parts/util/PageTitle";
export function OnboardingPage() { export function OnboardingPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const skipModal = useModal("skip"); const skipModal = useModal("skip");
const { skipAndRedirect } = useRedirectBack(); const { completeAndRedirect } = useRedirectBack();
return ( return (
<MinimalPageLayout> <MinimalPageLayout>
@ -25,7 +25,7 @@ export function OnboardingPage() {
<Button theme="secondary" onClick={skipModal.hide}> <Button theme="secondary" onClick={skipModal.hide}>
Lorem ipsum Lorem ipsum
</Button> </Button>
<Button theme="danger" onClick={() => skipAndRedirect()}> <Button theme="danger" onClick={() => completeAndRedirect()}>
Lorem ipsum Lorem ipsum
</Button> </Button>
</ModalCard> </ModalCard>

View file

@ -1,14 +1,64 @@
import { ReactNode } from "react";
import { useNavigate } from "react-router-dom"; 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 { Button } from "@/components/buttons/Button";
import { Stepper } from "@/components/layout/Stepper"; import { Stepper } from "@/components/layout/Stepper";
import { CenterContainer } from "@/components/layout/ThinContainer"; import { CenterContainer } from "@/components/layout/ThinContainer";
import { Heading2, Paragraph } from "@/components/utils/Text"; import { Heading2, Paragraph } from "@/components/utils/Text";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
import { useRedirectBack } from "@/pages/onboarding/onboardingHooks";
import { PageTitle } from "@/pages/parts/util/PageTitle"; import { PageTitle } from "@/pages/parts/util/PageTitle";
type ExtensionStatus =
| "unknown"
| "failed"
| "disallowed"
| "noperms"
| "outdated"
| "success";
async function getExtensionState(): Promise<ExtensionStatus> {
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 = <p>waiting on extension...</p>;
if (props.status === "disallowed")
content = <p>Extension disabled for this page</p>;
else if (props.status === "failed") content = <p>Failed to request status</p>;
else if (props.status === "outdated") content = <p>Extension too old</p>;
else if (props.status === "noperms") content = <p>No permissions to act</p>;
else if (props.status === "success") content = <p>Extension is working!</p>;
return <div>{content}</div>;
}
export function OnboardingExtensionPage() { export function OnboardingExtensionPage() {
const navigate = useNavigate(); 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 ( return (
<MinimalPageLayout> <MinimalPageLayout>
@ -17,9 +67,10 @@ export function OnboardingExtensionPage() {
<Stepper steps={2} current={2} className="mb-12" /> <Stepper steps={2} current={2} className="mb-12" />
<Heading2 className="!mt-0">Lorem ipsum</Heading2> <Heading2 className="!mt-0">Lorem ipsum</Heading2>
<Paragraph>Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum</Paragraph> <Paragraph>Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum</Paragraph>
<ExtensionStatus status={value ?? "unknown"} loading={loading} />
<Button onClick={() => navigate("/onboarding")}>Back</Button> <Button onClick={() => navigate("/onboarding")}>Back</Button>
<Button onClick={() => alert("Check extension here or something")}> <Button onClick={() => exec(true)}>
Check extension {value === "success" ? "Continue" : "Check extension"}{" "}
</Button> </Button>
</CenterContainer> </CenterContainer>
</MinimalPageLayout> </MinimalPageLayout>

View file

@ -1,14 +1,33 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useAsyncFn } from "react-use";
import { singularProxiedFetch } from "@/backend/helpers/fetch";
import { Button } from "@/components/buttons/Button"; import { Button } from "@/components/buttons/Button";
import { Stepper } from "@/components/layout/Stepper"; import { Stepper } from "@/components/layout/Stepper";
import { CenterContainer } from "@/components/layout/ThinContainer"; import { CenterContainer } from "@/components/layout/ThinContainer";
import { AuthInputBox } from "@/components/text-inputs/AuthInputBox";
import { Heading2, Paragraph } from "@/components/utils/Text"; import { Heading2, Paragraph } from "@/components/utils/Text";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
import { useRedirectBack } from "@/pages/onboarding/onboardingHooks";
import { PageTitle } from "@/pages/parts/util/PageTitle"; import { PageTitle } from "@/pages/parts/util/PageTitle";
import { useAuthStore } from "@/stores/auth";
const testUrl = "https://postman-echo.com/get";
export function OnboardingProxyPage() { export function OnboardingProxyPage() {
const navigate = useNavigate(); 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 ( return (
<MinimalPageLayout> <MinimalPageLayout>
@ -17,10 +36,12 @@ export function OnboardingProxyPage() {
<Stepper steps={2} current={2} className="mb-12" /> <Stepper steps={2} current={2} className="mb-12" />
<Heading2 className="!mt-0">Lorem ipsum</Heading2> <Heading2 className="!mt-0">Lorem ipsum</Heading2>
<Paragraph>Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum</Paragraph> <Paragraph>Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum</Paragraph>
<Button onClick={() => navigate("/onboarding")}>Back</Button> <AuthInputBox value={url} onChange={setUrl} placeholder="lorem ipsum" />
<Button onClick={() => alert("Check proxy or smth")}> {error ? <p>url invalid</p> : null}
Check extension <Button onClick={() => navigate("/onboarding")} loading={loading}>
Backagd
</Button> </Button>
<Button onClick={test}>Submit proxy</Button>
</CenterContainer> </CenterContainer>
</MinimalPageLayout> </MinimalPageLayout>
); );

View file

@ -7,16 +7,16 @@ import { useOnboardingStore } from "@/stores/onboarding";
export function useRedirectBack() { export function useRedirectBack() {
const [url] = useQueryParam("redirect"); const [url] = useQueryParam("redirect");
const navigate = useNavigate(); const navigate = useNavigate();
const setSkipped = useOnboardingStore((s) => s.setSkipped); const setCompleted = useOnboardingStore((s) => s.setCompleted);
const redirectBack = useCallback(() => { const redirectBack = useCallback(() => {
navigate(url ?? "/"); navigate(url ?? "/");
}, [navigate, url]); }, [navigate, url]);
const skipAndRedirect = useCallback(() => { const completeAndRedirect = useCallback(() => {
setSkipped(true); setCompleted(true);
redirectBack(); redirectBack();
}, [redirectBack, setSkipped]); }, [redirectBack, setCompleted]);
return { redirectBack, skipAndRedirect }; return { completeAndRedirect };
} }

View file

@ -3,17 +3,17 @@ import { persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer"; import { immer } from "zustand/middleware/immer";
export interface OnboardingStore { export interface OnboardingStore {
skipped: boolean; completed: boolean;
setSkipped(v: boolean): void; setCompleted(v: boolean): void;
} }
export const useOnboardingStore = create( export const useOnboardingStore = create(
persist( persist(
immer<OnboardingStore>((set) => ({ immer<OnboardingStore>((set) => ({
skipped: false, completed: false,
setSkipped(v) { setCompleted(v) {
set((s) => { set((s) => {
s.skipped = v; s.completed = v;
}); });
}, },
})), })),

View file

@ -15,9 +15,9 @@ export async function needsOnboarding(): Promise<boolean> {
const proxyUrls = useAuthStore.getState().proxySet; const proxyUrls = useAuthStore.getState().proxySet;
if (proxyUrls) return false; if (proxyUrls) return false;
// if onboarding has been skipped, no onboarding needed // if onboarding has been completed, no onboarding needed
const skipped = useOnboardingStore.getState().skipped; const completed = useOnboardingStore.getState().completed;
if (skipped) return false; if (completed) return false;
return true; return true;
} }