diff --git a/src/components/settings-modal/settings-modal.module.css b/src/components/settings-modal/settings-modal.module.css index cc83f218..7dc2db14 100644 --- a/src/components/settings-modal/settings-modal.module.css +++ b/src/components/settings-modal/settings-modal.module.css @@ -28,7 +28,7 @@ border-left: var(--settings-modal-border-left); } -.settingsModal select { +.settingsModal select, .settingsModal button, .settingsModal textarea { filter: var(--filter80); } @@ -122,6 +122,40 @@ margin-left: 5px; } +.accountSettings { + margin-left: 10px; +} + +.accountSettings 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%; +} + +.accountSettings textarea:focus { + border-color: 1px solid #98e; +} + +.accountSettings button { + cursor: pointer; + margin-top: 5px; +} + +.deleteAccount, .deleteAccount button { + color: red; +} + +.accountSettings select { + display: block; + margin-bottom: 10px; +} + @media (max-width: 640px) { .settingsModal { width: 350px !important; diff --git a/src/components/settings-modal/settings-modal.tsx b/src/components/settings-modal/settings-modal.tsx index f41818ff..4011dd1c 100644 --- a/src/components/settings-modal/settings-modal.tsx +++ b/src/components/settings-modal/settings-modal.tsx @@ -1,10 +1,11 @@ -import { useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; 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 styles from './settings-modal.module.css'; import useTheme from '../../hooks/use-theme'; import packageJson from '../../../package.json'; -import { useAccount } from '@plebbit/plebbit-react-hooks'; const commitRef = process.env.REACT_APP_COMMIT_REF; const isElectron = window.isElectron === true; @@ -104,24 +105,180 @@ const InterfaceLanguage = () => { }; const AccountSettings = () => { + const { t } = useTranslation(); const account = useAccount(); + const { accounts } = useAccounts(); + const [text, setText] = useState(''); + const [switchToLastAccount, setSwitchToLastAccount] = useState(false); + + const accountJson = useMemo( + () => stringify({ account: { ...account, plebbit: undefined, karma: undefined, plebbitReactOptions: undefined, unreadNotificationCount: undefined } }), + [account], + ); + + useEffect(() => { + setText(accountJson); + }, [accountJson]); + + useEffect(() => { + if (switchToLastAccount && accounts.length > 0) { + const lastAccount = accounts[accounts.length - 1]; + setActiveAccount(lastAccount.name); + setSwitchToLastAccount(false); + } + }, [accounts, switchToLastAccount]); + + const _createAccount = async () => { + try { + await createAccount(); + } catch (error) { + if (error instanceof Error) { + alert(error.message); + console.log(error); + } else { + console.error('An unknown error occurred:', error); + } + } + setSwitchToLastAccount(true); + }; + + const _deleteAccount = (accountName: string) => { + if (!accountName) { + return; + } else if (window.confirm(t('delete_confirm', { value: accountName, interpolation: { escapeValue: false } }))) { + if (window.confirm(t('double_confirm'))) { + deleteAccount(accountName); + } + } else { + return; + } + }; + + const saveAccount = async () => { + try { + const newAccount = JSON.parse(text).account; + // force keeping the same id, makes it easier to copy paste + await setAccount({ ...newAccount, id: account?.id }); + alert(`Saved ${newAccount.name}`); + } catch (error) { + if (error instanceof Error) { + alert(error.message); + console.log(error); + } else { + console.error('An unknown error occurred:', error); + } + } + }; + + const _exportAccount = async () => { + try { + const accountString = await exportAccount(); + const accountObject = JSON.parse(accountString); + const formattedAccountJson = JSON.stringify(accountObject, null, 2); + + // Create a Blob from the JSON string + const blob = new Blob([formattedAccountJson], { type: 'application/json' }); + + // Create a URL for the Blob + const fileUrl = URL.createObjectURL(blob); + + // Create a temporary download link + const link = document.createElement('a'); + link.href = fileUrl; + link.download = `${account.name}.json`; + + // Append the link, trigger the download, then remove the link + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // Release the Blob URL + URL.revokeObjectURL(fileUrl); + } catch (error) { + if (error instanceof Error) { + alert(error.message); + console.log(error); + } else { + console.error('An unknown error occurred:', error); + } + } + }; + + const _importAccount = async () => { + // Create a file input element + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + + // Handle file selection + fileInput.onchange = async (event) => { + try { + const files = (event.target as HTMLInputElement).files; + if (!files || files.length === 0) { + throw new Error('No file selected.'); + } + const file = files[0]; + + // Read the file content + const reader = new FileReader(); + reader.onload = async (e) => { + const fileContent = e.target!.result; // Non-null assertion + if (typeof fileContent !== 'string') { + throw new Error('File content is not a string.'); + } + const newAccount = JSON.parse(fileContent); + await importAccount(fileContent); + setSwitchToLastAccount(true); + alert(`Imported ${newAccount.account?.name}`); + }; + reader.readAsText(file); + } catch (error) { + if (error instanceof Error) { + alert(error.message); + console.log(error); + } else { + console.error('An unknown error occurred:', error); + } + } + }; + + // Trigger file selection dialog + fileInput.click(); + }; + + const accountsOptions = accounts.map((account) => ( + + )); return ( - <> -
{account ? account.username : 'Not logged in'}test
- +
+ + Account Data Preview: +