mirror of
https://github.com/p-stream/p-stream.git
synced 2026-01-11 20:10:32 +00:00
Update migration
todo finish upload feat
This commit is contained in:
parent
3bbfbc0e69
commit
4039ae8f68
6 changed files with 923 additions and 717 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,10 @@
|
|||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Stepper } from "@/components/layout/Stepper";
|
||||
import { CenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { BiggerCenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { VerticalLine } from "@/components/layout/VerticalLine";
|
||||
import { Heading2, Paragraph } from "@/components/utils/Text";
|
||||
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
|
||||
|
|
@ -17,17 +18,20 @@ export function MigrationPage() {
|
|||
return (
|
||||
<MinimalPageLayout>
|
||||
<PageTitle subpage k="global.pages.migration" />
|
||||
<CenterContainer>
|
||||
<BiggerCenterContainer>
|
||||
<Stepper steps={2} current={1} className="mb-12" />
|
||||
<Heading2 className="!mt-0 !text-3xl max-w-[435px]">
|
||||
<Heading2 className="!mt-0 !text-3xl">
|
||||
{t("migration.start.title")}
|
||||
</Heading2>
|
||||
<Paragraph className="max-w-[320px]">
|
||||
<Paragraph className="max-w-[360px]">
|
||||
{t("migration.start.explainer")}
|
||||
</Paragraph>
|
||||
|
||||
<div className="w-full flex flex-col md:flex-row gap-3">
|
||||
<Card onClick={() => navigate("/migration/direct")}>
|
||||
<Card
|
||||
onClick={() => navigate("/migration/direct")}
|
||||
className="flex-1"
|
||||
>
|
||||
<CardContent
|
||||
colorClass="!text-onboarding-best"
|
||||
title={t("migration.start.options.direct.title")}
|
||||
|
|
@ -47,7 +51,10 @@ export function MigrationPage() {
|
|||
</span>
|
||||
<VerticalLine />
|
||||
</div>
|
||||
<Card onClick={() => navigate("/migration/download")}>
|
||||
<Card
|
||||
onClick={() => navigate("/migration/download")}
|
||||
className="flex-1"
|
||||
>
|
||||
<CardContent
|
||||
colorClass="!text-migration-good"
|
||||
title={t("migration.start.options.download.title")}
|
||||
|
|
@ -59,7 +66,7 @@ export function MigrationPage() {
|
|||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CenterContainer>
|
||||
</BiggerCenterContainer>
|
||||
</MinimalPageLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useNavigate } from "react-router-dom";
|
|||
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
import { SettingsCard } from "@/components/layout/SettingsCard";
|
||||
import { Stepper } from "@/components/layout/Stepper";
|
||||
import { CenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { AuthInputBox } from "@/components/text-inputs/AuthInputBox";
|
||||
import { Divider } from "@/components/utils/Divider";
|
||||
|
|
@ -66,6 +67,7 @@ export function MigrationDirectPage() {
|
|||
<CenterContainer>
|
||||
{user.account ? (
|
||||
<div>
|
||||
<Stepper steps={2} current={2} className="mb-12" />
|
||||
<Heading2 className="!text-4xl">
|
||||
{" "}
|
||||
{t("migration.direct.title")}
|
||||
|
|
@ -91,47 +93,50 @@ export function MigrationDirectPage() {
|
|||
</>
|
||||
)}
|
||||
</SettingsCard>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className="flex justify-between">
|
||||
<Button theme="secondary" onClick={() => navigate("/migration")}>
|
||||
{t("migration.back")}
|
||||
</Button>
|
||||
{status !== "success" && (
|
||||
<Button
|
||||
theme="purple"
|
||||
onClick={handleMigration}
|
||||
disabled={status === "processing"}
|
||||
>
|
||||
{status === "processing"
|
||||
? t("migration.direct.button.processing")
|
||||
: t("migration.direct.button.migrate")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className="text-center">
|
||||
{status !== "success" && (
|
||||
{status === "success" && (
|
||||
<div>
|
||||
<Button
|
||||
theme="purple"
|
||||
onClick={handleMigration}
|
||||
disabled={status === "processing"}
|
||||
className="mt-4"
|
||||
onClick={continueButton}
|
||||
>
|
||||
{status === "processing"
|
||||
? t("migration.direct.button.processing")
|
||||
: t("migration.direct.button.migrate")}
|
||||
{t("migration.direct.button.login")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{status === "success" && (
|
||||
<div>
|
||||
<Button
|
||||
theme="purple"
|
||||
className="mt-4"
|
||||
onClick={continueButton}
|
||||
>
|
||||
{t("migration.direct.button.login")}
|
||||
</Button>
|
||||
<p className="text-green-600 mt-4">
|
||||
{t("migration.direct.status.success")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "error" && (
|
||||
<p className="text-red-600 mt-4">
|
||||
{t("migration.direct.status.error")}
|
||||
<p className="text-green-600 mt-4">
|
||||
{t("migration.direct.status.success")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "error" && (
|
||||
<p className="text-red-600 mt-4">
|
||||
{t("migration.direct.status.error")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center text-center mb-8">
|
||||
<Paragraph className="max-w-[320px] text-md">
|
||||
{t("migration.direct.loginRequired")}
|
||||
{t("migration.loginRequired")}
|
||||
</Paragraph>
|
||||
<Button
|
||||
theme="purple"
|
||||
|
|
|
|||
144
src/pages/migration/MigrationDownload.tsx
Normal file
144
src/pages/migration/MigrationDownload.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
import { SettingsCard } from "@/components/layout/SettingsCard";
|
||||
import { Stepper } from "@/components/layout/Stepper";
|
||||
import { CenterContainer } from "@/components/layout/ThinContainer";
|
||||
import { Divider } from "@/components/utils/Divider";
|
||||
import { Heading2, Paragraph } from "@/components/utils/Text";
|
||||
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
|
||||
import { PageTitle } from "@/pages/parts/util/PageTitle";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useBookmarkStore } from "@/stores/bookmarks";
|
||||
import { useProgressStore } from "@/stores/progress";
|
||||
|
||||
export function MigrationDownloadPage() {
|
||||
const { t } = useTranslation();
|
||||
const user = useAuthStore();
|
||||
const navigate = useNavigate();
|
||||
const bookmarks = useBookmarkStore((s) => s.bookmarks);
|
||||
const progress = useProgressStore((s) => s.items);
|
||||
const [status, setStatus] = useState<"idle" | "success" | "error">("idle");
|
||||
|
||||
const handleDownload = useCallback(() => {
|
||||
try {
|
||||
// Create a data object containing user's account data, bookmarks, and progress
|
||||
const exportData = {
|
||||
account: {
|
||||
profile: user.account?.profile,
|
||||
deviceName: user.account?.deviceName,
|
||||
},
|
||||
bookmarks,
|
||||
progress,
|
||||
exportDate: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Convert to JSON and create a downloadable link
|
||||
const dataStr = JSON.stringify(exportData, null, 2);
|
||||
const dataUri = `data:application/json;charset=utf-8,${encodeURIComponent(dataStr)}`;
|
||||
|
||||
// Create filename with current date
|
||||
const exportFileDefaultName = `p-stream-data-${new Date().toISOString().split("T")[0]}.json`;
|
||||
|
||||
// Create a temporary link element and click it to trigger download
|
||||
const linkElement = document.createElement("a");
|
||||
linkElement.setAttribute("href", dataUri);
|
||||
linkElement.setAttribute("download", exportFileDefaultName);
|
||||
linkElement.click();
|
||||
|
||||
setStatus("success");
|
||||
} catch (error) {
|
||||
console.error("Error during data download:", error);
|
||||
setStatus("error");
|
||||
}
|
||||
}, [bookmarks, progress, user.account]);
|
||||
|
||||
return (
|
||||
<MinimalPageLayout>
|
||||
<PageTitle subpage k="global.pages.migration" />
|
||||
<CenterContainer>
|
||||
{user.account ? (
|
||||
<div>
|
||||
<Stepper steps={2} current={2} className="mb-12" />
|
||||
<Heading2 className="!text-4xl">
|
||||
{t("migration.download.title")}
|
||||
</Heading2>
|
||||
<div className="space-y-6 max-w-3xl mx-auto">
|
||||
<Paragraph className="text-lg max-w-md">
|
||||
{t("migration.download.description")}
|
||||
</Paragraph>
|
||||
<SettingsCard>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="font-bold text-white">
|
||||
{t("migration.download.title")}
|
||||
</p>
|
||||
</div>
|
||||
<Divider marginClass="my-6 px-8 box-content -mx-8" />
|
||||
<div className="text-white mb-4">
|
||||
<p>{t("migration.download.items.description")}</p>
|
||||
<ul className="list-disc ml-6 mt-2">
|
||||
<li>{t("migration.download.items.profile")}</li>
|
||||
<li>
|
||||
{t("migration.download.items.bookmarks")} (
|
||||
{Object.keys(bookmarks).length} items)
|
||||
</li>
|
||||
<li>
|
||||
{t("migration.download.items.progress")} (
|
||||
{Object.keys(progress).length} items)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className="flex justify-between">
|
||||
<Button theme="secondary" onClick={() => navigate("/migration")}>
|
||||
{t("migration.back")}
|
||||
</Button>
|
||||
{status !== "success" && (
|
||||
<Button theme="purple" onClick={handleDownload}>
|
||||
{t("migration.download.button.download")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{status === "success" && (
|
||||
<div>
|
||||
<Button theme="purple" onClick={() => navigate("/")}>
|
||||
{t("migration.download.button.home")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-center pt-4">
|
||||
{status === "success" && (
|
||||
<p className="text-green-600 mt-4">
|
||||
{t("migration.download.status.success")}
|
||||
</p>
|
||||
)}
|
||||
{status === "error" && (
|
||||
<p className="text-red-600 mt-4">
|
||||
{t("migration.download.status.error")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center text-center mb-8">
|
||||
<Paragraph className="max-w-[320px] text-md">
|
||||
{t("migration.loginRequired")}
|
||||
</Paragraph>
|
||||
<Button
|
||||
theme="purple"
|
||||
className="mt-4"
|
||||
onClick={() => navigate("/")}
|
||||
>
|
||||
{t("migration.download.button.home")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CenterContainer>
|
||||
</MinimalPageLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -32,7 +32,30 @@ export function AccountActionsPart() {
|
|||
<div>
|
||||
<Heading2 border>{t("settings.account.actions.title")}</Heading2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* Account Migration Card */}
|
||||
<SolidSettingsCard
|
||||
paddingClass="px-6 py-8"
|
||||
className="flex flex-col h-full"
|
||||
>
|
||||
<div className="flex-grow">
|
||||
<Heading3>{t("settings.account.actions.migration.title")}</Heading3>
|
||||
<p className="text-type-text mt-3">
|
||||
{t("settings.account.actions.migration.text")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-center">
|
||||
<Button
|
||||
theme="purple"
|
||||
onClick={() => {
|
||||
window.location.href = "/migration";
|
||||
}}
|
||||
>
|
||||
{t("settings.account.actions.migration.button")}
|
||||
</Button>
|
||||
</div>
|
||||
</SolidSettingsCard>
|
||||
|
||||
{/* Logout All Devices Card */}
|
||||
<SolidSettingsCard
|
||||
paddingClass="px-6 py-8"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { JipPage } from "@/pages/Jip";
|
|||
import { LoginPage } from "@/pages/Login";
|
||||
import { MigrationPage } from "@/pages/migration/Migration";
|
||||
import { MigrationDirectPage } from "@/pages/migration/MigrationDirect";
|
||||
import { MigrationDownloadPage } from "@/pages/migration/MigrationDownload";
|
||||
import { OnboardingPage } from "@/pages/onboarding/Onboarding";
|
||||
import { OnboardingExtensionPage } from "@/pages/onboarding/OnboardingExtension";
|
||||
import { OnboardingProxyPage } from "@/pages/onboarding/OnboardingProxy";
|
||||
|
|
@ -151,11 +152,13 @@ function App() {
|
|||
/>
|
||||
<Route path="/onboarding/proxy" element={<OnboardingProxyPage />} />
|
||||
|
||||
<Route path="/migration" element={<MigrationDirectPage />} />
|
||||
{/* Migration pages - awaiting import and export fixes
|
||||
{/* Migration pages - awaiting import and export fixes */}
|
||||
<Route path="/migration" element={<MigrationPage />} />
|
||||
<Route path="/migration/direct" element={<MigrationDirectPage />} />
|
||||
*/}
|
||||
<Route
|
||||
path="/migration/download"
|
||||
element={<MigrationDownloadPage />}
|
||||
/>
|
||||
|
||||
<Route path="/dmca" element={<DmcaPage />} />
|
||||
{/* Support page */}
|
||||
|
|
|
|||
Loading…
Reference in a new issue