mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(settings): move account json editing to dedicated full editor route
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import * as React from 'react';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import AccountSettings from '../account-settings';
|
||||
|
||||
(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>;
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: { changeLanguage: vi.fn(), language: 'en' },
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@plebbit/plebbit-react-hooks', () => ({
|
||||
useAccount: () => ({ id: 'test-id', name: 'Account 1', author: { address: '0x123', shortAddress: '0x1...3' } }),
|
||||
useAccounts: () => ({ accounts: [{ id: 'test-id', name: 'Account 1', author: { shortAddress: '0x1...3' } }] }),
|
||||
createAccount: vi.fn(),
|
||||
deleteAccount: vi.fn(),
|
||||
exportAccount: vi.fn(),
|
||||
importAccount: vi.fn(),
|
||||
setActiveAccount: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@capacitor/core', () => ({
|
||||
Capacitor: { getPlatform: () => 'web' },
|
||||
}));
|
||||
|
||||
let root: Root;
|
||||
let container: HTMLDivElement;
|
||||
|
||||
const render = (children: React.ReactNode) => {
|
||||
act(() => {
|
||||
root.render(createElement(MemoryRouter, { initialEntries: ['/subs/settings'] }, children));
|
||||
});
|
||||
};
|
||||
|
||||
describe('AccountSettings', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it('does not render an inline textarea', () => {
|
||||
render(createElement(AccountSettings));
|
||||
expect(container.querySelector('textarea')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render save_changes or reset_changes buttons', () => {
|
||||
render(createElement(AccountSettings));
|
||||
const buttons = Array.from(container.querySelectorAll('button'));
|
||||
const buttonTexts = buttons.map((b) => b.textContent ?? '');
|
||||
expect(buttonTexts.some((t) => t.includes('save_changes'))).toBe(false);
|
||||
expect(buttonTexts.some((t) => t.includes('reset_changes'))).toBe(false);
|
||||
});
|
||||
|
||||
it('renders an edit button', () => {
|
||||
render(createElement(AccountSettings));
|
||||
const buttons = Array.from(container.querySelectorAll('button'));
|
||||
expect(buttons.some((b) => (b.textContent ?? '').includes('edit'))).toBe(true);
|
||||
});
|
||||
|
||||
it('renders create, import, export buttons', () => {
|
||||
render(createElement(AccountSettings));
|
||||
const buttons = Array.from(container.querySelectorAll('button'));
|
||||
const texts = buttons.map((b) => b.textContent ?? '');
|
||||
expect(texts.some((t) => t.includes('create'))).toBe(true);
|
||||
expect(texts.some((t) => t.includes('import'))).toBe(true);
|
||||
expect(texts.some((t) => t.includes('export'))).toBe(true);
|
||||
});
|
||||
|
||||
it('renders delete_account button', () => {
|
||||
render(createElement(AccountSettings));
|
||||
const buttons = Array.from(container.querySelectorAll('button'));
|
||||
expect(buttons.some((b) => (b.textContent ?? '').includes('delete_account'))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -3,34 +3,10 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.setting textarea {
|
||||
display: block;
|
||||
font-family: dejavu sans mono, consolas, andale mono, lucida console, monospace;
|
||||
outline: none;
|
||||
border: 1px solid #aaa;
|
||||
font-size: 11px;
|
||||
margin: 0 2px 0 0;
|
||||
padding: 2px 4px 3px;
|
||||
min-height: 150px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.setting textarea:focus {
|
||||
border-color: 1px solid #98e;
|
||||
}
|
||||
|
||||
.setting button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.saveResetAccount {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.setting textarea {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.setting div {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createAccount, deleteAccount, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts } from '@plebbit/plebbit-react-hooks';
|
||||
import stringify from 'json-stringify-pretty-compact';
|
||||
import { createAccount, deleteAccount, exportAccount, importAccount, setActiveAccount, useAccount, useAccounts } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './account-settings.module.css';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
@@ -33,24 +32,6 @@ const AccountSettingsEditor = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
|
||||
const accountJson = useMemo(
|
||||
() =>
|
||||
stringify({
|
||||
account: {
|
||||
...account,
|
||||
author: { ...account?.author, avatar: undefined },
|
||||
plebbit: undefined,
|
||||
karma: undefined,
|
||||
plebbitReactOptions: undefined,
|
||||
unreadNotificationCount: undefined,
|
||||
},
|
||||
}),
|
||||
[account],
|
||||
);
|
||||
|
||||
const [text, setText] = useState(() => accountJson);
|
||||
|
||||
const { accounts } = useAccounts();
|
||||
const switchToNewAccountRef = useRef(false);
|
||||
const navigate = useNavigate();
|
||||
@@ -93,29 +74,6 @@ const AccountSettingsEditor = ({
|
||||
}
|
||||
};
|
||||
|
||||
const saveAccount = async () => {
|
||||
const parsed = safeParseJSON<{ account: Record<string, unknown> }>(text);
|
||||
if (!parsed?.account) {
|
||||
alert('Invalid JSON');
|
||||
return;
|
||||
}
|
||||
const newAccount = parsed.account;
|
||||
const result = await withErrorHandling(
|
||||
() => setAccount({ ...newAccount, id: account?.id }),
|
||||
(error) => {
|
||||
if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
console.log(error);
|
||||
} else {
|
||||
console.error('An unknown error occurred:', error);
|
||||
}
|
||||
},
|
||||
);
|
||||
if (result !== undefined) {
|
||||
alert(`Saved ${newAccount.name}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportAccount = async () => {
|
||||
const accountString = await withErrorHandling(
|
||||
() => exportAccount(),
|
||||
@@ -247,10 +205,8 @@ const AccountSettingsEditor = ({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
<textarea value={text} onChange={(e) => setText(e.target.value)} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||
<div>
|
||||
<button onClick={saveAccount}>{t('save_changes')}</button> <button onClick={() => setText(accountJson)}>{t('reset_changes')}</button>
|
||||
<button onClick={() => navigate('/settings/account-data', { state: { returnTo: location.pathname + location.hash } })}>{t('edit')}</button>
|
||||
<button className={styles.deleteAccount} onClick={() => _deleteAccount(account?.name ?? '')}>
|
||||
{t('delete_account')}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user