mirror of
https://github.com/sussy-code/smov.git
synced 2026-01-11 20:10:16 +00:00
Start adding migration pages
This commit is contained in:
parent
4b27aa457f
commit
718acfd76a
6 changed files with 265 additions and 0 deletions
|
|
@ -112,6 +112,7 @@
|
|||
"pagetitle": "{{title}} - sudo-flix",
|
||||
"register": "Register",
|
||||
"settings": "Settings",
|
||||
"migration": "Migration",
|
||||
"jip": "Jip"
|
||||
}
|
||||
},
|
||||
|
|
@ -197,6 +198,27 @@
|
|||
"show": "Show"
|
||||
}
|
||||
},
|
||||
"migration": {
|
||||
"start": {
|
||||
"title": "Migrate your data",
|
||||
"explainer": "If you wish to migrate or backup your data, you can do so using the options below. This will allow you to keep your data when you switch backend servers.",
|
||||
"options": {
|
||||
"or": "or",
|
||||
"direct": {
|
||||
"description": "This will directly migrate your data to the new server. This is the fastest option. <br /><br />This option allows you to keep your passphrase the same!",
|
||||
"title": "Direct migration",
|
||||
"quality": "Easiest and fastest",
|
||||
"action": "Transfer data"
|
||||
},
|
||||
"download": {
|
||||
"description": "This will download your data to your device. You can then upload it to the new server or just keep it for safekeeping.",
|
||||
"title": "Download data",
|
||||
"quality": "More technical",
|
||||
"action": "Download data"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"banner": {
|
||||
"offline": "Check your internet connection, silly goose! 🦢"
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ export enum Icons {
|
|||
BRUSH = "brush",
|
||||
UPLOAD = "upload",
|
||||
WEB = "web",
|
||||
CLOUD_ARROW_UP = "cloud_arrow_up",
|
||||
FILE_ARROW_DOWN = "file_arrow_down",
|
||||
}
|
||||
|
||||
export interface IconProps {
|
||||
|
|
@ -153,6 +155,8 @@ const iconList: Record<Icons, string> = {
|
|||
<path d="M22.0182 15.0781C20.9582 15.403 18.7915 16.0311 16.4781 16.4781C16.0311 18.7915 15.403 20.9581 15.0781 22.0182L15.0702 22.044C18.4002 21.0274 21.0274 18.4002 22.044 15.0702L22.0182 15.0781Z" fill="currentColor"/>
|
||||
<path d="M1.6103 13.323C1.64665 13.3277 1.67628 13.3327 1.68611 13.3349C1.69472 13.337 1.70821 13.3406 1.7131 13.3419L1.72391 13.345L1.72973 13.3468L1.73585 13.3487L1.74098 13.3503C1.7381 13.3494 1.67976 13.3348 1.6103 13.323Z" fill="currentColor"/>
|
||||
</svg>`,
|
||||
cloud_arrow_up: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128H144zm79-217c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l39-39V392c0 13.3 10.7 24 24 24s24-10.7 24-24V257.9l39 39c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0l-80 80z" fill="currentColor"/></svg>`,
|
||||
file_arrow_down: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM216 232V334.1l31-31c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-72 72c-9.4 9.4-24.6 9.4-33.9 0l-72-72c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l31 31V232c0-13.3 10.7-24 24-24s24 10.7 24 24z" fill="currentColor"/></svg>`,
|
||||
};
|
||||
|
||||
function ChromeCastButton() {
|
||||
|
|
|
|||
65
src/pages/migration/Migration.tsx
Normal file
65
src/pages/migration/Migration.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { Stepper } from "@/components/layout/Stepper";
|
||||
import { CenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { VerticalLine } from "@/components/layout/VerticalLine";
|
||||
import { Heading2, Paragraph } from "@/components/utils/Text";
|
||||
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
|
||||
import { Card, CardContent, Link } from "@/pages/migration/utils";
|
||||
import { PageTitle } from "@/pages/parts/util/PageTitle";
|
||||
|
||||
export function MigrationPage() {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MinimalPageLayout>
|
||||
<PageTitle subpage k="global.pages.migration" />
|
||||
<CenterContainer>
|
||||
<Stepper steps={2} current={1} className="mb-12" />
|
||||
<Heading2 className="!mt-0 !text-3xl max-w-[435px]">
|
||||
{t("migration.start.title")}
|
||||
</Heading2>
|
||||
<Paragraph className="max-w-[320px]">
|
||||
{t("migration.start.explainer")}
|
||||
</Paragraph>
|
||||
|
||||
<div className="w-full flex flex-col md:flex-row gap-3">
|
||||
<Card onClick={() => navigate("/migration/direct")}>
|
||||
<CardContent
|
||||
colorClass="!text-onboarding-best"
|
||||
title={t("migration.start.options.direct.title")}
|
||||
subtitle={t("migration.start.options.direct.quality")}
|
||||
description={
|
||||
<Trans i18nKey="migration.start.options.direct.description" />
|
||||
}
|
||||
icon={Icons.CLOUD_ARROW_UP}
|
||||
>
|
||||
<Link>{t("migration.start.options.direct.action")}</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="hidden md:grid grid-rows-[1fr,auto,1fr] justify-center gap-4">
|
||||
<VerticalLine className="items-end" />
|
||||
<span className="text-xs uppercase font-bold">
|
||||
{t("migration.start.options.or")}
|
||||
</span>
|
||||
<VerticalLine />
|
||||
</div>
|
||||
<Card onClick={() => navigate("/migration/download")}>
|
||||
<CardContent
|
||||
colorClass="!text-migration-good"
|
||||
title={t("migration.start.options.download.title")}
|
||||
subtitle={t("migration.start.options.download.quality")}
|
||||
description={t("migration.start.options.download.description")}
|
||||
icon={Icons.FILE_ARROW_DOWN}
|
||||
>
|
||||
<Link>{t("migration.start.options.download.action")}</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CenterContainer>
|
||||
</MinimalPageLayout>
|
||||
);
|
||||
}
|
||||
12
src/pages/migration/MigrationDirect.tsx
Normal file
12
src/pages/migration/MigrationDirect.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { CenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
|
||||
import { PageTitle } from "@/pages/parts/util/PageTitle";
|
||||
|
||||
export function MigrationDirectPage() {
|
||||
return (
|
||||
<MinimalPageLayout>
|
||||
<PageTitle subpage k="global.pages.migration" />
|
||||
<CenterContainer>Hi</CenterContainer>
|
||||
</MinimalPageLayout>
|
||||
);
|
||||
}
|
||||
92
src/pages/migration/utils.tsx
Normal file
92
src/pages/migration/utils.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import classNames from "classnames";
|
||||
import { ReactNode } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Heading2, Heading3, Paragraph } from "@/components/utils/Text";
|
||||
|
||||
export function Card(props: {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
"bg-onboarding-card duration-300 border border-onboarding-border rounded-lg p-7":
|
||||
true,
|
||||
"hover:bg-onboarding-cardHover transition-colors cursor-pointer":
|
||||
!!props.onClick,
|
||||
},
|
||||
props.className,
|
||||
)}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function CardContent(props: {
|
||||
title: ReactNode;
|
||||
description: ReactNode;
|
||||
subtitle: ReactNode;
|
||||
colorClass: string;
|
||||
children?: React.ReactNode;
|
||||
icon: Icons;
|
||||
}) {
|
||||
return (
|
||||
<div className="grid grid-rows-[1fr,auto] h-full">
|
||||
<div>
|
||||
<Icon
|
||||
icon={props.icon}
|
||||
className={classNames("text-4xl mb-8 block", props.colorClass)}
|
||||
/>
|
||||
<Heading3
|
||||
className={classNames(
|
||||
"!mt-0 !mb-0 !text-xs uppercase",
|
||||
props.colorClass,
|
||||
)}
|
||||
>
|
||||
{props.subtitle}
|
||||
</Heading3>
|
||||
<Heading2 className="!mb-0 !mt-1 !text-base">{props.title}</Heading2>
|
||||
<Paragraph className="max-w-[320px] !my-4">
|
||||
{props.description}
|
||||
</Paragraph>
|
||||
</div>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Link(props: {
|
||||
children?: React.ReactNode;
|
||||
to?: string;
|
||||
href?: string;
|
||||
className?: string;
|
||||
target?: "_blank";
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<a
|
||||
onClick={() => {
|
||||
if (props.to) navigate(props.to);
|
||||
}}
|
||||
href={props.href}
|
||||
target={props.target}
|
||||
className={classNames(
|
||||
"text-onboarding-link cursor-pointer inline-flex gap-2 items-center group hover:opacity-75 transition-opacity",
|
||||
props.className,
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{props.children}
|
||||
<Icon
|
||||
icon={Icons.ARROW_RIGHT}
|
||||
className="group-hover:translate-x-0.5 transition-transform text-xl group-active:translate-x-0"
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
@ -22,6 +22,8 @@ import { NotFoundPage } from "@/pages/errors/NotFoundPage";
|
|||
import { HomePage } from "@/pages/HomePage";
|
||||
import { JipPage } from "@/pages/Jip";
|
||||
import { LoginPage } from "@/pages/Login";
|
||||
import { MigrationPage } from "@/pages/migration/Migration";
|
||||
import { MigrationDirectPage } from "@/pages/migration/MigrationDirect";
|
||||
import { OnboardingPage } from "@/pages/onboarding/Onboarding";
|
||||
import { OnboardingExtensionPage } from "@/pages/onboarding/OnboardingExtension";
|
||||
import { OnboardingProxyPage } from "@/pages/onboarding/OnboardingProxy";
|
||||
|
|
@ -178,6 +180,74 @@ function App() {
|
|||
{showDowntime && (
|
||||
<MaintenancePage onHomeButtonClick={handleButtonClick} />
|
||||
)}
|
||||
<Routes>
|
||||
{/* functional routes */}
|
||||
<Route path="/s/:query" element={<QuickSearch />} />
|
||||
<Route path="/search/:type" element={<Navigate to="/browse" />} />
|
||||
<Route path="/search/:type/:query?" element={<QueryView />} />
|
||||
|
||||
{/* pages */}
|
||||
<Route
|
||||
path="/media/:media"
|
||||
element={
|
||||
<LegacyUrlView>
|
||||
<Suspense fallback={null}>
|
||||
<PlayerView />
|
||||
</Suspense>
|
||||
</LegacyUrlView>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/media/:media/:season/:episode"
|
||||
element={
|
||||
<LegacyUrlView>
|
||||
<Suspense fallback={null}>
|
||||
<PlayerView />
|
||||
</Suspense>
|
||||
</LegacyUrlView>
|
||||
}
|
||||
/>
|
||||
<Route path="/browse/:query?" element={<HomePage />} />
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/onboarding" element={<OnboardingPage />} />
|
||||
<Route
|
||||
path="/onboarding/extension"
|
||||
element={<OnboardingExtensionPage />}
|
||||
/>
|
||||
<Route path="/onboarding/proxy" element={<OnboardingProxyPage />} />
|
||||
|
||||
<Route path="/migration" element={<MigrationPage />} />
|
||||
<Route path="/migration/direct" element={<MigrationDirectPage />} />
|
||||
|
||||
{shouldHaveDmcaPage() ? (
|
||||
<Route path="/dmca" element={<DmcaPage />} />
|
||||
) : null}
|
||||
|
||||
{/* Settings page */}
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<Suspense fallback={null}>
|
||||
<SettingsPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* admin routes */}
|
||||
<Route path="/admin" element={<AdminPage />} />
|
||||
|
||||
{/* other */}
|
||||
<Route path="/dev" element={<DeveloperPage />} />
|
||||
<Route path="/dev/video" element={<VideoTesterView />} />
|
||||
{/* developer routes that can abuse workers are disabled in production */}
|
||||
{process.env.NODE_ENV === "development" ? (
|
||||
<Route path="/dev/test" element={<TestView />} />
|
||||
) : null}
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue