mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +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 { 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";
|
||||
|
||||
export interface ScrapeItemProps {
|
||||
|
@ -23,13 +26,14 @@ const statusTextMap: Partial<Record<ScrapeCardProps["status"], string>> = {
|
|||
pending: "player.scraping.items.pending",
|
||||
};
|
||||
|
||||
const statusMap: Record<ScrapeCardProps["status"], StatusCircle["type"]> = {
|
||||
failure: "error",
|
||||
notfound: "noresult",
|
||||
pending: "loading",
|
||||
success: "success",
|
||||
waiting: "waiting",
|
||||
};
|
||||
const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> =
|
||||
{
|
||||
failure: "error",
|
||||
notfound: "noresult",
|
||||
pending: "loading",
|
||||
success: "success",
|
||||
waiting: "waiting",
|
||||
};
|
||||
|
||||
export function ScrapeItem(props: ScrapeItemProps) {
|
||||
const { t } = useTranslation();
|
||||
|
|
|
@ -4,23 +4,24 @@ import classNames from "classnames";
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Transition } from "@/components/utils/Transition";
|
||||
|
||||
export interface StatusCircle {
|
||||
export interface StatusCircleProps {
|
||||
type: "loading" | "success" | "error" | "noresult" | "waiting";
|
||||
percentage?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface StatusCircleLoading extends StatusCircle {
|
||||
export interface StatusCircleLoading extends StatusCircleProps {
|
||||
type: "loading";
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
function statusIsLoading(
|
||||
props: StatusCircle | StatusCircleLoading,
|
||||
props: StatusCircleProps | StatusCircleLoading,
|
||||
): props is StatusCircleLoading {
|
||||
return props.type === "loading";
|
||||
}
|
||||
|
||||
export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
||||
export function StatusCircle(props: StatusCircleProps | StatusCircleLoading) {
|
||||
const [spring] = useSpring(
|
||||
() => ({
|
||||
percentage: statusIsLoading(props) ? props.percentage : 0,
|
||||
|
@ -30,18 +31,21 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
|
||||
true,
|
||||
"text-video-scraping-loading": props.type === "loading",
|
||||
"text-video-scraping-noresult text-opacity-50":
|
||||
props.type === "waiting",
|
||||
"text-video-scraping-error bg-video-scraping-error":
|
||||
props.type === "error",
|
||||
"text-green-500 bg-green-500": props.type === "success",
|
||||
"text-video-scraping-noresult bg-video-scraping-noresult":
|
||||
props.type === "noresult",
|
||||
})}
|
||||
className={classNames(
|
||||
{
|
||||
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
|
||||
true,
|
||||
"text-video-scraping-loading": props.type === "loading",
|
||||
"text-video-scraping-noresult text-opacity-50":
|
||||
props.type === "waiting",
|
||||
"text-video-scraping-error bg-video-scraping-error":
|
||||
props.type === "error",
|
||||
"text-green-500 bg-green-500": props.type === "success",
|
||||
"text-video-scraping-noresult bg-video-scraping-noresult":
|
||||
props.type === "noresult",
|
||||
},
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<Transition animation="fade" show={statusIsLoading(props)}>
|
||||
<svg
|
||||
|
@ -65,13 +69,13 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
|
|||
</Transition>
|
||||
<Transition animation="fade" show={props.type === "error"}>
|
||||
<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}
|
||||
/>
|
||||
</Transition>
|
||||
<Transition animation="fade" show={props.type === "success"}>
|
||||
<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}
|
||||
/>
|
||||
</Transition>
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import classNames from "classnames";
|
||||
import { t } from "i18next";
|
||||
import { ReactNode } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAsync } from "react-use";
|
||||
|
||||
import { isExtensionActive } from "@/backend/extension/messaging";
|
||||
import { singularProxiedFetch } from "@/backend/helpers/fetch";
|
||||
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";
|
||||
|
||||
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() {
|
||||
const navigate = useNavigate();
|
||||
const { loading, setupStates, globalState } = useIsSetup();
|
||||
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 (
|
||||
<div>
|
||||
<p className="font-bold text-white">state: {globalState}</p>
|
||||
<p>extension: {setupStates.extension}</p>
|
||||
<p>proxy: {setupStates.proxy}</p>
|
||||
<p>defaults: {setupStates.defaultProxy}</p>
|
||||
<Button onClick={() => navigate("/onboarding")}>Do setup</Button>
|
||||
</div>
|
||||
<SettingsCard>
|
||||
<div className="flex items-start gap-4">
|
||||
<div>
|
||||
<div
|
||||
className={classNames({
|
||||
"rounded-full h-12 w-12 flex bg-opacity-15 justify-center items-center":
|
||||
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,
|
||||
secondary: tokens.ash.c100,
|
||||
danger: tokens.semantic.red.c100,
|
||||
success: tokens.semantic.green.c100,
|
||||
link: tokens.purple.c100,
|
||||
linkHover: tokens.purple.c50,
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue