mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat: add TOTP secret input actions and enhance dark mode styles
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from 'preact/hooks';
|
||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
||||
import { CheckCheck, ChevronLeft, Copy, Eye, EyeOff, File, FileText, LayoutGrid, Pencil, Plus, RefreshCw, Save, Send as SendIcon, Trash2, X } from 'lucide-preact';
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
import type { Send, SendDraft } from '@/lib/types';
|
||||
@@ -79,6 +79,7 @@ export default function SendsPage(props: SendsPageProps) {
|
||||
const [isMobileLayout, setIsMobileLayout] = useState(getInitialIsMobileLayout);
|
||||
const [mobilePanel, setMobilePanel] = useState<'list' | 'detail' | 'edit'>('list');
|
||||
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
||||
const mobileSidebarToggleKeyRef = useRef(props.mobileSidebarToggleKey);
|
||||
const [autoCopyLink, setAutoCopyLink] = useState<boolean>(() => {
|
||||
try {
|
||||
return localStorage.getItem(AUTO_COPY_KEY) === '1';
|
||||
@@ -108,7 +109,8 @@ export default function SendsPage(props: SendsPageProps) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.mobileSidebarToggleKey) return;
|
||||
if (props.mobileSidebarToggleKey === mobileSidebarToggleKeyRef.current) return;
|
||||
mobileSidebarToggleKeyRef.current = props.mobileSidebarToggleKey;
|
||||
setMobileSidebarOpen((open) => !open);
|
||||
}, [props.mobileSidebarToggleKey]);
|
||||
|
||||
|
||||
@@ -269,7 +269,33 @@ export default function SettingsPage(props: SettingsPageProps) {
|
||||
<div>
|
||||
<label className="field">
|
||||
<span>{t('txt_authenticator_key')}</span>
|
||||
<input className="input" value={secret} disabled={totpLocked} onInput={(e) => setSecret((e.currentTarget as HTMLInputElement).value.toUpperCase())} />
|
||||
<div className="totp-secret-input-wrap">
|
||||
<input className="input totp-secret-input" value={secret} disabled={totpLocked} onInput={(e) => setSecret((e.currentTarget as HTMLInputElement).value.toUpperCase())} />
|
||||
<div className="totp-secret-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary small totp-secret-icon-btn"
|
||||
disabled={totpLocked}
|
||||
title={t('txt_regenerate')}
|
||||
aria-label={t('txt_regenerate')}
|
||||
onClick={() => setSecret(randomBase32Secret(32))}
|
||||
>
|
||||
<RefreshCw size={14} className="btn-icon" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary small totp-secret-icon-btn"
|
||||
disabled={totpLocked}
|
||||
title={t('txt_copy_secret')}
|
||||
aria-label={t('txt_copy_secret')}
|
||||
onClick={() => {
|
||||
void copyTextToClipboard(secret, { successMessage: t('txt_secret_copied') });
|
||||
}}
|
||||
>
|
||||
<Clipboard size={14} className="btn-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span>{t('txt_verification_code')}</span>
|
||||
@@ -280,29 +306,14 @@ export default function SettingsPage(props: SettingsPageProps) {
|
||||
<ShieldCheck size={14} className="btn-icon" />
|
||||
{totpLocked ? t('txt_enabled') : t('txt_enable_totp')}
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary" disabled={totpLocked} onClick={() => setSecret(randomBase32Secret(32))}>
|
||||
<RefreshCw size={14} className="btn-icon" />
|
||||
{t('txt_regenerate')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
disabled={totpLocked}
|
||||
onClick={() => {
|
||||
void copyTextToClipboard(secret, { successMessage: t('txt_secret_copied') });
|
||||
}}
|
||||
>
|
||||
<Clipboard size={14} className="btn-icon" />
|
||||
{t('txt_copy_secret')}
|
||||
<button type="button" className="btn btn-danger" disabled={!totpLocked} onClick={props.onOpenDisableTotp}>
|
||||
<ShieldOff size={14} className="btn-icon" />
|
||||
{t('txt_disable_totp')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" className="btn btn-danger" disabled={!totpLocked} onClick={props.onOpenDisableTotp}>
|
||||
<ShieldOff size={14} className="btn-icon" />
|
||||
{t('txt_disable_totp')}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<section className="card settings-module">
|
||||
|
||||
@@ -127,6 +127,7 @@ export default function VaultPage(props: VaultPageProps) {
|
||||
const folderSortMenuRef = useRef<HTMLDivElement | null>(null);
|
||||
const attachmentInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const listPanelRef = useRef<HTMLDivElement | null>(null);
|
||||
const mobileSidebarToggleKeyRef = useRef(props.mobileSidebarToggleKey);
|
||||
const suppressNextSortScrollRef = useRef(false);
|
||||
const sshSeedTicketRef = useRef(0);
|
||||
const sshFingerprintTicketRef = useRef(0);
|
||||
@@ -147,7 +148,8 @@ export default function VaultPage(props: VaultPageProps) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.mobileSidebarToggleKey) return;
|
||||
if (props.mobileSidebarToggleKey === mobileSidebarToggleKeyRef.current) return;
|
||||
mobileSidebarToggleKeyRef.current = props.mobileSidebarToggleKey;
|
||||
setMobileSidebarOpen((open) => !open);
|
||||
}, [props.mobileSidebarToggleKey]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user