mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
Style the settings onboarding card
This commit is contained in:
parent
de5d47a730
commit
f96a0de373
4 changed files with 145 additions and 33 deletions
|
@ -2,7 +2,10 @@ import classNames from "classnames";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { StatusCircle } from "@/components/player/internals/StatusCircle";
|
import {
|
||||||
|
StatusCircle,
|
||||||
|
StatusCircleProps,
|
||||||
|
} from "@/components/player/internals/StatusCircle";
|
||||||
import { Transition } from "@/components/utils/Transition";
|
import { Transition } from "@/components/utils/Transition";
|
||||||
|
|
||||||
export interface ScrapeItemProps {
|
export interface ScrapeItemProps {
|
||||||
|
@ -23,13 +26,14 @@ const statusTextMap: Partial<Record<ScrapeCardProps["status"], string>> = {
|
||||||
pending: "player.scraping.items.pending",
|
pending: "player.scraping.items.pending",
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusMap: Record<ScrapeCardProps["status"], StatusCircle["type"]> = {
|
const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> =
|
||||||
failure: "error",
|
{
|
||||||
notfound: "noresult",
|
failure: "error",
|
||||||
pending: "loading",
|
notfound: "noresult",
|
||||||
success: "success",
|
pending: "loading",
|
||||||
waiting: "waiting",
|
success: "success",
|
||||||
};
|
waiting: "waiting",
|
||||||
|
};
|
||||||
|
|
||||||
export function ScrapeItem(props: ScrapeItemProps) {
|
export function ScrapeItem(props: ScrapeItemProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
|
@ -4,23 +4,24 @@ import classNames from "classnames";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { Transition } from "@/components/utils/Transition";
|
import { Transition } from "@/components/utils/Transition";
|
||||||
|
|
||||||
export interface StatusCircle {
|
export interface StatusCircleProps {
|
||||||
type: "loading" | "success" | "error" | "noresult" | "waiting";
|
type: "loading" | "success" | "error" | "noresult" | "waiting";
|
||||||
percentage?: number;
|
percentage?: number;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusCircleLoading extends StatusCircle {
|
export interface StatusCircleLoading extends StatusCircleProps {
|
||||||
type: "loading";
|
type: "loading";
|
||||||
percentage: number;
|
percentage: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusIsLoading(
|
function statusIsLoading(
|
||||||
props: StatusCircle | StatusCircleLoading,
|
props: StatusCircleProps | StatusCircleLoading,
|
||||||
): props is StatusCircleLoading {
|
): props is StatusCircleLoading {
|
||||||
return props.type === "loading";
|
return props.type === "loading";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
export function StatusCircle(props: StatusCircleProps | StatusCircleLoading) {
|
||||||
const [spring] = useSpring(
|
const [spring] = useSpring(
|
||||||
() => ({
|
() => ({
|
||||||
percentage: statusIsLoading(props) ? props.percentage : 0,
|
percentage: statusIsLoading(props) ? props.percentage : 0,
|
||||||
|
@ -30,18 +31,21 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames(
|
||||||
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
|
{
|
||||||
true,
|
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
|
||||||
"text-video-scraping-loading": props.type === "loading",
|
true,
|
||||||
"text-video-scraping-noresult text-opacity-50":
|
"text-video-scraping-loading": props.type === "loading",
|
||||||
props.type === "waiting",
|
"text-video-scraping-noresult text-opacity-50":
|
||||||
"text-video-scraping-error bg-video-scraping-error":
|
props.type === "waiting",
|
||||||
props.type === "error",
|
"text-video-scraping-error bg-video-scraping-error":
|
||||||
"text-green-500 bg-green-500": props.type === "success",
|
props.type === "error",
|
||||||
"text-video-scraping-noresult bg-video-scraping-noresult":
|
"text-green-500 bg-green-500": props.type === "success",
|
||||||
props.type === "noresult",
|
"text-video-scraping-noresult bg-video-scraping-noresult":
|
||||||
})}
|
props.type === "noresult",
|
||||||
|
},
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Transition animation="fade" show={statusIsLoading(props)}>
|
<Transition animation="fade" show={statusIsLoading(props)}>
|
||||||
<svg
|
<svg
|
||||||
|
@ -65,13 +69,13 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
||||||
</Transition>
|
</Transition>
|
||||||
<Transition animation="fade" show={props.type === "error"}>
|
<Transition animation="fade" show={props.type === "error"}>
|
||||||
<Icon
|
<Icon
|
||||||
className="absolute inset-0 flex items-center justify-center text-white"
|
className="absolute inset-0 flex items-center justify-center text-background-main"
|
||||||
icon={Icons.X}
|
icon={Icons.X}
|
||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
<Transition animation="fade" show={props.type === "success"}>
|
<Transition animation="fade" show={props.type === "success"}>
|
||||||
<Icon
|
<Icon
|
||||||
className="absolute inset-0 flex items-center text-xs justify-center text-white"
|
className="absolute inset-0 flex items-center text-sm justify-center text-background-main"
|
||||||
icon={Icons.CHECKMARK}
|
icon={Icons.CHECKMARK}
|
||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import { ReactNode } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useAsync } from "react-use";
|
import { useAsync } from "react-use";
|
||||||
|
|
||||||
import { isExtensionActive } from "@/backend/extension/messaging";
|
import { isExtensionActive } from "@/backend/extension/messaging";
|
||||||
import { singularProxiedFetch } from "@/backend/helpers/fetch";
|
import { singularProxiedFetch } from "@/backend/helpers/fetch";
|
||||||
import { Button } from "@/components/buttons/Button";
|
import { Button } from "@/components/buttons/Button";
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { SettingsCard } from "@/components/layout/SettingsCard";
|
||||||
|
import {
|
||||||
|
StatusCircle,
|
||||||
|
StatusCircleProps,
|
||||||
|
} from "@/components/player/internals/StatusCircle";
|
||||||
|
import { Heading3 } from "@/components/utils/Text";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
const testUrl = "https://postman-echo.com/get";
|
const testUrl = "https://postman-echo.com/get";
|
||||||
|
@ -63,17 +73,110 @@ function useIsSetup() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SetupCheckList(props: {
|
||||||
|
status: Status;
|
||||||
|
grey?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
}) {
|
||||||
|
const statusMap: Record<Status, StatusCircleProps["type"]> = {
|
||||||
|
error: "error",
|
||||||
|
success: "success",
|
||||||
|
unset: "noresult",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-start text-type-dimmed my-4">
|
||||||
|
<StatusCircle
|
||||||
|
type={statusMap[props.status]}
|
||||||
|
className={classNames({
|
||||||
|
"!text-video-scraping-noresult !bg-video-scraping-noresult opacity-50":
|
||||||
|
props.grey,
|
||||||
|
"scale-90 mr-3": true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p
|
||||||
|
className={classNames({
|
||||||
|
"!text-type-dimmed opacity-75": props.grey,
|
||||||
|
"text-type-danger": props.status === "error",
|
||||||
|
"text-white": props.status === "success",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</p>
|
||||||
|
{props.status === "error" ? (
|
||||||
|
<p className="max-w-96">
|
||||||
|
There is something wrong with this setting. Go through setup again
|
||||||
|
to fix it.
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function SetupPart() {
|
export function SetupPart() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { loading, setupStates, globalState } = useIsSetup();
|
const { loading, setupStates, globalState } = useIsSetup();
|
||||||
if (loading || !setupStates) return <p>Loading states...</p>;
|
if (loading || !setupStates) return <p>Loading states...</p>;
|
||||||
|
|
||||||
|
const textLookupMap: Record<Status, { title: string; desc: string }> = {
|
||||||
|
error: {
|
||||||
|
title: "err1",
|
||||||
|
desc: "err2",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
title: "success1",
|
||||||
|
desc: "success2",
|
||||||
|
},
|
||||||
|
unset: {
|
||||||
|
title: "unset1",
|
||||||
|
desc: "unset2",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<SettingsCard>
|
||||||
<p className="font-bold text-white">state: {globalState}</p>
|
<div className="flex items-start gap-4">
|
||||||
<p>extension: {setupStates.extension}</p>
|
<div>
|
||||||
<p>proxy: {setupStates.proxy}</p>
|
<div
|
||||||
<p>defaults: {setupStates.defaultProxy}</p>
|
className={classNames({
|
||||||
<Button onClick={() => navigate("/onboarding")}>Do setup</Button>
|
"rounded-full h-12 w-12 flex bg-opacity-15 justify-center items-center":
|
||||||
</div>
|
true,
|
||||||
|
"text-type-success bg-type-success": globalState === "success",
|
||||||
|
"text-type-danger bg-type-danger":
|
||||||
|
globalState === "error" || globalState === "unset",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={globalState === "success" ? Icons.CHECKMARK : Icons.X}
|
||||||
|
className="text-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Heading3 className="!mb-3">
|
||||||
|
{t(textLookupMap[globalState].title)}
|
||||||
|
</Heading3>
|
||||||
|
<p className="max-w-[20rem] font-medium mb-6">
|
||||||
|
{t(textLookupMap[globalState].desc)}
|
||||||
|
</p>
|
||||||
|
<SetupCheckList status={setupStates.extension}>
|
||||||
|
Extension
|
||||||
|
</SetupCheckList>
|
||||||
|
<SetupCheckList status={setupStates.proxy}>
|
||||||
|
Custom proxy
|
||||||
|
</SetupCheckList>
|
||||||
|
<SetupCheckList grey status={setupStates.defaultProxy}>
|
||||||
|
Default setup
|
||||||
|
</SetupCheckList>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<Button theme="purple" onClick={() => navigate("/onboarding")}>
|
||||||
|
Do setup
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SettingsCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ export const defaultTheme = {
|
||||||
divider: tokens.ash.c500,
|
divider: tokens.ash.c500,
|
||||||
secondary: tokens.ash.c100,
|
secondary: tokens.ash.c100,
|
||||||
danger: tokens.semantic.red.c100,
|
danger: tokens.semantic.red.c100,
|
||||||
|
success: tokens.semantic.green.c100,
|
||||||
link: tokens.purple.c100,
|
link: tokens.purple.c100,
|
||||||
linkHover: tokens.purple.c50,
|
linkHover: tokens.purple.c50,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue