mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix: hide generated account copy for imported accounts
This commit is contained in:
@@ -7,6 +7,7 @@ import AccountSettings from '../account-settings';
|
|||||||
|
|
||||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||||
|
const IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY = 'importedAccountAddresses';
|
||||||
|
|
||||||
const hookMocks = vi.hoisted(() => ({
|
const hookMocks = vi.hoisted(() => ({
|
||||||
deleteAccount: vi.fn(),
|
deleteAccount: vi.fn(),
|
||||||
@@ -177,6 +178,34 @@ describe('AccountSettings', () => {
|
|||||||
expect(buttonTexts.some((text) => text.includes('delete_account'))).toBe(true);
|
expect(buttonTexts.some((text) => text.includes('delete_account'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows generated-account copy for app-created accounts', () => {
|
||||||
|
render();
|
||||||
|
|
||||||
|
const infoText = container.querySelector('select')?.parentElement?.querySelector('div')?.textContent ?? '';
|
||||||
|
expect(infoText).toContain('account_auto_generated');
|
||||||
|
expect(infoText).toContain('stored_locally');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows only the stored-locally copy for imported accounts', () => {
|
||||||
|
localStorage.setItem(IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY, JSON.stringify(['0x123']));
|
||||||
|
|
||||||
|
render();
|
||||||
|
|
||||||
|
const infoText = container.querySelector('select')?.parentElement?.querySelector('div')?.textContent ?? '';
|
||||||
|
expect(infoText).toContain('stored_locally');
|
||||||
|
expect(infoText).not.toContain('account_auto_generated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats the legacy imported account storage key as imported state', () => {
|
||||||
|
localStorage.setItem('importedAccountAddress', '0x123');
|
||||||
|
|
||||||
|
render();
|
||||||
|
|
||||||
|
const infoText = container.querySelector('select')?.parentElement?.querySelector('div')?.textContent ?? '';
|
||||||
|
expect(infoText).toContain('stored_locally');
|
||||||
|
expect(infoText).not.toContain('account_auto_generated');
|
||||||
|
});
|
||||||
|
|
||||||
it('deletes the account only after both confirmations succeed', async () => {
|
it('deletes the account only after both confirmations succeed', async () => {
|
||||||
confirmSpy.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
confirmSpy.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
||||||
|
|
||||||
@@ -314,6 +343,7 @@ describe('AccountSettings', () => {
|
|||||||
expect(hookMocks.importAccount).toHaveBeenCalledOnce();
|
expect(hookMocks.importAccount).toHaveBeenCalledOnce();
|
||||||
const importedPayload = JSON.parse(hookMocks.importAccount.mock.calls[0][0]);
|
const importedPayload = JSON.parse(hookMocks.importAccount.mock.calls[0][0]);
|
||||||
expect(importedPayload.account.subscriptions).toEqual(['business.eth', 'music-posting.bso']);
|
expect(importedPayload.account.subscriptions).toEqual(['business.eth', 'music-posting.bso']);
|
||||||
|
expect(localStorage.getItem(IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY)).toBe(JSON.stringify(['0x999']));
|
||||||
expect(localStorage.getItem('importedAccountAddress')).toBe('0x999');
|
expect(localStorage.getItem('importedAccountAddress')).toBe('0x999');
|
||||||
expect(hookMocks.setActiveAccount).toHaveBeenCalledWith('Imported');
|
expect(hookMocks.setActiveAccount).toHaveBeenCalledWith('Imported');
|
||||||
expect(alertSpy).toHaveBeenCalledWith('Imported Imported');
|
expect(alertSpy).toHaveBeenCalledWith('Imported Imported');
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { Capacitor } from '@capacitor/core';
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const isAndroid = Capacitor.getPlatform() === 'android';
|
const isAndroid = Capacitor.getPlatform() === 'android';
|
||||||
|
const IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY = 'importedAccountAddresses';
|
||||||
|
const IMPORTED_ACCOUNT_ADDRESS_LEGACY_STORAGE_KEY = 'importedAccountAddress';
|
||||||
|
|
||||||
const safeParseJSON = <T,>(value: string): T | null => {
|
const safeParseJSON = <T,>(value: string): T | null => {
|
||||||
try {
|
try {
|
||||||
@@ -24,6 +26,32 @@ const withErrorHandling = async <T,>(fn: () => Promise<T>, onError: (e: unknown)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const readImportedAccountAddresses = (): string[] => {
|
||||||
|
try {
|
||||||
|
const storedAddresses = localStorage.getItem(IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY);
|
||||||
|
const parsedAddresses = storedAddresses ? safeParseJSON<unknown>(storedAddresses) : null;
|
||||||
|
const normalizedAddresses = Array.isArray(parsedAddresses)
|
||||||
|
? parsedAddresses.filter((address): address is string => typeof address === 'string' && address.length > 0)
|
||||||
|
: [];
|
||||||
|
const legacyAddress = localStorage.getItem(IMPORTED_ACCOUNT_ADDRESS_LEGACY_STORAGE_KEY);
|
||||||
|
return [...new Set(legacyAddress ? [...normalizedAddresses, legacyAddress] : normalizedAddresses)];
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to read imported account addresses from localStorage:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rememberImportedAccountAddress = (address: string) => {
|
||||||
|
try {
|
||||||
|
const importedAddresses = readImportedAccountAddresses();
|
||||||
|
const nextImportedAddresses = [...new Set([...importedAddresses, address])];
|
||||||
|
localStorage.setItem(IMPORTED_ACCOUNT_ADDRESSES_STORAGE_KEY, JSON.stringify(nextImportedAddresses));
|
||||||
|
localStorage.setItem(IMPORTED_ACCOUNT_ADDRESS_LEGACY_STORAGE_KEY, address);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to save imported account address to localStorage:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Inner component keyed by account id so state resets when user switches account
|
// Inner component keyed by account id so state resets when user switches account
|
||||||
const AccountSettingsEditor = ({
|
const AccountSettingsEditor = ({
|
||||||
account,
|
account,
|
||||||
@@ -125,7 +153,7 @@ const AccountSettingsEditor = ({
|
|||||||
async () => {
|
async () => {
|
||||||
await importAccount(modifiedAccountJson);
|
await importAccount(modifiedAccountJson);
|
||||||
if (accountData.account?.author?.address) {
|
if (accountData.account?.author?.address) {
|
||||||
localStorage.setItem('importedAccountAddress', accountData.account.author.address);
|
rememberImportedAccountAddress(accountData.account.author.address);
|
||||||
}
|
}
|
||||||
if (accountData.account?.name) {
|
if (accountData.account?.name) {
|
||||||
await setActiveAccount(accountData.account.name);
|
await setActiveAccount(accountData.account.name);
|
||||||
@@ -163,6 +191,10 @@ const AccountSettingsEditor = ({
|
|||||||
));
|
));
|
||||||
|
|
||||||
const host = window.electronApi?.isElectron ? 'this desktop app' : isAndroid ? 'this mobile app' : window.location.hostname;
|
const host = window.electronApi?.isElectron ? 'this desktop app' : isAndroid ? 'this mobile app' : window.location.hostname;
|
||||||
|
const isImportedAccount = typeof account?.author?.address === 'string' && readImportedAccountAddresses().includes(account.author.address);
|
||||||
|
const accountStorageInfo = isImportedAccount
|
||||||
|
? t('stored_locally', { location: host, interpolation: { escapeValue: false } })
|
||||||
|
: `${t('account_auto_generated')} ${t('stored_locally', { location: host, interpolation: { escapeValue: false } })}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.setting}>
|
<div className={styles.setting}>
|
||||||
@@ -172,9 +204,7 @@ const AccountSettingsEditor = ({
|
|||||||
</select>{' '}
|
</select>{' '}
|
||||||
<button onClick={() => navigate('/settings/account-data', { state: { returnTo: location.pathname + location.hash } })}>{t('edit')}</button>{' '}
|
<button onClick={() => navigate('/settings/account-data', { state: { returnTo: location.pathname + location.hash } })}>{t('edit')}</button>{' '}
|
||||||
<button onClick={handleExportAccount}>{t('download_backup')}</button>
|
<button onClick={handleExportAccount}>{t('download_backup')}</button>
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>{accountStorageInfo}</div>
|
||||||
{t('account_auto_generated')} {t('stored_locally', { location: host, interpolation: { escapeValue: false } })}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={handleImportAccount}>{t('import_account_backup')}</button>
|
<button onClick={handleImportAccount}>{t('import_account_backup')}</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user