Update migration

todo finish upload feat
This commit is contained in:
Pas 2025-03-23 12:38:08 -06:00
parent 3bbfbc0e69
commit 4039ae8f68
6 changed files with 923 additions and 717 deletions

File diff suppressed because it is too large Load diff

View file

@ -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>
);
}

View file

@ -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"

View 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>
);
}

View file

@ -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"

View file

@ -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 */}