Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 34 additions & 36 deletions src/clear/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./styles.css";
import './styles.css';

const baseUri = new URL("./", window.location.href).href;
const baseUri = new URL('./', window.location.href).href;

interface StepElement {
id: string;
Expand All @@ -10,22 +10,22 @@ interface StepElement {

const steps: StepElement[] = [
{
id: "step-local",
label: "本地存储 (localStorage)",
id: 'step-local',
label: '本地存储 (localStorage)', // i18n-ignore: standalone page
action: async () => {
localStorage.clear();
},
},
{
id: "step-session",
label: "会话存储 (sessionStorage)",
id: 'step-session',
label: '会话存储 (sessionStorage)', // i18n-ignore: standalone page
action: async () => {
sessionStorage.clear();
},
},
{
id: "step-indexeddb",
label: "数据库 (IndexedDB)",
id: 'step-indexeddb',
label: '数据库 (IndexedDB)', // i18n-ignore: standalone page
action: async () => {
if (indexedDB.databases) {
const databases = await indexedDB.databases();
Expand All @@ -43,10 +43,10 @@ const steps: StepElement[] = [
},
},
{
id: "step-cache",
label: "缓存 (Cache Storage)",
id: 'step-cache',
label: '缓存 (Cache Storage)', // i18n-ignore: standalone page
action: async () => {
if ("caches" in window) {
if ('caches' in window) {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map((name) => caches.delete(name)));
}
Expand All @@ -55,7 +55,7 @@ const steps: StepElement[] = [
];

function createUI() {
const root = document.getElementById("root")!;
const root = document.getElementById('root')!;
root.innerHTML = `
<div class="container fade-in">
<div class="progress-ring" id="progressRing">
Expand All @@ -70,8 +70,8 @@ function createUI() {
</div>
</div>

<h1 id="title">正在清理数据</h1>
<p class="status" id="status">请稍候...</p>
<h1 id="title">正在清理数据</h1><!-- i18n-ignore: standalone page -->
<p class="status" id="status">请稍候...</p><!-- i18n-ignore: standalone page -->

<div class="steps" id="steps">
${steps
Expand All @@ -85,9 +85,9 @@ function createUI() {
</span>
<span>${step.label}</span>
</div>
`
`,
)
.join("")}
.join('')}
</div>

<p class="error-message" id="error"></p>
Expand All @@ -100,7 +100,7 @@ function delay(ms: number) {
}

function updateProgress(completed: number, total: number) {
const progressCircle = document.getElementById("progressCircle");
const progressCircle = document.getElementById('progressCircle');
if (progressCircle) {
const percent = (completed / total) * 100;
const offset = 226 - (226 * percent) / 100;
Expand All @@ -109,13 +109,13 @@ function updateProgress(completed: number, total: number) {
}

function setStepActive(stepId: string) {
document.getElementById(stepId)?.classList.add("active");
document.getElementById(stepId)?.classList.add('active');
}

function setStepDone(stepId: string) {
const step = document.getElementById(stepId);
step?.classList.remove("active");
step?.classList.add("done");
step?.classList.remove('active');
step?.classList.add('done');
}

async function clearAllData() {
Expand All @@ -127,9 +127,7 @@ async function clearAllData() {

try {
await step.action();
} catch (e) {

}
} catch (e) {}

setStepDone(step.id);
completed++;
Expand All @@ -140,28 +138,28 @@ async function clearAllData() {
async function main() {
createUI();

const title = document.getElementById("title")!;
const status = document.getElementById("status")!;
const error = document.getElementById("error")!;
const checkIcon = document.getElementById("checkIcon")!;
const container = document.querySelector(".container")!;
const title = document.getElementById('title')!;
const status = document.getElementById('status')!;
const error = document.getElementById('error')!;
const checkIcon = document.getElementById('checkIcon')!;
const container = document.querySelector('.container')!;

try {
await clearAllData();

await delay(300);
checkIcon.classList.add("visible");
container.classList.add("success-state");
title.textContent = "清理完成";
status.textContent = "正在返回应用...";
checkIcon.classList.add('visible');
container.classList.add('success-state');
title.textContent = '清理完成'; // i18n-ignore: standalone page
status.textContent = '正在返回应用...'; // i18n-ignore: standalone page

await delay(1200);
window.location.href = baseUri;
} catch (e) {
title.textContent = "清理失败";
status.textContent = "";
error.style.display = "block";
error.textContent = e instanceof Error ? e.message : "发生未知错误";
title.textContent = '清理失败'; // i18n-ignore: standalone page
status.textContent = '';
error.style.display = 'block';
error.textContent = e instanceof Error ? e.message : '发生未知错误'; // i18n-ignore: standalone page

await delay(3000);
window.location.href = baseUri;
Expand Down
6 changes: 3 additions & 3 deletions src/components/asset/asset-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function AssetSelector({
}
};

const displayPlaceholder = placeholder ?? t('assetSelector.selectAsset', '选择资产');
const displayPlaceholder = placeholder ?? t('assetSelector.selectAsset');
// 使用空字符串代替 undefined,确保 Select 始终是受控的
const selectedValue = selectedAsset ? getAssetKey(selectedAsset) : '';

Expand All @@ -91,7 +91,7 @@ export function AssetSelector({
<span className="text-sm font-medium">{selectedAsset.symbol}</span>
{showBalance && (
<span className="text-muted-foreground text-xs">
{t('assetSelector.balance', '余额')}:{' '}
{t('assetSelector.balance')}:{' '}
<AmountDisplay
value={selectedAsset.balance}
size="xs"
Expand All @@ -115,7 +115,7 @@ export function AssetSelector({
<SelectContent>
{availableAssets.length === 0 ? (
<div className="text-muted-foreground flex flex-col items-center py-4 text-sm">
<p>{t('assetSelector.noAssets', '暂无可选资产')}</p>
<p>{t('assetSelector.noAssets')}</p>
</div>
) : (
availableAssets.map((asset) => (
Expand Down
58 changes: 23 additions & 35 deletions src/components/common/migration-required-view.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,51 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { IconAlertTriangle, IconTrash } from '@tabler/icons-react'
import { Button } from '@/components/ui/button'
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IconAlertTriangle, IconTrash } from '@tabler/icons-react';
import { Button } from '@/components/ui/button';

/**
* 数据库迁移引导组件
* 当检测到旧版数据需要迁移时显示
*
*
* 注意:此组件在 Stackflow context 外部渲染,不能使用 useFlow()
*/
export function MigrationRequiredView() {
const { t } = useTranslation(['settings', 'common'])
const [isClearing, setIsClearing] = useState(false)
const { t } = useTranslation(['settings', 'common']);
const [isClearing, setIsClearing] = useState(false);

const handleClearData = () => {
setIsClearing(true)
setIsClearing(true);
// 跳转到 clear.html 进行清理
const baseUri = import.meta.env.BASE_URL || '/'
window.location.href = `${baseUri}clear.html`
}
const baseUri = import.meta.env.BASE_URL || '/';
window.location.href = `${baseUri}clear.html`;
};

return (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-6">
<div className="flex flex-col items-center gap-6 text-center max-w-sm">
<div className="bg-background flex min-h-screen flex-col items-center justify-center p-6">
<div className="flex max-w-sm flex-col items-center gap-6 text-center">
<div className="bg-destructive/10 flex size-20 items-center justify-center rounded-full">
<IconAlertTriangle className="text-destructive size-10" />
</div>

<div className="space-y-2">
<h1 className="text-xl font-bold">
{t('settings:storage.migrationRequired', '数据需要迁移')}
</h1>
<p className="text-muted-foreground text-sm">
{t(
'settings:storage.migrationDesc',
'检测到旧版本数据格式,需要清空本地数据库后才能继续使用。您的助记词和私钥不会受到影响,但需要重新导入钱包。'
)}
</p>
<h1 className="text-xl font-bold">{t('settings:storage.migrationRequired')}</h1>
<p className="text-muted-foreground text-sm">{t('settings:storage.migrationDesc')}</p>
</div>

{/* Warning List */}
<div className="bg-destructive/5 rounded-lg p-4 w-full">
<ul className="text-destructive space-y-2 text-sm text-left">
<li>• {t('settings:clearData.item1', '所有钱包数据将被删除')}</li>
<li>• {t('settings:clearData.item2', '所有设置将恢复默认')}</li>
<li>• {t('settings:clearData.item3', '应用将重新启动')}</li>
<div className="bg-destructive/5 w-full rounded-lg p-4">
<ul className="text-destructive space-y-2 text-left text-sm">
<li>• {t('settings:clearData.item1')}</li>
<li>• {t('settings:clearData.item2')}</li>
<li>• {t('settings:clearData.item3')}</li>
</ul>
</div>

<Button
variant="destructive"
onClick={handleClearData}
className="w-full gap-2"
disabled={isClearing}
>
<Button variant="destructive" onClick={handleClearData} className="w-full gap-2" disabled={isClearing}>
<IconTrash className="size-4" />
{t('settings:clearData.confirm', '确认清空')}
{t('settings:clearData.confirm')}
</Button>
</div>
</div>
)
);
}
Loading